voxcity 0.5.14__py3-none-any.whl → 0.5.15__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.

Potentially problematic release.


This version of voxcity might be problematic. Click here for more details.

voxcity/downloader/omt.py CHANGED
@@ -4,6 +4,21 @@ Module for downloading and processing building data from OpenMapTiles vector til
4
4
  This module provides functionality to download and process building footprint data from
5
5
  OpenMapTiles vector tile service. It handles downloading PBF tiles, extracting building
6
6
  geometries, and converting them to GeoJSON format with standardized properties.
7
+
8
+ Key Features:
9
+ - Downloads vector tiles from OpenMapTiles API
10
+ - Extracts building footprints and properties
11
+ - Converts coordinates from tile-local to WGS84
12
+ - Standardizes building height information
13
+ - Handles both Polygon and MultiPolygon geometries
14
+ - Separates inner and outer rings of building footprints
15
+
16
+ Dependencies:
17
+ - mercantile: For tile calculations and coordinate transformations
18
+ - mapbox_vector_tile: For decoding PBF vector tiles
19
+ - shapely: For geometry operations
20
+ - pyproj: For coordinate system transformations
21
+ - geopandas: For working with geospatial data
7
22
  """
8
23
 
9
24
  import mercantile
@@ -16,15 +31,33 @@ import json
16
31
  from pyproj import Transformer
17
32
  import json
18
33
  import geopandas as gpd
34
+
19
35
  def load_gdf_from_openmaptiles(rectangle_vertices, API_KEY):
20
36
  """Download and process building footprint data from OpenMapTiles vector tiles.
21
37
 
38
+ This function downloads vector tiles covering the specified area, extracts building
39
+ footprints, and converts them to a standardized format in a GeoDataFrame.
40
+
22
41
  Args:
23
- rectangle_vertices: List of (lon, lat) coordinates defining the bounding box
24
- API_KEY: OpenMapTiles API key for authentication
42
+ rectangle_vertices (list): List of (lon, lat) tuples defining the bounding box corners.
43
+ The coordinates should be in WGS84 (EPSG:4326) format.
44
+ API_KEY (str): OpenMapTiles API key for authentication. Must be valid for the v3 endpoint.
25
45
 
26
46
  Returns:
27
- geopandas.GeoDataFrame: GeoDataFrame containing building footprints with standardized properties
47
+ geopandas.GeoDataFrame: A GeoDataFrame containing building footprints with the following columns:
48
+ - geometry: Building footprint geometry in WGS84 coordinates
49
+ - height: Building height in meters
50
+ - min_height: Minimum height (e.g., for elevated structures) in meters
51
+ - confidence: Confidence score (-1.0 for OpenMapTiles data)
52
+ - is_inner: Boolean indicating if the polygon is an inner ring
53
+ - role: String indicating 'inner' or 'outer' ring
54
+ - id: Unique identifier for each building feature
55
+
56
+ Notes:
57
+ - Uses zoom level 15 for optimal detail vs data size balance
58
+ - Converts coordinates from Web Mercator (EPSG:3857) to WGS84 (EPSG:4326)
59
+ - Handles both Polygon and MultiPolygon geometries
60
+ - Separates complex building footprints into their constituent parts
28
61
  """
29
62
  # Extract longitudes and latitudes from vertices to find bounding box
30
63
  lons = [coord[0] for coord in rectangle_vertices]
@@ -124,11 +157,26 @@ def load_gdf_from_openmaptiles(rectangle_vertices, API_KEY):
124
157
  def get_height_from_properties(properties):
125
158
  """Extract building height from properties, using levels if height is not available.
126
159
 
160
+ This function implements a fallback strategy for determining building heights:
161
+ 1. First tries to use explicit render_height property
162
+ 2. If not available, estimates height from number of building levels
163
+ 3. Returns 0 if no valid height information is found
164
+
127
165
  Args:
128
- properties: Dictionary containing building properties
166
+ properties (dict): Dictionary containing building properties from OpenMapTiles.
167
+ Expected keys:
168
+ - render_height: Direct height specification in meters
169
+ - building:levels: Number of building floors/levels
129
170
 
130
171
  Returns:
