iduedu 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iduedu-0.1.0/PKG-INFO +59 -0
- iduedu-0.1.0/README.md +32 -0
- iduedu-0.1.0/pyproject.toml +62 -0
- iduedu-0.1.0/src/iduedu/__init__.py +11 -0
- iduedu-0.1.0/src/iduedu/_api.py +7 -0
- iduedu-0.1.0/src/iduedu/_config.py +58 -0
- iduedu-0.1.0/src/iduedu/_version.py +1 -0
- iduedu-0.1.0/src/iduedu/enums/__init__.py +0 -0
- iduedu-0.1.0/src/iduedu/enums/drive_enums.py +73 -0
- iduedu-0.1.0/src/iduedu/enums/pt_enums.py +25 -0
- iduedu-0.1.0/src/iduedu/modules/__init__.py +0 -0
- iduedu-0.1.0/src/iduedu/modules/downloaders.py +126 -0
- iduedu-0.1.0/src/iduedu/modules/drive_walk_builder.py +247 -0
- iduedu-0.1.0/src/iduedu/modules/intermodal_builder.py +86 -0
- iduedu-0.1.0/src/iduedu/modules/matrix_builder.py +104 -0
- iduedu-0.1.0/src/iduedu/modules/pt_walk_joiner.py +179 -0
- iduedu-0.1.0/src/iduedu/modules/public_transport_builder.py +266 -0
- iduedu-0.1.0/src/iduedu/modules/routes_parser.py +324 -0
- iduedu-0.1.0/src/iduedu/utils/__init__.py +0 -0
- iduedu-0.1.0/src/iduedu/utils/utils.py +36 -0
iduedu-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: iduedu
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: IduEdu is a Python package for the creation and manipulation of complex city networks from OpenStreetMap.
|
|
5
|
+
License: BSD-3-Clause
|
|
6
|
+
Author: Danila
|
|
7
|
+
Author-email: 63115678+DDonnyy@users.noreply.github.com
|
|
8
|
+
Requires-Python: >=3.10,<3.13
|
|
9
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Dist: geopandas (>=0.14.4,<0.15.0)
|
|
15
|
+
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
|
16
|
+
Requires-Dist: networkit (>=11.0,<12.0)
|
|
17
|
+
Requires-Dist: networkx (>=3.3,<4.0)
|
|
18
|
+
Requires-Dist: numpy (>=1.23.5,<2.0.0)
|
|
19
|
+
Requires-Dist: osm2geojson (>=0.2.4,<0.3.0)
|
|
20
|
+
Requires-Dist: osmnx (>=1.9.4,<2.0.0)
|
|
21
|
+
Requires-Dist: pandas (>=2.2.0,<3.0.0)
|
|
22
|
+
Requires-Dist: pydantic (>=2.6.1,<3.0.0)
|
|
23
|
+
Requires-Dist: scipy (>=1.13.1,<2.0.0)
|
|
24
|
+
Requires-Dist: tqdm (>=4.66.2,<5.0.0)
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# IduEdu
|
|
28
|
+
|
|
29
|
+
[](https://github.com/psf/black)
|
|
30
|
+
|
|
31
|
+
### IduEdu is an open-source Python library for the creation and manipulation of complex city networks from OpenStreetMap.
|
|
32
|
+
|
|
33
|
+
## Features and how to use
|
|
34
|
+
|
|
35
|
+
1. **[Graphs from OSM/Polygon/Name](./examples/get_any_graph.ipynb)** - Functions for building a graph of roads,
|
|
36
|
+
pedestrians and public transport based on OpenStreetMap (OSM), as well as creating an intermodal (public transport +
|
|
37
|
+
pedestrians) graph.
|
|
38
|
+
2. **[Adjacency matrix](./examples/calc_adj_matrix.ipynb)** - Calculate adjacency matrix based on the provided graph and
|
|
39
|
+
edge weight type (_time_min_ or _length_meter_).
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
**IduEdu** can be installed with ``pip``:
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
pip install IduEdu
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Configuration changes
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from iduedu import config
|
|
53
|
+
|
|
54
|
+
config.set_timeout(10) # Timeout for overpass queries
|
|
55
|
+
config.change_logger_lvl('INFO') # To mute all debug msgs
|
|
56
|
+
config.set_enable_tqdm(False) # To mute all tqdm's progress bars
|
|
57
|
+
config.set_overpass_url('http://your.overpass-api.de/interpreter/URL')
|
|
58
|
+
```
|
|
59
|
+
|
iduedu-0.1.0/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# IduEdu
|
|
2
|
+
|
|
3
|
+
[](https://github.com/psf/black)
|
|
4
|
+
|
|
5
|
+
### IduEdu is an open-source Python library for the creation and manipulation of complex city networks from OpenStreetMap.
|
|
6
|
+
|
|
7
|
+
## Features and how to use
|
|
8
|
+
|
|
9
|
+
1. **[Graphs from OSM/Polygon/Name](./examples/get_any_graph.ipynb)** - Functions for building a graph of roads,
|
|
10
|
+
pedestrians and public transport based on OpenStreetMap (OSM), as well as creating an intermodal (public transport +
|
|
11
|
+
pedestrians) graph.
|
|
12
|
+
2. **[Adjacency matrix](./examples/calc_adj_matrix.ipynb)** - Calculate adjacency matrix based on the provided graph and
|
|
13
|
+
edge weight type (_time_min_ or _length_meter_).
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
**IduEdu** can be installed with ``pip``:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
pip install IduEdu
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Configuration changes
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
from iduedu import config
|
|
27
|
+
|
|
28
|
+
config.set_timeout(10) # Timeout for overpass queries
|
|
29
|
+
config.change_logger_lvl('INFO') # To mute all debug msgs
|
|
30
|
+
config.set_enable_tqdm(False) # To mute all tqdm's progress bars
|
|
31
|
+
config.set_overpass_url('http://your.overpass-api.de/interpreter/URL')
|
|
32
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "iduedu"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
license = "BSD-3-Clause"
|
|
5
|
+
description = "IduEdu is a Python package for the creation and manipulation of complex city networks from OpenStreetMap."
|
|
6
|
+
authors = ["Danila <63115678+DDonnyy@users.noreply.github.com>"]
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
packages = [{ include = "iduedu", from = "src" }]
|
|
10
|
+
|
|
11
|
+
[tool.poetry.dependencies]
|
|
12
|
+
python = ">=3.10,<3.13"
|
|
13
|
+
osmnx = "^1.9.4"
|
|
14
|
+
tqdm = "^4.66.2"
|
|
15
|
+
osm2geojson = "^0.2.4"
|
|
16
|
+
pydantic = "^2.6.1"
|
|
17
|
+
networkit = "^11.0"
|
|
18
|
+
numpy = "^1.23.5"
|
|
19
|
+
pandas = "^2.2.0"
|
|
20
|
+
geopandas = "^0.14.4"
|
|
21
|
+
networkx = "^3.3"
|
|
22
|
+
loguru = "^0.7.2"
|
|
23
|
+
scipy = "^1.13.1"
|
|
24
|
+
|
|
25
|
+
[tool.poetry.group.dev.dependencies]
|
|
26
|
+
black = "^24.2.0"
|
|
27
|
+
pylint = "^3.0.3"
|
|
28
|
+
isort = "^5.13.2"
|
|
29
|
+
jupyter = "^1.0.0"
|
|
30
|
+
ortools = "^9.9.3963"
|
|
31
|
+
|
|
32
|
+
[build-system]
|
|
33
|
+
requires = ["poetry-core"]
|
|
34
|
+
build-backend = "poetry.core.masonry.api"
|
|
35
|
+
|
|
36
|
+
[tool.black]
|
|
37
|
+
line-length = 120
|
|
38
|
+
target-version = ['py310']
|
|
39
|
+
|
|
40
|
+
[tool.pylint.format]
|
|
41
|
+
max-line-length = 120
|
|
42
|
+
expected-line-ending-format = "LF"
|
|
43
|
+
max-locals = 20
|
|
44
|
+
extension-pkg-allow-list = ["networkit"]
|
|
45
|
+
disable = [
|
|
46
|
+
"duplicate-code",
|
|
47
|
+
"missing-module-docstring",
|
|
48
|
+
"missing-function-docstring",
|
|
49
|
+
"too-many-locals",
|
|
50
|
+
"too-many-branches",
|
|
51
|
+
"too-many-statements",
|
|
52
|
+
"too-many-arguments"
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.isort]
|
|
56
|
+
multi_line_output = 3
|
|
57
|
+
include_trailing_comma = true
|
|
58
|
+
force_grid_wrap = 0
|
|
59
|
+
use_parentheses = true
|
|
60
|
+
ensure_newline_before_comments = true
|
|
61
|
+
line_length = 120
|
|
62
|
+
split_on_trailing_comma = true
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""
|
|
2
|
+
IduEdu
|
|
3
|
+
========
|
|
4
|
+
|
|
5
|
+
IduEdu is a Python package for the creation and manipulation of complex city networks from OpenStreetMap.
|
|
6
|
+
|
|
7
|
+
Homepage https://github.com/DDonnyy/IduEdu.
|
|
8
|
+
"""
|
|
9
|
+
from ._config import config
|
|
10
|
+
from ._api import *
|
|
11
|
+
from ._version import VERSION as __version__
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# pylint: disable=unused-import
|
|
2
|
+
from .modules.downloaders import get_boundary
|
|
3
|
+
from .modules.drive_walk_builder import get_drive_graph, get_walk_graph
|
|
4
|
+
from .modules.intermodal_builder import get_intermodal_graph
|
|
5
|
+
from .modules.matrix_builder import get_adj_matrix_gdf_to_gdf
|
|
6
|
+
from .modules.pt_walk_joiner import join_pt_walk_graph
|
|
7
|
+
from .modules.public_transport_builder import get_all_public_transport_graph, get_single_public_transport_graph
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Config:
|
|
8
|
+
"""
|
|
9
|
+
A configuration class to manage global settings for the application, such as Overpass API URL, timeouts, and logging options.
|
|
10
|
+
|
|
11
|
+
Attributes
|
|
12
|
+
----------
|
|
13
|
+
overpass_url : str
|
|
14
|
+
URL for accessing the Overpass API. Defaults to "http://lz4.overpass-api.de/api/interpreter".
|
|
15
|
+
timeout : int or None
|
|
16
|
+
Timeout in seconds for API requests. If None, no timeout is applied.
|
|
17
|
+
enable_tqdm_bar : bool
|
|
18
|
+
Enables or disables progress bars (via tqdm). Defaults to True.
|
|
19
|
+
logger : Logger
|
|
20
|
+
Logging instance to handle application logging.
|
|
21
|
+
|
|
22
|
+
Methods
|
|
23
|
+
-------
|
|
24
|
+
change_logger_lvl(lvl: Literal["TRACE", "DEBUG", "INFO", "WARN", "ERROR"])
|
|
25
|
+
Changes the logging level to the specified value.
|
|
26
|
+
set_overpass_url(url: str)
|
|
27
|
+
Sets a new Overpass API URL.
|
|
28
|
+
set_timeout(timeout: int)
|
|
29
|
+
Sets the timeout for API requests.
|
|
30
|
+
set_enable_tqdm(enable: bool)
|
|
31
|
+
Enables or disables progress bars in the application.
|
|
32
|
+
"""
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
overpass_url="http://lz4.overpass-api.de/api/interpreter",
|
|
36
|
+
timeout=None,
|
|
37
|
+
enable_tqdm_bar=True,
|
|
38
|
+
):
|
|
39
|
+
self.overpass_url = overpass_url
|
|
40
|
+
self.timeout = timeout
|
|
41
|
+
self.enable_tqdm_bar = enable_tqdm_bar
|
|
42
|
+
self.logger = logger
|
|
43
|
+
|
|
44
|
+
def change_logger_lvl(self, lvl: Literal["TRACE", "DEBUG", "INFO", "WARN", "ERROR"]):
|
|
45
|
+
self.logger.remove()
|
|
46
|
+
self.logger.add(sys.stderr, level=lvl)
|
|
47
|
+
|
|
48
|
+
def set_overpass_url(self, url: str):
|
|
49
|
+
self.overpass_url = url
|
|
50
|
+
|
|
51
|
+
def set_timeout(self, timeout: int):
|
|
52
|
+
self.timeout = timeout
|
|
53
|
+
|
|
54
|
+
def set_enable_tqdm(self, enable: bool):
|
|
55
|
+
self.enable_tqdm_bar = enable
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
config = Config()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VERSION = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RegistrationStatus(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Enum for registration status of the road.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
FEDERAL = 1
|
|
11
|
+
REGIONAL = 2
|
|
12
|
+
LOCAL = 3
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class HighwayType(Enum):
|
|
16
|
+
"""
|
|
17
|
+
Enum of highway types. Properties contain registration status & max speeds
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
MOTORWAY = "motorway"
|
|
21
|
+
TRUNK = "trunk"
|
|
22
|
+
PRIMARY = "primary"
|
|
23
|
+
SECONDARY = "secondary"
|
|
24
|
+
TERTIARY = "tertiary"
|
|
25
|
+
UNCLASSIFIED = "unclassified"
|
|
26
|
+
RESIDENTIAL = "residential"
|
|
27
|
+
MOTORWAY_LINK = "motorway_link"
|
|
28
|
+
TRUNK_LINK = "trunk_link"
|
|
29
|
+
PRIMARY_LINK = "primary_link"
|
|
30
|
+
SECONDARY_LINK = "secondary_link"
|
|
31
|
+
TERTIARY_LINK = "tertiary_link"
|
|
32
|
+
LIVING_STREET = "living_street"
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def reg_status(self) -> Literal[1, 2, 3]:
|
|
36
|
+
reg_status = {
|
|
37
|
+
HighwayType.MOTORWAY: RegistrationStatus.FEDERAL,
|
|
38
|
+
HighwayType.TRUNK: RegistrationStatus.FEDERAL,
|
|
39
|
+
HighwayType.PRIMARY: RegistrationStatus.REGIONAL,
|
|
40
|
+
HighwayType.SECONDARY: RegistrationStatus.REGIONAL,
|
|
41
|
+
HighwayType.TERTIARY: RegistrationStatus.LOCAL,
|
|
42
|
+
HighwayType.UNCLASSIFIED: RegistrationStatus.LOCAL,
|
|
43
|
+
HighwayType.RESIDENTIAL: RegistrationStatus.LOCAL,
|
|
44
|
+
HighwayType.MOTORWAY_LINK: RegistrationStatus.FEDERAL,
|
|
45
|
+
HighwayType.TRUNK_LINK: RegistrationStatus.FEDERAL,
|
|
46
|
+
HighwayType.PRIMARY_LINK: RegistrationStatus.REGIONAL,
|
|
47
|
+
HighwayType.SECONDARY_LINK: RegistrationStatus.REGIONAL,
|
|
48
|
+
HighwayType.TERTIARY_LINK: RegistrationStatus.LOCAL,
|
|
49
|
+
HighwayType.LIVING_STREET: RegistrationStatus.LOCAL,
|
|
50
|
+
}
|
|
51
|
+
return reg_status[self].value
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def max_speed(self) -> float:
|
|
55
|
+
"""
|
|
56
|
+
Average speed in m/min.
|
|
57
|
+
"""
|
|
58
|
+
speeds = { # km/h
|
|
59
|
+
HighwayType.MOTORWAY: 110,
|
|
60
|
+
HighwayType.TRUNK: 90,
|
|
61
|
+
HighwayType.PRIMARY: 60,
|
|
62
|
+
HighwayType.SECONDARY: 60,
|
|
63
|
+
HighwayType.TERTIARY: 60,
|
|
64
|
+
HighwayType.UNCLASSIFIED: 40,
|
|
65
|
+
HighwayType.RESIDENTIAL: 40,
|
|
66
|
+
HighwayType.MOTORWAY_LINK: 90,
|
|
67
|
+
HighwayType.TRUNK_LINK: 90,
|
|
68
|
+
HighwayType.PRIMARY_LINK: 60,
|
|
69
|
+
HighwayType.SECONDARY_LINK: 60,
|
|
70
|
+
HighwayType.TERTIARY_LINK: 60,
|
|
71
|
+
HighwayType.LIVING_STREET: 20,
|
|
72
|
+
}
|
|
73
|
+
return speeds[self] * 1000 / 60 # metres per minute
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PublicTrasport(Enum):
|
|
5
|
+
"""
|
|
6
|
+
Enumeration class for edge types in graphs.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
SUBWAY = "subway"
|
|
10
|
+
BUS = "bus"
|
|
11
|
+
TRAM = "tram"
|
|
12
|
+
TROLLEYBUS = "trolleybus"
|
|
13
|
+
|
|
14
|
+
@property
|
|
15
|
+
def avg_speed(self) -> float:
|
|
16
|
+
"""
|
|
17
|
+
Average speed in m/min.
|
|
18
|
+
"""
|
|
19
|
+
speeds = { # km/h
|
|
20
|
+
PublicTrasport.SUBWAY: 40,
|
|
21
|
+
PublicTrasport.BUS: 20,
|
|
22
|
+
PublicTrasport.TRAM: 20,
|
|
23
|
+
PublicTrasport.TROLLEYBUS: 18,
|
|
24
|
+
}
|
|
25
|
+
return speeds[self] * 1000 / 60
|
|
File without changes
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import geopandas as gpd
|
|
2
|
+
import osm2geojson
|
|
3
|
+
import osmnx as ox
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import requests
|
|
6
|
+
from shapely import MultiPolygon, Polygon, unary_union
|
|
7
|
+
|
|
8
|
+
from iduedu._config import config
|
|
9
|
+
|
|
10
|
+
logger = config.logger
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RequestError(RuntimeError):
|
|
14
|
+
def __init__(self, message, status_code=None, reason=None, response_text=None, response_content=None):
|
|
15
|
+
super().__init__(message)
|
|
16
|
+
self.status_code = status_code
|
|
17
|
+
self.reason = reason
|
|
18
|
+
self.response_text = response_text
|
|
19
|
+
self.response_content = response_content
|
|
20
|
+
|
|
21
|
+
def __str__(self):
|
|
22
|
+
return f"{super().__str__()} (status: {self.status_code}, reason: {self.reason})"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_boundary_by_osm_id(osm_id) -> MultiPolygon | Polygon:
|
|
26
|
+
overpass_query = f"""
|
|
27
|
+
[out:json];
|
|
28
|
+
(
|
|
29
|
+
relation({osm_id});
|
|
30
|
+
);
|
|
31
|
+
out geom;
|
|
32
|
+
"""
|
|
33
|
+
logger.debug(f"Downloading territory bounds with osm_id <{osm_id}> ...")
|
|
34
|
+
result = requests.get(config.overpass_url, params={"data": overpass_query}, timeout=config.timeout)
|
|
35
|
+
if result.status_code == 200:
|
|
36
|
+
json_result = result.json()
|
|
37
|
+
boundary = osm2geojson.json2geojson(json_result)
|
|
38
|
+
boundary = gpd.GeoDataFrame.from_features(boundary["features"]).set_crs(4326)
|
|
39
|
+
poly = unary_union(boundary.geometry)
|
|
40
|
+
return poly
|
|
41
|
+
raise RequestError(
|
|
42
|
+
message=f"Request failed with status code {result.status_code}, reason: {result.reason}",
|
|
43
|
+
status_code=result.status_code,
|
|
44
|
+
reason=result.reason,
|
|
45
|
+
response_text=result.text,
|
|
46
|
+
response_content=result.content,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_boundary_by_name(territory_name: str) -> Polygon | MultiPolygon:
|
|
51
|
+
logger.debug(f"Downloading territory bounds with name <{territory_name}> ...")
|
|
52
|
+
place = ox.geocode_to_gdf(territory_name)
|
|
53
|
+
return unary_union(place.geometry)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def get_boundary(
|
|
57
|
+
osm_id: int | None = None, territory_name: str | None = None, polygon: Polygon | MultiPolygon | None = None
|
|
58
|
+
) -> Polygon:
|
|
59
|
+
"""
|
|
60
|
+
Retrieve the boundary polygon for a given territory, either by OSM ID, territory name, or an existing polygon.
|
|
61
|
+
If a MultiPolygon is provided, it will be converted to a convex hull.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
osm_id : int, optional
|
|
66
|
+
OpenStreetMap ID of the territory to retrieve the boundary for.
|
|
67
|
+
Either this or `territory_name` must be provided.
|
|
68
|
+
territory_name : str, optional
|
|
69
|
+
Name of the territory to retrieve the boundary for. Either this or `osm_id` must be provided.
|
|
70
|
+
polygon : Polygon | MultiPolygon, optional
|
|
71
|
+
A custom polygon or MultiPolygon to use instead of querying by `osm_id` or `territory_name`.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
Polygon
|
|
76
|
+
The boundary polygon for the specified territory.
|
|
77
|
+
If a MultiPolygon was provided, it will return the convex hull.
|
|
78
|
+
|
|
79
|
+
Raises
|
|
80
|
+
------
|
|
81
|
+
ValueError
|
|
82
|
+
If neither `osm_id`, `territory_name`, nor `polygon` are provided.
|
|
83
|
+
|
|
84
|
+
Examples
|
|
85
|
+
--------
|
|
86
|
+
>>> boundary = get_boundary(osm_id=123456)
|
|
87
|
+
>>> boundary = get_boundary(territory_name="New York")
|
|
88
|
+
>>> boundary = get_boundary(polygon=some_polygon)
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
if osm_id is None and territory_name is None and polygon is None:
|
|
92
|
+
raise ValueError("Either osm_id or name or polygon must be specified")
|
|
93
|
+
if osm_id:
|
|
94
|
+
polygon: Polygon = get_boundary_by_osm_id(osm_id)
|
|
95
|
+
elif territory_name:
|
|
96
|
+
polygon: Polygon = get_boundary_by_name(territory_name)
|
|
97
|
+
|
|
98
|
+
if isinstance(polygon, MultiPolygon):
|
|
99
|
+
polygon: Polygon = polygon.convex_hull
|
|
100
|
+
|
|
101
|
+
return polygon
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_routes_by_poly(polygon: Polygon, public_transport_type: str) -> pd.DataFrame:
|
|
105
|
+
polygon_coords = " ".join(f"{y} {x}" for x, y in polygon.exterior.coords[:-1])
|
|
106
|
+
overpass_query = f"""
|
|
107
|
+
[out:json];
|
|
108
|
+
(
|
|
109
|
+
relation(poly:"{polygon_coords}")['route'='{public_transport_type}'];
|
|
110
|
+
);
|
|
111
|
+
out geom;
|
|
112
|
+
"""
|
|
113
|
+
logger.debug(f"Downloading routes from OSM with type <{public_transport_type}> ...")
|
|
114
|
+
result = requests.post(config.overpass_url, data={"data": overpass_query}, timeout=config.timeout)
|
|
115
|
+
if result.status_code == 200:
|
|
116
|
+
json_result = result.json()["elements"]
|
|
117
|
+
data = pd.DataFrame(json_result)
|
|
118
|
+
data["transport_type"] = public_transport_type
|
|
119
|
+
return data
|
|
120
|
+
raise RequestError(
|
|
121
|
+
message=f"Request failed with status code {result.status_code}, reason: {result.reason}",
|
|
122
|
+
status_code=result.status_code,
|
|
123
|
+
reason=result.reason,
|
|
124
|
+
response_text=result.text,
|
|
125
|
+
response_content=result.content,
|
|
126
|
+
)
|