sibi-dst 0.3.32__py3-none-any.whl → 0.3.34__py3-none-any.whl
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.
- sibi_dst/df_helper/_df_helper.py +108 -5
- sibi_dst/df_helper/_parquet_artifact.py +63 -0
- sibi_dst/df_helper/_parquet_reader.py +36 -0
- sibi_dst/df_helper/backends/django/_db_connection.py +41 -1
- sibi_dst/df_helper/backends/django/_io_dask.py +211 -3
- sibi_dst/df_helper/backends/django/_load_from_db.py +96 -1
- sibi_dst/df_helper/backends/django/_sql_model_builder.py +132 -6
- sibi_dst/df_helper/backends/http/_http_config.py +52 -1
- sibi_dst/df_helper/backends/parquet/_filter_handler.py +28 -0
- sibi_dst/df_helper/backends/parquet/_parquet_options.py +105 -1
- sibi_dst/df_helper/backends/sqlalchemy/_db_connection.py +17 -0
- sibi_dst/df_helper/backends/sqlalchemy/_load_from_db.py +80 -2
- sibi_dst/df_helper/backends/sqlalchemy/_sql_model_builder.py +90 -29
- sibi_dst/df_helper/core/_params_config.py +59 -0
- sibi_dst/geopy_helper/geo_location_service.py +14 -0
- sibi_dst/geopy_helper/utils.py +37 -3
- sibi_dst/osmnx_helper/base_osm_map.py +254 -0
- sibi_dst/osmnx_helper/utils.py +226 -4
- sibi_dst/utils/clickhouse_writer.py +27 -0
- sibi_dst/utils/data_utils.py +32 -1
- sibi_dst/utils/data_wrapper.py +94 -6
- sibi_dst/utils/date_utils.py +35 -0
- sibi_dst/utils/log_utils.py +19 -2
- sibi_dst/utils/parquet_saver.py +0 -106
- {sibi_dst-0.3.32.dist-info → sibi_dst-0.3.34.dist-info}/METADATA +1 -1
- {sibi_dst-0.3.32.dist-info → sibi_dst-0.3.34.dist-info}/RECORD +27 -27
- {sibi_dst-0.3.32.dist-info → sibi_dst-0.3.34.dist-info}/WHEEL +0 -0
@@ -11,6 +11,64 @@ from folium.plugins import Fullscreen
|
|
11
11
|
|
12
12
|
|
13
13
|
class BaseOsmMap:
|
14
|
+
"""
|
15
|
+
BaseOsmMap class serves as a foundational class for creating and managing maps
|
16
|
+
using OpenStreetMap data. It integrates features from libraries like osmnx and
|
17
|
+
folium to facilitate visualization, tile layer management, bounding box handling,
|
18
|
+
and nearest node calculation for geographic coordinates.
|
19
|
+
|
20
|
+
The class is designed to be subclassed, enabling customization of map processing
|
21
|
+
logic while providing core functionalities needed for map creation and manipulation.
|
22
|
+
|
23
|
+
:ivar tile_options: Dictionary containing options for pre-defined tile layers.
|
24
|
+
Keys represent display names while values represent tile layer configurations.
|
25
|
+
:type tile_options: dict
|
26
|
+
:ivar bounds: Default map bounds representing geographical extents for Costa Rica.
|
27
|
+
:type bounds: list[list[float]]
|
28
|
+
:ivar osmnx_graph: Input graph representing OpenStreetMap network data, used for
|
29
|
+
operations like subgraph extraction and nearest node calculation.
|
30
|
+
:type osmnx_graph: networkx.classes.multidigraph.MultiDiGraph
|
31
|
+
:ivar df: DataFrame containing data points with geographic coordinates. It must
|
32
|
+
not be empty and should include latitude and longitude columns as specified
|
33
|
+
in `lat_col` and `lon_col`.
|
34
|
+
:type df: pandas.DataFrame
|
35
|
+
:ivar lat_col: Name of the column in `df` representing latitude coordinates of data points.
|
36
|
+
:type lat_col: str
|
37
|
+
:ivar lon_col: Name of the column in `df` representing longitude coordinates of data points.
|
38
|
+
:type lon_col: str
|
39
|
+
:ivar osm_map: Folium Map object that serves as a container for geographic visualization.
|
40
|
+
This is dynamically initialized based on the provided data.
|
41
|
+
:type osm_map: folium.Map
|
42
|
+
:ivar G: Subgraph of the provided `osmnx_graph`, extracted based on bounding box
|
43
|
+
derived from the input data.
|
44
|
+
:type G: networkx.classes.multidigraph.MultiDiGraph
|
45
|
+
:ivar map_html_title: HTML-escaped title for map visualization, shown on the interactive map.
|
46
|
+
:type map_html_title: str
|
47
|
+
:ivar zoom_start: Initial zoom level for the map when rendered.
|
48
|
+
:type zoom_start: int
|
49
|
+
:ivar fullscreen: Boolean option to enable or disable fullscreen control on the interactive map.
|
50
|
+
:type fullscreen: bool
|
51
|
+
:ivar fullscreen_position: Position of the fullscreen control on the map, e.g., 'topright'.
|
52
|
+
:type fullscreen_position: str
|
53
|
+
:ivar tiles: Default tile layer to be used for the map visualization.
|
54
|
+
:type tiles: str
|
55
|
+
:ivar verbose: Boolean flag indicating if verbose logging of operations should be enabled.
|
56
|
+
:type verbose: bool
|
57
|
+
:ivar sort_keys: Keys to sort the DataFrame by, if applicable, before processing.
|
58
|
+
:type sort_keys: list[str] or None
|
59
|
+
:ivar dt_field: Column name in `df` representing datetimes, if applicable, for specialized processing.
|
60
|
+
:type dt_field: str or None
|
61
|
+
:ivar dt: List of datetime objects extracted from `dt_field` in `df`, dynamically initialized.
|
62
|
+
:type dt: list[datetime.datetime] or None
|
63
|
+
:ivar calc_nearest_nodes: Boolean flag to indicate whether nearest nodes to the input
|
64
|
+
points should be calculated from `osmnx_graph`.
|
65
|
+
:type calc_nearest_nodes: bool
|
66
|
+
:ivar nearest_nodes: List of nearest node IDs for each point in the input data, if calculated.
|
67
|
+
:type nearest_nodes: list[int] or None
|
68
|
+
:ivar max_bounds: Boolean indicating whether bounds (default to Costa Rica) should be applied
|
69
|
+
to fit the map within those limits interactively.
|
70
|
+
:type max_bounds: bool
|
71
|
+
"""
|
14
72
|
tile_options = {
|
15
73
|
"OpenStreetMap": "OpenStreetMap",
|
16
74
|
"CartoDB": "cartodbpositron",
|
@@ -20,6 +78,47 @@ class BaseOsmMap:
|
|
20
78
|
bounds = [[8.0340, -85.9417], [11.2192, -82.5566]]
|
21
79
|
|
22
80
|
def __init__(self, osmnx_graph=None, df=None, **kwargs):
|
81
|
+
"""
|
82
|
+
Initializes a map visualization handler with input parameters for managing and displaying
|
83
|
+
spatial data in conjunction with an OSMnx graph. It validates input parameters for completeness
|
84
|
+
and correctness, and configures internal attributes for the visualization of map layers.
|
85
|
+
|
86
|
+
:param osmnx_graph: The OSMnx graph to be used for spatial data processing.
|
87
|
+
:param df: The pandas DataFrame containing spatial data.
|
88
|
+
:param kwargs: Additional keyword arguments for customization.
|
89
|
+
:type osmnx_graph: object
|
90
|
+
:type df: pandas.DataFrame
|
91
|
+
:type kwargs: dict
|
92
|
+
|
93
|
+
:raises ValueError: If `osmnx_graph` is not provided.
|
94
|
+
:raises ValueError: If `df` is not provided.
|
95
|
+
:raises ValueError: If the provided `df` is empty.
|
96
|
+
|
97
|
+
:attribute df: A copy of the provided DataFrame.
|
98
|
+
:attribute osmnx_graph: The input OSMnx graph used for spatial data operations.
|
99
|
+
:attribute lat_col: The name of the column in `df` containing latitude values.
|
100
|
+
:attribute lon_col: The name of the column in `df` containing longitude values.
|
101
|
+
:attribute osm_map: Internal representation of the map; initialized to None.
|
102
|
+
:attribute G: Placeholder for graph-related data; initialized to None.
|
103
|
+
:attribute map_html_title: Sanitized HTML title to be used in rendered map outputs.
|
104
|
+
:attribute zoom_start: The initial zoom level for the map visualization (default: 13).
|
105
|
+
:attribute fullscreen: Boolean flag to enable fullscreen map display (default: True).
|
106
|
+
:attribute fullscreen_position: The position of the fullscreen control button
|
107
|
+
(default: 'topright').
|
108
|
+
:attribute tiles: The tile set to be used for the map visualization (default: 'OpenStreetMap').
|
109
|
+
:attribute verbose: Boolean flag for enabling verbose output (default: False).
|
110
|
+
:attribute sort_keys: Optional parameter dictating sorting order for keys (default: None).
|
111
|
+
:attribute dt_field: Optional name of the datetime field in the DataFrame (default: None).
|
112
|
+
:attribute dt: Placeholder for date/time data; initialized to None.
|
113
|
+
:attribute calc_nearest_nodes: Boolean flag to calculate nearest nodes to coordinates
|
114
|
+
(default: False).
|
115
|
+
:attribute nearest_nodes: Placeholder for nearest nodes data; initialized to None.
|
116
|
+
:attribute max_bounds: Boolean flag to restrict the map view to maximum bounds
|
117
|
+
(default: False).
|
118
|
+
|
119
|
+
:note: This class also initializes necessary internal functionalities including DataFrame
|
120
|
+
pre-processing (`_prepare_df`) and base map setup (`_initialise_map`).
|
121
|
+
"""
|
23
122
|
if osmnx_graph is None:
|
24
123
|
raise ValueError('osmnx_graph must be provided')
|
25
124
|
if df is None:
|
@@ -50,6 +149,21 @@ class BaseOsmMap:
|
|
50
149
|
|
51
150
|
|
52
151
|
def _prepare_df(self):
|
152
|
+
"""
|
153
|
+
Prepares the underlying DataFrame for further processing.
|
154
|
+
|
155
|
+
This method performs several operations on the DataFrame, such as sorting,
|
156
|
+
resetting the index, and extracting relevant columns into various lists.
|
157
|
+
Additionally, it calculates the nearest nodes from an OSMnx graph if
|
158
|
+
required. The operations are governed by instance attributes and their
|
159
|
+
current states.
|
160
|
+
|
161
|
+
:raises AttributeError: If required attributes for processing are not
|
162
|
+
correctly set or do not exist in the DataFrame.
|
163
|
+
:param self: Object that contains the DataFrame (df) and other related
|
164
|
+
attributes required for processing.
|
165
|
+
:return: None
|
166
|
+
"""
|
53
167
|
if self.sort_keys:
|
54
168
|
self.df.sort_values(by=self.sort_keys, inplace=True)
|
55
169
|
self.df.reset_index(drop=True, inplace=True)
|
@@ -63,6 +177,15 @@ class BaseOsmMap:
|
|
63
177
|
|
64
178
|
|
65
179
|
def _initialise_map(self):
|
180
|
+
"""
|
181
|
+
Initializes an OpenStreetMap (OSM) map instance and extracts a subgraph of map
|
182
|
+
data based on provided GPS points. The map's central location is calculated
|
183
|
+
as the mean of the latitudinal and longitudinal values of the GPS points.
|
184
|
+
Additionally, a bounding box is determined to extract the relevant section
|
185
|
+
of the map graph.
|
186
|
+
|
187
|
+
:return: None
|
188
|
+
"""
|
66
189
|
gps_array = np.array(self.gps_points)
|
67
190
|
mean_latitude = np.mean(gps_array[:, 0])
|
68
191
|
mean_longitude = np.mean(gps_array[:, 1])
|
@@ -73,6 +196,24 @@ class BaseOsmMap:
|
|
73
196
|
|
74
197
|
|
75
198
|
def _attach_supported_tiles(self):
|
199
|
+
"""
|
200
|
+
Attach supported tile layers from the provided tile options to the map object.
|
201
|
+
|
202
|
+
This method normalizes the default tile layer name and ensures that it is not
|
203
|
+
duplicated in the map by filtering out the default tile from the `tile_options`.
|
204
|
+
Then, it iterates over the filtered tile options and attaches each supported
|
205
|
+
tile layer to the map object using the folium library.
|
206
|
+
|
207
|
+
:raises KeyError: If any key in `tile_options` is invalid or not found.
|
208
|
+
|
209
|
+
:param self:
|
210
|
+
The instance of the class. It is expected to have the following attributes:
|
211
|
+
- tiles (str): The name of the default tile layer.
|
212
|
+
- tile_options (Dict[str, str]): A dictionary of tile layer names
|
213
|
+
and their associated URLs.
|
214
|
+
- osm_map (folium.Map): The map object to which tile layers are attached.
|
215
|
+
:return: None
|
216
|
+
"""
|
76
217
|
# Normalize the default tile name to lowercase for comparison
|
77
218
|
normalized_default_tile = self.tiles.lower()
|
78
219
|
|
@@ -84,6 +225,22 @@ class BaseOsmMap:
|
|
84
225
|
|
85
226
|
|
86
227
|
def _get_bounding_box_from_points(self, margin=0.001):
|
228
|
+
"""
|
229
|
+
Calculate a bounding box from GPS points with an additional margin.
|
230
|
+
|
231
|
+
This method processes the GPS points in the `self.gps_points` list
|
232
|
+
and calculates a geographical bounding box encompassing all
|
233
|
+
points with an optional margin added to its boundaries. The
|
234
|
+
bounding box is defined by the northernmost, southernmost,
|
235
|
+
easternmost, and westernmost boundaries.
|
236
|
+
|
237
|
+
:param margin: A float value that adds a margin to the bounding
|
238
|
+
box boundaries.
|
239
|
+
Defaults to 0.001.
|
240
|
+
:return: A tuple containing four float values in the order:
|
241
|
+
north (northernmost boundary), south (southernmost boundary),
|
242
|
+
east (easternmost boundary), and west (westernmost boundary).
|
243
|
+
"""
|
87
244
|
latitudes = [point[0] for point in self.gps_points]
|
88
245
|
longitudes = [point[1] for point in self.gps_points]
|
89
246
|
|
@@ -96,6 +253,26 @@ class BaseOsmMap:
|
|
96
253
|
|
97
254
|
|
98
255
|
def _extract_subgraph(self, north, south, east, west):
|
256
|
+
"""
|
257
|
+
Extracts a subgraph from the OSMnx graph based on a specified bounding box.
|
258
|
+
|
259
|
+
This function takes the northern, southern, eastern, and western bounds to
|
260
|
+
define a geographic bounding box. It creates a polygon from these bounds and
|
261
|
+
identifies nodes within the graph that fall within this boundary. A new subgraph
|
262
|
+
is then created using these identified nodes. This functionality supports different
|
263
|
+
approaches depending on the OSMnx version.
|
264
|
+
|
265
|
+
:param north: The northern boundary of the bounding box.
|
266
|
+
:type north: float
|
267
|
+
:param south: The southern boundary of the bounding box.
|
268
|
+
:type south: float
|
269
|
+
:param east: The eastern boundary of the bounding box.
|
270
|
+
:type east: float
|
271
|
+
:param west: The western boundary of the bounding box.
|
272
|
+
:type west: float
|
273
|
+
:return: A subgraph extracted from the OSMnx graph containing nodes within the bounding box.
|
274
|
+
:rtype: networkx.Graph
|
275
|
+
"""
|
99
276
|
# Create a bounding box polygon
|
100
277
|
# from osmnx v2 this is how it is done
|
101
278
|
if ox.__version__ >= '2.0':
|
@@ -117,18 +294,45 @@ class BaseOsmMap:
|
|
117
294
|
|
118
295
|
@abstractmethod
|
119
296
|
def process_map(self):
|
297
|
+
"""
|
298
|
+
This abstract method serves as a blueprint for processing a map. It is intentionally
|
299
|
+
left unimplemented and must be overridden in any concrete subclass. Subclasses should
|
300
|
+
provide their own specific logic for map processing by implementing this interface.
|
301
|
+
|
302
|
+
:return: None
|
303
|
+
"""
|
120
304
|
# this is to be implemented at the subclass level
|
121
305
|
# implement here your specific map logic.
|
122
306
|
...
|
123
307
|
|
124
308
|
|
125
309
|
def pre_process_map(self):
|
310
|
+
"""
|
311
|
+
Pre-processes the map data.
|
312
|
+
|
313
|
+
This method is designed to be overridden in a subclass to perform specific
|
314
|
+
pre-processing procedures on map data. When overridden, the implementation
|
315
|
+
in the subclass must call `super().pre_process_map` first to inherit and
|
316
|
+
maintain the existing behavior of this base implementation.
|
317
|
+
|
318
|
+
:return: None
|
319
|
+
"""
|
126
320
|
# this is to be implemented at the subclass level
|
127
321
|
# call super().pre_process_map first to inherit the following behaviour
|
128
322
|
...
|
129
323
|
|
130
324
|
|
131
325
|
def _post_process_map(self):
|
326
|
+
"""
|
327
|
+
Performs post-processing tasks on the map object to enhance its functionality
|
328
|
+
and ensure required adjustments or attributes are attached to it. This method
|
329
|
+
is mainly used internally to finalize map setup based on current configurations.
|
330
|
+
|
331
|
+
:raises AttributeError: This error is raised if one of the required attributes
|
332
|
+
is not properly initialized or missing during the processing.
|
333
|
+
:raises ValueError: This error is raised if provided bounds are invalid or
|
334
|
+
incompatible for fitting.
|
335
|
+
"""
|
132
336
|
self._attach_supported_tiles()
|
133
337
|
self.add_tile_layer()
|
134
338
|
self._add_fullscreen()
|
@@ -138,26 +342,76 @@ class BaseOsmMap:
|
|
138
342
|
|
139
343
|
|
140
344
|
def add_tile_layer(self):
|
345
|
+
"""
|
346
|
+
Adds a tile layer to the OpenStreetMap (OSM) representation.
|
347
|
+
This method is intended to handle the inclusion of additional
|
348
|
+
tile layers in the map. Subclasses should override this method,
|
349
|
+
call their custom logic, and conclude with a call to this base
|
350
|
+
implementation to add necessary controls to the map instance.
|
351
|
+
|
352
|
+
:rtype: None
|
353
|
+
:return: This method does not return any value.
|
354
|
+
"""
|
141
355
|
# Override in subclass and call super().add_tile_layer at the end
|
142
356
|
folium.LayerControl().add_to(self.osm_map)
|
143
357
|
|
144
358
|
|
145
359
|
def _add_fullscreen(self):
|
360
|
+
"""
|
361
|
+
Adds a fullscreen functionality to the map if the fullscreen attribute is set.
|
362
|
+
|
363
|
+
This method checks whether the fullscreen mode is enabled by evaluating
|
364
|
+
the `fullscreen` attribute of the class. If `fullscreen` is set to True,
|
365
|
+
a Fullscreen instance is created with the specified position from
|
366
|
+
`fullscreen_position` and added to the map (`osm_map`).
|
367
|
+
|
368
|
+
:return: None
|
369
|
+
"""
|
146
370
|
if self.fullscreen:
|
147
371
|
Fullscreen(position=self.fullscreen_position).add_to(self.osm_map)
|
148
372
|
|
149
373
|
|
150
374
|
def _add_map_title(self):
|
375
|
+
"""
|
376
|
+
Adds a title to the map if the title is specified.
|
377
|
+
|
378
|
+
This method checks if the attribute `map_html_title` is set. If it is,
|
379
|
+
it creates an HTML element containing the title and adds it to the
|
380
|
+
map's root HTML structure using Folium's API.
|
381
|
+
|
382
|
+
:raises AttributeError: If `osm_map` or `osm_map.get_root()` is not defined
|
383
|
+
or does not support the necessary operations.
|
384
|
+
"""
|
151
385
|
if self.map_html_title:
|
152
386
|
self.osm_map.get_root().html.add_child(folium.Element(self.map_html_title))
|
153
387
|
|
154
388
|
|
155
389
|
@staticmethod
|
156
390
|
def _sanitize_html(input_html):
|
391
|
+
"""
|
392
|
+
Sanitizes the provided HTML input by escaping special HTML characters.
|
393
|
+
This method ensures the input string is safe for use in HTML contexts
|
394
|
+
by converting characters like `<`, `>`, and `&` into their corresponding
|
395
|
+
HTML-safe representations.
|
396
|
+
|
397
|
+
:param input_html: The HTML string that needs to be sanitized.
|
398
|
+
:type input_html: str
|
399
|
+
:return: The sanitized version of the input HTML string with special
|
400
|
+
characters escaped.
|
401
|
+
:rtype: str
|
402
|
+
"""
|
157
403
|
return html.escape(input_html)
|
158
404
|
|
159
405
|
|
160
406
|
def generate_map(self):
|
407
|
+
"""
|
408
|
+
Generates a map by executing preprocessing, main processing, and
|
409
|
+
post-processing tasks sequentially. This method combines multiple
|
410
|
+
stages to prepare and retrieve the final map object.
|
411
|
+
|
412
|
+
:return: The completed and processed OpenStreetMap map instance
|
413
|
+
:rtype: object
|
414
|
+
"""
|
161
415
|
self.pre_process_map()
|
162
416
|
self.process_map()
|
163
417
|
self._post_process_map()
|
sibi_dst/osmnx_helper/utils.py
CHANGED
@@ -23,6 +23,37 @@ from geopy.distance import geodesic
|
|
23
23
|
|
24
24
|
|
25
25
|
class PBFHandler:
|
26
|
+
"""
|
27
|
+
Handles the creation, management, and visualization of graph data derived
|
28
|
+
from .pbf (Protocolbuffer Binary Format) files. This class enables the
|
29
|
+
loading, processing, saving, and reutilization of graph, node, and edge
|
30
|
+
data for geographical regions, supporting verbose mode for detailed outputs.
|
31
|
+
|
32
|
+
:ivar graph: The generated graph object representing the spatial network; can be None if not yet loaded or processed.
|
33
|
+
:type graph: Optional[NetworkX.Graph]
|
34
|
+
:ivar nodes: GeoDataFrame representing the nodes of the graph; can be None if not yet loaded or processed.
|
35
|
+
:type nodes: Optional[geopandas.GeoDataFrame]
|
36
|
+
:ivar edges: GeoDataFrame representing the edges of the graph; can be None if not yet loaded or processed.
|
37
|
+
:type edges: Optional[geopandas.GeoDataFrame]
|
38
|
+
:ivar rebuild: Indicates whether to rebuild the graph data, ignoring any existing cached files. Default is ``False``.
|
39
|
+
:type rebuild: bool
|
40
|
+
:ivar verbose: Enables verbose mode to provide detailed status messages during operations. Default is ``False``.
|
41
|
+
:type verbose: bool
|
42
|
+
:ivar place: The name of the geographical region to process with OpenStreetMap. Default is ``Costa Rica``.
|
43
|
+
:type place: str
|
44
|
+
:ivar filepath: The path to the directory where the graph, nodes, and edges pickle files are saved. Default is ``gis_data/``.
|
45
|
+
:type filepath: str
|
46
|
+
:ivar file_prefix: The prefix for the filenames of the saved graph, node, and edge pickle files. Default is ``costa-rica-``.
|
47
|
+
:type file_prefix: str
|
48
|
+
:ivar network_type: The type of network to extract from OpenStreetMap, such as "all" or other specific network types. Default is ``all``.
|
49
|
+
:type network_type: str
|
50
|
+
:ivar graph_file: Full path of the file to save or load the graph data as a pickle file.
|
51
|
+
:type graph_file: str
|
52
|
+
:ivar node_file: Full path of the file to save or load the graph's node data as a pickle file.
|
53
|
+
:type node_file: str
|
54
|
+
:ivar edge_file: Full path of the file to save or load the graph's edge data as a pickle file.
|
55
|
+
:type edge_file: str
|
56
|
+
"""
|
26
57
|
def __init__(self, **kwargs):
|
27
58
|
self.graph = None
|
28
59
|
self.nodes = None
|
@@ -38,6 +69,23 @@ class PBFHandler:
|
|
38
69
|
self.edge_file = f"{self.filepath}{self.file_prefix}edges.pkl"
|
39
70
|
|
40
71
|
def load(self):
|
72
|
+
"""
|
73
|
+
Loads the required data files for processing. If the files do not exist or
|
74
|
+
if the `rebuild` flag is set to True, it will process and recreate the
|
75
|
+
necessary data from the source. Otherwise, it will load the data from
|
76
|
+
existing pickle files. This function ensures the target directory exists,
|
77
|
+
and processes files conditionally based on their presence.
|
78
|
+
|
79
|
+
:param verbose: Flag to control the verbosity of the function's output.
|
80
|
+
:param rebuild: Indicates whether the data should be rebuilt from the raw
|
81
|
+
source files.
|
82
|
+
:param graph_file: Path to the graph file to be loaded or rebuilt.
|
83
|
+
:param node_file: Path to the node file to be loaded or rebuilt.
|
84
|
+
:param edge_file: Path to the edge file to be loaded or rebuilt.
|
85
|
+
:param filepath: Path to the directory where files are processed and saved.
|
86
|
+
|
87
|
+
:return: None
|
88
|
+
"""
|
41
89
|
if self.verbose:
|
42
90
|
print("Loading data...")
|
43
91
|
|
@@ -62,7 +110,31 @@ class PBFHandler:
|
|
62
110
|
|
63
111
|
def process_pbf(self):
|
64
112
|
"""
|
65
|
-
|
113
|
+
Processes the Protocolbuffer Binary Format (PBF) data specified for a given place by
|
114
|
+
utilizing the OSMnx library to create a graph representation and extracts nodes and
|
115
|
+
edges into GeoDataFrames. The function provides verbose output if enabled.
|
116
|
+
|
117
|
+
:param self: Refers to the current instance of the class containing this method.
|
118
|
+
|
119
|
+
:param self.verbose: bool
|
120
|
+
A flag to control verbose output. If True, detailed processing status messages are
|
121
|
+
logged to the console.
|
122
|
+
|
123
|
+
:param self.place: str
|
124
|
+
The name or description of the geographic place for which PBF data is processed. It
|
125
|
+
is used to construct a graph representation of the place.
|
126
|
+
|
127
|
+
:param self.network_type: str
|
128
|
+
The type of network graph to be created, typically one of 'all', 'walk', 'drive',
|
129
|
+
etc., reflecting the type of paths or streets included in the graph.
|
130
|
+
|
131
|
+
:return: None
|
132
|
+
This function does not return a value, but updates class attributes ``graph``,
|
133
|
+
``nodes``, and ``edges``.
|
134
|
+
|
135
|
+
:raises Exception:
|
136
|
+
Raises a general exception when there is an error in processing the PBF data. Error
|
137
|
+
details are printed when verbose output is enabled.
|
66
138
|
"""
|
67
139
|
try:
|
68
140
|
if self.verbose:
|
@@ -79,7 +151,20 @@ class PBFHandler:
|
|
79
151
|
|
80
152
|
def save_to_pickle(self):
|
81
153
|
"""
|
82
|
-
|
154
|
+
Saves data, including graph, nodes, and edges, to pickle files. Each data object is
|
155
|
+
saved to its corresponding file if available. If verbose mode is enabled, prints
|
156
|
+
messages indicating the saving progress and success.
|
157
|
+
|
158
|
+
:param self:
|
159
|
+
Represents the instance of the class that contains attributes `graph_file`,
|
160
|
+
`graph`, `node_file`, `nodes`, `edge_file`, `edges`, and `verbose`. These
|
161
|
+
attributes determine the files to save to and the data to save.
|
162
|
+
|
163
|
+
:raises Exception:
|
164
|
+
Raises an exception if an error occurs during the saving process.
|
165
|
+
|
166
|
+
:return:
|
167
|
+
None
|
83
168
|
"""
|
84
169
|
try:
|
85
170
|
if self.verbose:
|
@@ -104,7 +189,13 @@ class PBFHandler:
|
|
104
189
|
|
105
190
|
def load_from_pickle(self):
|
106
191
|
"""
|
107
|
-
|
192
|
+
Loads data from pickle files specified by the attributes `graph_file`, `node_file`,
|
193
|
+
and `edge_file` and assigns them to the corresponding attributes `graph`,
|
194
|
+
`nodes`, and `edges`, respectively. Displays verbose messages during the load
|
195
|
+
process if the `verbose` attribute is set to True.
|
196
|
+
|
197
|
+
:raises Exception: If an error occurs during reading or deserialization of the
|
198
|
+
pickle files.
|
108
199
|
"""
|
109
200
|
try:
|
110
201
|
if self.verbose:
|
@@ -128,7 +219,13 @@ class PBFHandler:
|
|
128
219
|
|
129
220
|
def plot_graph(self):
|
130
221
|
"""
|
131
|
-
|
222
|
+
Plots the loaded graph using the OSMnx library.
|
223
|
+
|
224
|
+
This method checks if a graph is loaded and, if available, plots it. Outputs
|
225
|
+
verbose messages during the process if verbosity is enabled.
|
226
|
+
|
227
|
+
:raises Exception: Raises if an error occurs during the plotting process.
|
228
|
+
:return: None
|
132
229
|
"""
|
133
230
|
try:
|
134
231
|
if self.graph is not None:
|
@@ -145,6 +242,23 @@ class PBFHandler:
|
|
145
242
|
|
146
243
|
|
147
244
|
def get_bounding_box_from_points(gps_points, margin=0.001):
|
245
|
+
"""
|
246
|
+
Calculates a bounding box from a list of GPS points, with an optional margin added
|
247
|
+
to expand the bounding box in all directions. The function iterates over the GPS
|
248
|
+
points to determine the maximum and minimum latitude and longitude values, then
|
249
|
+
applies the specified margin to calculate the bounding box's boundaries.
|
250
|
+
|
251
|
+
:param gps_points: A list of GPS points, where each point is represented as a tuple
|
252
|
+
containing a latitude and a longitude (latitude, longitude).
|
253
|
+
:type gps_points: list[tuple[float, float]]
|
254
|
+
:param margin: An optional margin value to expand the bounding box in all directions.
|
255
|
+
Default value is 0.001.
|
256
|
+
:type margin: float
|
257
|
+
:return: A tuple containing the bounding box boundaries in the following order:
|
258
|
+
north (maximum latitude), south (minimum latitude), east (maximum longitude),
|
259
|
+
and west (minimum longitude), each adjusted with the margin.
|
260
|
+
:rtype: tuple[float, float, float, float]
|
261
|
+
"""
|
148
262
|
latitudes = [point[0] for point in gps_points]
|
149
263
|
longitudes = [point[1] for point in gps_points]
|
150
264
|
|
@@ -157,6 +271,28 @@ def get_bounding_box_from_points(gps_points, margin=0.001):
|
|
157
271
|
|
158
272
|
|
159
273
|
def add_arrows(map_object, locations, color, n_arrows):
|
274
|
+
"""
|
275
|
+
Adds directional arrows to a map object to indicate paths or flows along a polyline
|
276
|
+
defined by the given locations.
|
277
|
+
|
278
|
+
The function computes directional arrows based on the locations list, places them
|
279
|
+
along the defined path at intervals determined by the number of arrows, and adds
|
280
|
+
these arrows to the specified `map_object`.
|
281
|
+
|
282
|
+
.. note::
|
283
|
+
The function works optimally when the number of locations is greater than two.
|
284
|
+
|
285
|
+
:param map_object: The folium map object to which the directional arrows will be added.
|
286
|
+
:param locations: A list containing tuples of latitude and longitude values that define
|
287
|
+
the polyline. Each tuple represents a geographic point.
|
288
|
+
:type locations: list[tuple[float, float]]
|
289
|
+
:param color: The color to be used for the directional arrows.
|
290
|
+
:type color: str
|
291
|
+
:param n_arrows: The number of arrows to be drawn along the path.
|
292
|
+
:type n_arrows: int
|
293
|
+
:return: The modified folium map object containing the added arrows.
|
294
|
+
:rtype: folium.Map
|
295
|
+
"""
|
160
296
|
# Get the number of locations
|
161
297
|
n = len(locations)
|
162
298
|
|
@@ -179,6 +315,26 @@ def add_arrows(map_object, locations, color, n_arrows):
|
|
179
315
|
|
180
316
|
|
181
317
|
def extract_subgraph(G, north, south, east, west):
|
318
|
+
"""
|
319
|
+
Extracts a subgraph from the input graph `G` within a specified bounding box. The bounding
|
320
|
+
box is defined by its north, south, east, and west coordinates. The function identifies
|
321
|
+
nodes from the graph that lie within this bounding box and creates a subgraph containing
|
322
|
+
only these nodes and their corresponding edges.
|
323
|
+
|
324
|
+
:param G: The input graph representing the original main graph.
|
325
|
+
:type G: networkx.Graph
|
326
|
+
:param north: The northern latitude that defines the upper boundary of the bounding box.
|
327
|
+
:type north: float
|
328
|
+
:param south: The southern latitude that defines the lower boundary of the bounding box.
|
329
|
+
:type south: float
|
330
|
+
:param east: The eastern longitude that defines the right boundary of the bounding box.
|
331
|
+
:type east: float
|
332
|
+
:param west: The western longitude that defines the left boundary of the bounding box.
|
333
|
+
:type west: float
|
334
|
+
:return: A subgraph extracted from the input graph `G` containing nodes and edges within
|
335
|
+
the specified bounding box.
|
336
|
+
:rtype: networkx.Graph
|
337
|
+
"""
|
182
338
|
# Create a bounding box polygon
|
183
339
|
# from osmnx v2 this is how it is done
|
184
340
|
if ox.__version__ >= '2.0':
|
@@ -199,6 +355,26 @@ def extract_subgraph(G, north, south, east, west):
|
|
199
355
|
|
200
356
|
|
201
357
|
def get_distance_between_points(point_a, point_b, unit='km'):
|
358
|
+
"""
|
359
|
+
Calculate the geographical distance between two points on Earth.
|
360
|
+
|
361
|
+
This function computes the distance between two points on the Earth's surface
|
362
|
+
specified in their geographical coordinates (latitude, longitude). The calculation
|
363
|
+
employs the geodesic distance, which represents the shortest distance between
|
364
|
+
two points on the Earth's surface. The distance can be returned in different units of
|
365
|
+
measurement depending on the provided parameter.
|
366
|
+
|
367
|
+
:param point_a: A tuple representing the latitude and longitude of the first
|
368
|
+
point in decimal degrees (e.g., (latitude, longitude)). Must be a tuple of
|
369
|
+
two float values.
|
370
|
+
:param point_b: A tuple representing the latitude and longitude of the second
|
371
|
+
point in decimal degrees (e.g., (latitude, longitude)). Must be a tuple of
|
372
|
+
two float values.
|
373
|
+
:param unit: A string value representing the unit of the calculated distance. Can be
|
374
|
+
'km' for kilometers (default), 'm' for meters, or 'mi' for miles.
|
375
|
+
:return: A float value of the distance between the two points in the specified unit.
|
376
|
+
Returns 0 if the input validation fails or the specified unit is invalid.
|
377
|
+
"""
|
202
378
|
if not isinstance(point_a, tuple) or len(point_a) != 2:
|
203
379
|
return 0
|
204
380
|
if not all(isinstance(x, float) and not math.isnan(x) for x in point_a):
|
@@ -226,6 +402,20 @@ tile_options = {
|
|
226
402
|
|
227
403
|
|
228
404
|
def attach_supported_tiles(map_object, default_tile="OpenStreetMap"):
|
405
|
+
"""
|
406
|
+
Attaches supported tile layers to a given folium map object, excluding the
|
407
|
+
default tile layer, to provide layer selection functionality in the map.
|
408
|
+
|
409
|
+
This function allows dynamic addition of multiple tile layers to the map
|
410
|
+
object while avoiding duplication of the default tile. By filtering out the
|
411
|
+
default tile, it prevents redundancy and ensures a cleaner map interface.
|
412
|
+
|
413
|
+
:param map_object: The folium map object to which the tile layers will be added.
|
414
|
+
It must be an instance of Folium's Map class or a compatible map object.
|
415
|
+
:param default_tile: The name of the default tile layer to exclude from the
|
416
|
+
list of tiles added to the map. If not specified, defaults to 'OpenStreetMap'.
|
417
|
+
:return: None. The function modifies the provided map object in place.
|
418
|
+
"""
|
229
419
|
# Normalize the default tile name to lowercase for comparison
|
230
420
|
normalized_default_tile = default_tile.lower()
|
231
421
|
|
@@ -237,12 +427,44 @@ def attach_supported_tiles(map_object, default_tile="OpenStreetMap"):
|
|
237
427
|
|
238
428
|
|
239
429
|
def get_graph(**options):
|
430
|
+
"""
|
431
|
+
Generates and returns a graph along with its nodes and edges based on the
|
432
|
+
provided options. The function initializes a PBFHandler instance with the
|
433
|
+
given options, processes any data required, and retrieves the resulting
|
434
|
+
graph structure.
|
435
|
+
|
436
|
+
:param options: Variable-length keyword arguments passed to initialize the
|
437
|
+
PBFHandler instance. These parameters play a role in
|
438
|
+
determining how the graph data is processed and structured.
|
439
|
+
:return: Returns a tuple containing three elements:
|
440
|
+
- The generated graph object
|
441
|
+
- The list or collection of nodes within the graph
|
442
|
+
- The list or collection of edges that describe relationships
|
443
|
+
between nodes in the graph
|
444
|
+
"""
|
240
445
|
handler = PBFHandler(**options)
|
241
446
|
handler.load()
|
242
447
|
return handler.graph, handler.nodes, handler.edges
|
243
448
|
|
244
449
|
|
245
450
|
def add_query_params(url, params):
|
451
|
+
"""
|
452
|
+
Update the query parameters of a given URL with new parameters.
|
453
|
+
|
454
|
+
This function takes a URL and a dictionary of parameters, merges these
|
455
|
+
parameters with the existing parameters in the URL, and returns a new URL
|
456
|
+
with updated query parameters.
|
457
|
+
|
458
|
+
:param url: The original URL whose query parameters are to be updated,
|
459
|
+
including the scheme, netloc, path, and optional query string and fragment.
|
460
|
+
:type url: str
|
461
|
+
:param params: A dictionary containing the new parameters to be added or updated
|
462
|
+
in the query string of the given URL.
|
463
|
+
:type params: dict
|
464
|
+
:return: A new URL with updated query parameters after merging the original
|
465
|
+
and new parameters.
|
466
|
+
:rtype: str
|
467
|
+
"""
|
246
468
|
# Parse the original URL
|
247
469
|
url_components = urlsplit(url)
|
248
470
|
|