131
- float: Building height in meters, defaults to 0 if no valid height found
172
+ float: Building height in meters. Values can be:
173
+ - Explicit height from render_height property
174
+ - Estimated height (levels * 5.0 meters per level)
175
+ - 0.0 if no valid height information is found
176
+
177
+ Notes:
178
+ - Assumes average floor height of 5 meters when estimating from levels
179
+ - Handles potential invalid values gracefully by returning 0
132
180
  """
133
181
  # First try explicit render_height property
134
182
  height = properties.get('render_height')
@@ -152,11 +200,30 @@ def get_height_from_properties(properties):
152
200
  def convert_geojson_format(features):
153
201
  """Convert building features to standardized format with height information.
154
202
 
203
+ This function processes raw OpenMapTiles building features into a standardized format,
204
+ handling complex geometries and adding consistent property attributes.
205
+
155
206
  Args:
156
- features: List of GeoJSON features containing building footprints
207
+ features (list): List of GeoJSON features containing building footprints.
208
+ Each feature should have:
209
+ - geometry: GeoJSON geometry (Polygon or MultiPolygon)
210
+ - properties: Dictionary of building properties
157
211
 
158
212
  Returns:
159
- list: List of standardized GeoJSON features with height properties
213
+ list: List of standardized GeoJSON features where:
214
+ - Complex MultiPolygons are split into individual Polygons
215
+ - Each Polygon ring (outer and inner) becomes a separate feature
216
+ - Properties are standardized to include:
217
+ - height: Building height in meters
218
+ - min_height: Minimum height in meters
219
+ - confidence: Set to -1.0 for OpenMapTiles data
220
+ - is_inner: Boolean flag for inner rings
221
+ - role: String indicating 'inner' or 'outer' ring
222
+
223
+ Notes:
224
+ - Preserves coordinate order as (longitude, latitude)
225
+ - Maintains topological relationships through is_inner and role properties
226
+ - Splits complex geometries for easier processing downstream
160
227
  """
161
228
  new_features = []
162
229
 
voxcity/downloader/osm.py CHANGED
@@ -4,6 +4,13 @@ Module for downloading and processing OpenStreetMap data.
4
4
  This module provides functionality to download and process building footprints, land cover,
5
5
  and other geographic features from OpenStreetMap. It handles downloading data via the Overpass API,
6
6
  processing the responses, and converting them to standardized GeoJSON format with proper properties.
7
+
8
+ The module includes functions for:
9
+ - Converting OSM JSON to GeoJSON format
10
+ - Processing building footprints with height information
11
+ - Handling land cover classifications
12
+ - Managing coordinate systems and projections
13
+ - Processing roads and other geographic features
7
14
  """
8
15
 
9
16
  import requests
@@ -174,7 +181,15 @@ def osm_json_to_geojson(osm_data):
174
181
  }
175
182
 
176
183
  def is_part_of_relation(way_id, osm_data):
177
- """Check if a way is part of any relation."""
184
+ """Check if a way is part of any relation in the OSM data.
185
+
186
+ Args:
187
+ way_id (int): The ID of the way to check
188
+ osm_data (dict): OSM JSON data containing elements
189
+
190
+ Returns:
191
+ bool: True if the way is part of a relation, False otherwise
192
+ """
178
193
  for element in osm_data['elements']:
179
194
  if element['type'] == 'relation' and 'members' in element:
180
195
  for member in element['members']:
@@ -183,7 +198,18 @@ def is_part_of_relation(way_id, osm_data):
183
198
  return False
184
199
 
185
200
  def is_way_polygon(way):
186
- """Determine if a way should be treated as a polygon."""
201
+ """Determine if a way should be treated as a polygon based on OSM tags and geometry.
202
+
203
+ A way is considered a polygon if:
204
+ 1. It forms a closed loop (first and last nodes are the same)
205
+ 2. It has tags indicating it represents an area (building, landuse, etc.)
206
+
207
+ Args:
208
+ way (dict): OSM way element with nodes and tags
209
+
210
+ Returns:
211
+ bool: True if the way should be treated as a polygon, False otherwise
212
+ """
187
213
  # Check if the way is closed (first and last nodes are the same)
188
214
  if 'nodes' in way and way['nodes'][0] == way['nodes'][-1]:
189
215
  # Check for tags that indicate this is an area
@@ -196,7 +222,16 @@ def is_way_polygon(way):
196
222
  return False
197
223
 
198
224
  def get_way_coords(way, nodes):
199
- """Get coordinates for a way."""
225
+ """Extract coordinates for a way from its node references.
226
+
227
+ Args:
228
+ way (dict): OSM way element containing node references
229
+ nodes (dict): Dictionary mapping node IDs to their coordinates
230
+
231
+ Returns:
232
+ list: List of coordinate pairs [(lon, lat), ...] for the way,
233
+ or empty list if any nodes are missing
234
+ """
200
235
  coords = []
201
236
  if 'nodes' not in way:
202
237
  return coords
@@ -211,16 +246,22 @@ def get_way_coords(way, nodes):
211
246
  return coords
212
247
 
213
248
  def create_rings_from_ways(way_ids, ways, nodes):
214
- """
215
- Create continuous rings by connecting ways.
249
+ """Create continuous rings by connecting ways that share nodes.
250
+
251
+ This function handles complex relations by:
252
+ 1. Connecting ways that share end nodes
253
+ 2. Handling reversed way directions
254
+ 3. Closing rings when possible
255
+ 4. Converting node references to coordinates
216
256
 
217
257
  Args:
218
- way_ids: List of way IDs that make up the ring(s)
219
- ways: Dictionary mapping way IDs to way elements
220
- nodes: Dictionary mapping node IDs to coordinates
258
+ way_ids (list): List of way IDs that make up the ring(s)
259
+ ways (dict): Dictionary mapping way IDs to way elements
260
+ nodes (dict): Dictionary mapping node IDs to coordinates
221
261
 
222
262
  Returns:
223
- List of rings, where each ring is a list of coordinates
263
+ list: List of rings, where each ring is a list of coordinate pairs [(lon, lat), ...]
264
+ forming a closed polygon with at least 4 points
224
265
  """
225
266
  if not way_ids:
226
267
  return []
@@ -332,11 +373,23 @@ def create_rings_from_ways(way_ids, ways, nodes):
332
373
  def load_gdf_from_openstreetmap(rectangle_vertices):
333
374
  """Download and process building footprint data from OpenStreetMap.
334
375
 
376
+ This function:
377
+ 1. Downloads building data using the Overpass API
378
+ 2. Processes complex relations and their members
379
+ 3. Extracts height information and other properties
380
+ 4. Converts features to a GeoDataFrame with standardized properties
381
+
335
382
  Args:
336
- rectangle_vertices: List of (lon, lat) coordinates defining the bounding box
383
+ rectangle_vertices (list): List of (lon, lat) coordinates defining the bounding box
337
384
 
338
385
  Returns:
339
- geopandas.GeoDataFrame: GeoDataFrame containing building footprints with standardized properties
386
+ geopandas.GeoDataFrame: GeoDataFrame containing building footprints with properties:
387
+ - geometry: Polygon or MultiPolygon
388
+ - height: Building height in meters
389
+ - levels: Number of building levels
390
+ - min_height: Minimum height (for elevated structures)
391
+ - building_type: Type of building
392
+ - And other OSM tags as properties
340
393
  """
341
394
  # Create a bounding box from the rectangle vertices
342
395
  min_lon = min(v[0] for v in rectangle_vertices)
@@ -533,13 +586,23 @@ def load_gdf_from_openstreetmap(rectangle_vertices):
533
586
  return gdf
534
587
 
535
588
  def convert_feature(feature):
536
- """Convert a GeoJSON feature to the desired format with height information.
589
+ """Convert a GeoJSON feature to a standardized format with height information.
590
+
591
+ This function:
592
+ 1. Handles both Polygon and MultiPolygon geometries
593
+ 2. Extracts and validates height information
594
+ 3. Ensures coordinate order consistency (lon, lat)
595
+ 4. Adds confidence scores for height estimates
537
596
 
538
597
  Args:
539
- feature (dict): Input GeoJSON feature
598
+ feature (dict): Input GeoJSON feature with geometry and properties
540
599
 
541
600
  Returns:
542
- dict: Converted feature with height and confidence values, or None if invalid
601
+ dict: Converted feature with:
602
+ - Standardized geometry (always Polygon)
603
+ - Height information in properties
604
+ - Confidence score for height values
605
+ Or None if the feature is invalid or not a polygon
543
606
  """
544
607
  new_feature = {}
545
608
  new_feature['type'] = 'Feature'
@@ -736,13 +799,21 @@ tag_osm_key_value_mapping = {
736
799
  }
737
800
 
738
801
  def get_classification(tags):
739
- """Determine the classification code and name for a feature based on its OSM tags.
802
+ """Determine the land cover/use classification based on OSM tags.
803
+
804
+ This function maps OSM tags to standardized land cover classes using:
805
+ 1. A hierarchical classification system (codes 0-13)
806
+ 2. Tag matching patterns for different feature types
807
+ 3. Special cases for roads, water bodies, etc.
740
808
 
741
809
  Args:
742
- tags (dict): Dictionary of OSM tags
810
+ tags (dict): Dictionary of OSM tags (key-value pairs)
743
811
 
744
812
  Returns:
745
- tuple: (classification_code, classification_name) or (None, None) if no match
813
+ tuple: (classification_code, classification_name) where:
814
+ - classification_code (int): Numeric code (0-13) for the land cover class
815
+ - classification_name (str): Human-readable name of the class
816
+ Or (None, None) if no matching classification is found
746
817
  """
747
818
  # Iterate through each classification code and its associated info
748
819
  for code, info in classification_mapping.items():
@@ -764,13 +835,18 @@ def get_classification(tags):
764
835
  return None, None
765
836
 
766
837
  def swap_coordinates(geom_mapping):
767
- """Swap coordinates from (lon, lat) to (lat, lon) order.
838
+ """Swap coordinate order in a GeoJSON geometry object.
839
+
840
+ This function:
841
+ 1. Handles nested coordinate structures (Polygons, MultiPolygons)
842
+ 2. Preserves the original coordinate order if already correct
843
+ 3. Works recursively for complex geometries
768
844
 
769
845
  Args:
770
- geom_mapping (dict): GeoJSON geometry object
846
+ geom_mapping (dict): GeoJSON geometry object with coordinates
771
847
 
772
848
  Returns:
773
- dict: Geometry with swapped coordinates
849
+ dict: Geometry with coordinates in the correct order (lon, lat)
774
850
  """
775
851
  coords = geom_mapping['coordinates']
776
852
 
@@ -786,13 +862,23 @@ def swap_coordinates(geom_mapping):
786
862
  return geom_mapping
787
863
 
788
864
  def load_land_cover_gdf_from_osm(rectangle_vertices_ori):
789
- """Load land cover data from OpenStreetMap within a given rectangular area.
865
+ """Load and classify land cover data from OpenStreetMap.
866
+
867
+ This function:
868
+ 1. Downloads land cover features using the Overpass API
869
+ 2. Classifies features based on OSM tags
870
+ 3. Handles special cases like roads with width information
871
+ 4. Projects geometries for accurate buffering
872
+ 5. Creates a standardized GeoDataFrame with classifications
790
873
 
791
874
  Args:
792
- rectangle_vertices_ori (list): List of (lon, lat) coordinates defining the rectangle
875
+ rectangle_vertices_ori (list): List of (lon, lat) coordinates defining the area
793
876
 
794
877
  Returns:
795
- GeoDataFrame: GeoDataFrame containing land cover classifications
878
+ geopandas.GeoDataFrame: GeoDataFrame with:
879
+ - geometry: Polygon or MultiPolygon features
880
+ - class: Land cover classification name
881
+ - Additional properties from OSM tags
796
882
  """
797
883
  # Close the rectangle polygon by adding first vertex at the end
798
884
  rectangle_vertices = rectangle_vertices_ori.copy()
@@ -3,6 +3,18 @@ Module for downloading and processing building footprint data from Overture Maps
3
3
 
4
4
  This module provides functionality to download and process building footprints,
5
5
  handling the conversion of Overture Maps data to GeoJSON format with standardized properties.
6
+
7
+ The module includes functions for:
8
+ - Converting data types between numpy and Python native types
9
+ - Processing and validating building footprint data
10
+ - Handling geometric operations and coordinate transformations
11
+ - Combining and standardizing building data from multiple sources
12
+
13
+ Main workflow:
14
+ 1. Download building data from Overture Maps using a bounding box
15
+ 2. Process and standardize the data format
16
+ 3. Combine building and building part data
17
+ 4. Add unique identifiers and standardize properties
6
18
  """
7
19
 
8
20
  from overturemaps import core
@@ -15,11 +27,26 @@ def convert_numpy_to_python(obj):
15
27
  """
16
28
  Recursively convert numpy types to native Python types.
17
29
 
30
+ This function handles various numpy data types and complex nested structures,
31
+ ensuring all data is converted to Python native types for JSON serialization.
32
+
18
33
  Args:
19
- obj: Object to convert, can be dict, list, tuple, numpy type or other
34
+ obj: Object to convert, can be:
35
+ - dict: Dictionary with potentially nested numpy types
36
+ - list/tuple: Sequence with potentially nested numpy types
37
+ - numpy.ndarray: Numpy array to be converted to list
38
+ - numpy.integer/numpy.floating: Numpy numeric types
39
+ - native Python types (bool, str, int, float)
40
+ - None values
20
41
 
21
42
  Returns:
22
- Converted object with numpy types replaced by native Python types
43
+ object: Converted object with all numpy types replaced by native Python types
44
+
45
+ Examples:
46
+ >>> convert_numpy_to_python(np.int64(42))
47
+ 42
48
+ >>> convert_numpy_to_python({'a': np.array([1, 2, 3])})
49
+ {'a': [1, 2, 3]}
23
50
  """
24
51
  # Handle dictionary case - recursively convert all values
25
52
  if isinstance(obj, dict):
@@ -50,11 +77,21 @@ def is_valid_value(value):
50
77
  """
51
78
  Check if a value is valid (not NA/null) and handle array-like objects.
52
79
 
80
+ This function is used to validate data before processing, ensuring that
81
+ null/NA values are handled appropriately while preserving array-like structures.
82
+
53
83
  Args:
54
- value: Value to check
84
+ value: Value to check, can be:
85
+ - numpy.ndarray: Always considered valid
86
+ - list: Always considered valid
87
+ - scalar values: Checked for NA/null status
55
88
 
56
89
  Returns:
57
90
  bool: True if value is valid (not NA/null or is array-like), False otherwise
91
+
92
+ Note:
93
+ Arrays and lists are always considered valid since they may contain
94
+ valid data that needs to be processed individually.
58
95
  """
59
96
  # Arrays and lists are always considered valid since they may contain valid data
60
97
  if isinstance(value, (np.ndarray, list)):
@@ -65,14 +102,32 @@ def is_valid_value(value):
65
102
  def convert_gdf_to_geojson(gdf):
66
103
  """
67
104
  Convert GeoDataFrame to GeoJSON format with coordinates in (lon, lat) order.
68
- Extracts all columns as properties except for 'geometry' and 'bbox'.
69
- Sets height and min_height to 0 if not present and handles arrays.
105
+
106
+ This function processes a GeoDataFrame containing building data and converts it
107
+ to a standardized GeoJSON format. It handles special cases for height values
108
+ and ensures all properties are properly converted to JSON-serializable types.
70
109
 
71
110
  Args:
72
- gdf (GeoDataFrame): Input GeoDataFrame to convert
111
+ gdf (GeoDataFrame): Input GeoDataFrame containing building data with columns:
112
+ - geometry: Shapely geometry objects
113
+ - height: Building height (optional)
114
+ - min_height: Minimum building height (optional)
115
+ - Additional property columns
73
116
 
74
117
  Returns:
75
- list: List of GeoJSON feature dictionaries
118
+ list: List of GeoJSON feature dictionaries, each containing:
119
+ - type: Always "Feature"
120
+ - properties: Dictionary of building properties including:
121
+ - height: Building height (defaults to 0.0)
122
+ - min_height: Minimum height (defaults to 0.0)
123
+ - id: Sequential unique identifier
124
+ - All other columns from input GeoDataFrame
125
+ - geometry: GeoJSON geometry object
126
+
127
+ Note:
128
+ - Height values default to 0.0 if missing or invalid
129
+ - All numpy types are converted to native Python types
130
+ - Sequential IDs are assigned starting from 1
76
131
  """
77
132
  features = []
78
133
  id_count = 1
@@ -117,14 +172,22 @@ def convert_gdf_to_geojson(gdf):
117
172
 
118
173
  def rectangle_to_bbox(vertices):
119
174
  """
120
- Convert rectangle vertices in (lon, lat) format to a GeoDataFrame bbox
121
- with Shapely box geometry in (minx, miny, maxx, maxy) format
175
+ Convert rectangle vertices in (lon, lat) format to a bounding box.
176
+
177
+ This function takes a list of coordinate pairs defining a rectangle and
178
+ converts them to a bounding box format required by the Overture Maps API.
122
179
 
123
180
  Args:
124
181
  vertices (list): List of tuples containing (lon, lat) coordinates
182
+ defining the corners of a rectangle
125
183
 
126
184
  Returns:
127
- tuple: Bounding box coordinates (min_lon, min_lat, max_lon, max_lat)
185
+ tuple: Bounding box coordinates in format (min_lon, min_lat, max_lon, max_lat)
186
+ suitable for use with Overture Maps API
187
+
188
+ Note:
189
+ The function calculates the minimum and maximum coordinates to ensure
190
+ the bounding box encompasses all provided vertices.
128
191
  """
129
192
  # Extract lon, lat values from vertices
130
193
  lons = [vertex[0] for vertex in vertices]
@@ -141,14 +204,26 @@ def rectangle_to_bbox(vertices):
141
204
 
142
205
  def join_gdfs_vertically(gdf1, gdf2):
143
206
  """
144
- Join two GeoDataFrames vertically, handling different columns.
207
+ Join two GeoDataFrames vertically, handling different column structures.
208
+
209
+ This function combines two GeoDataFrames that may have different columns,
210
+ ensuring all columns from both datasets are preserved in the output.
211
+ It provides diagnostic information about the combining process.
145
212
 
146
213
  Args:
147
- gdf1 (GeoDataFrame): First GeoDataFrame
148
- gdf2 (GeoDataFrame): Second GeoDataFrame
214
+ gdf1 (GeoDataFrame): First GeoDataFrame (e.g., buildings)
215
+ gdf2 (GeoDataFrame): Second GeoDataFrame (e.g., building parts)
149
216
 
150
217
  Returns:
151
- GeoDataFrame: Combined GeoDataFrame with all columns from both inputs
218
+ GeoDataFrame: Combined GeoDataFrame containing:
219
+ - All rows from both input GeoDataFrames
220
+ - All columns from both inputs (filled with None where missing)
221
+ - Preserved geometry column
222
+
223
+ Note:
224
+ - Prints diagnostic information about column differences
225
+ - Handles missing columns by filling with None values
226
+ - Preserves the geometry column for spatial operations
152
227
  """
153
228
  # Print diagnostic information about column differences
154
229
  print("GDF1 columns:", list(gdf1.columns))
@@ -183,26 +258,36 @@ def load_gdf_from_overture(rectangle_vertices):
183
258
  """
184
259
  Download and process building footprint data from Overture Maps.
185
260
 
261
+ This function serves as the main entry point for downloading building data.
262
+ It handles the complete workflow of downloading both building and building
263
+ part data, combining them, and preparing them for further processing.
264
+
186
265
  Args:
187
- rectangle_vertices (list): List of (lon, lat) coordinates defining the bounding box
266
+ rectangle_vertices (list): List of (lon, lat) coordinates defining
267
+ the bounding box for data download
188
268
 
189
269
  Returns:
190
- list: List of GeoJSON features containing building footprints with standardized properties
270
+ GeoDataFrame: Combined dataset containing:
271
+ - Building and building part geometries
272
+ - Standardized properties
273
+ - Sequential numeric IDs
274
+
275
+ Note:
276
+ - Downloads both building and building_part data from Overture Maps
277
+ - Combines the datasets while preserving all properties
278
+ - Assigns sequential IDs based on the final dataset index
191
279
  """
192
- # Convert vertices to bounding box format required by Overture Maps
280
+ # Convert input vertices to Overture Maps API bounding box format
193
281
  bbox = rectangle_to_bbox(rectangle_vertices)
194
282
 
195
- # Download building and building part data from Overture Maps
283
+ # Download primary building footprints and additional building part data
196
284
  building_gdf = core.geodataframe("building", bbox=bbox)
197
285
  building_part_gdf = core.geodataframe("building_part", bbox=bbox)
198
286
 
199
- # Combine building and building part data into single dataset
287
+ # Combine both datasets into a single comprehensive building dataset
200
288
  joined_building_gdf = join_gdfs_vertically(building_gdf, building_part_gdf)
201
289
 
202
- # # Convert combined dataset to GeoJSON format
203
- # geojson_features = convert_gdf_to_geojson(joined_building_gdf)
204
-
205
- # Replace id column with index numbers
290
+ # Assign sequential IDs based on the final dataset index
206
291
  joined_building_gdf['id'] = joined_building_gdf.index
207
292
 
208
293
  return joined_building_gdf
@@ -1,21 +1,36 @@
1
+ # Utility functions for downloading files from various sources
1
2
  import requests
2
3
  import gdown
3
4
 
4
5
  def download_file(url, filename):
5
6
  """Download a file from a URL and save it locally.
6
7
 
8
+ This function uses the requests library to download a file from any publicly
9
+ accessible URL and save it to the local filesystem. It handles the download
10
+ process and provides feedback on the operation's success or failure.
11
+
7
12
  Args:
8
- url (str): URL of the file to download
9
- filename (str): Local path where the downloaded file will be saved
13
+ url (str): URL of the file to download. Must be a valid, accessible URL.
14
+ filename (str): Local path where the downloaded file will be saved.
15
+ Include the full path and filename with extension.
10
16
 
11
17
  Returns:
12
18
  None
13
19
 
14
20
  Prints:
15
- Success or failure message with status code
21
+ - Success message with filename if download is successful (status code 200)
22
+ - Error message with status code if download fails
23
+
24
+ Example:
25
+ >>> download_file('https://example.com/file.pdf', 'local_file.pdf')
26
+ File downloaded successfully and saved as local_file.pdf
16
27
  """
28
+ # Attempt to download the file from the provided URL
17
29
  response = requests.get(url)
30
+
31
+ # Check if the download was successful (HTTP status code 200)
18
32
  if response.status_code == 200:
33
+ # Open the local file in binary write mode and save the content
19
34
  with open(filename, 'wb') as file:
20
35
  file.write(response.content)
21
36
  print(f"File downloaded successfully and saved as {filename}")
@@ -25,18 +40,33 @@ def download_file(url, filename):
25
40
  def download_file_google_drive(file_id, output_path):
26
41
  """Download a file from Google Drive using its file ID.
27
42
 
43
+ This function specifically handles downloads from Google Drive using the gdown
44
+ library, which is designed to bypass Google Drive's download restrictions.
45
+ It's useful for downloading large files or files that require authentication.
46
+
28
47
  Args:
29
- file_id (str): Google Drive file ID
30
- output_path (str): Local path where the downloaded file will be saved
48
+ file_id (str): Google Drive file ID. This is the unique identifier in the
49
+ sharing URL after '/d/' or 'id='.
50
+ output_path (str): Local path where the downloaded file will be saved.
51
+ Include the full path and filename with extension.
31
52
 
32
53
  Returns:
33
- bool: True if download successful, False otherwise
54
+ bool: True if download was successful, False if any error occurred
34
55
 
35
56
  Prints:
36
- Error message if download fails
57
+ Error message with exception details if download fails
58
+
59
+ Example:
60
+ >>> success = download_file_google_drive('1234abcd...', 'downloaded_file.zip')
61
+ >>> if success:
62
+ >>> print("Download completed successfully")
37
63
  """
64
+ # Construct the direct download URL using the file ID
38
65
  url = f"https://drive.google.com/uc?id={file_id}"
66
+
39
67
  try:
68
+ # Use gdown to handle the Google Drive download
69
+ # quiet=False enables download progress display
40
70
  gdown.download(url, output_path, quiet=False)
41
71
  return True
42
72
  except Exception as e: