voxcity 0.7.0__py3-none-any.whl → 1.0.2__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.
Files changed (42) hide show
  1. voxcity/__init__.py +14 -14
  2. voxcity/exporter/__init__.py +12 -12
  3. voxcity/exporter/cityles.py +633 -633
  4. voxcity/exporter/envimet.py +733 -728
  5. voxcity/exporter/magicavoxel.py +333 -333
  6. voxcity/exporter/netcdf.py +238 -238
  7. voxcity/exporter/obj.py +1480 -1480
  8. voxcity/generator/__init__.py +47 -44
  9. voxcity/generator/api.py +721 -675
  10. voxcity/generator/grids.py +381 -379
  11. voxcity/generator/io.py +94 -94
  12. voxcity/generator/pipeline.py +282 -282
  13. voxcity/generator/update.py +429 -0
  14. voxcity/generator/voxelizer.py +18 -6
  15. voxcity/geoprocessor/__init__.py +75 -75
  16. voxcity/geoprocessor/draw.py +1488 -1219
  17. voxcity/geoprocessor/merge_utils.py +91 -91
  18. voxcity/geoprocessor/mesh.py +806 -806
  19. voxcity/geoprocessor/network.py +708 -708
  20. voxcity/geoprocessor/raster/buildings.py +435 -428
  21. voxcity/geoprocessor/raster/export.py +93 -93
  22. voxcity/geoprocessor/raster/landcover.py +5 -2
  23. voxcity/geoprocessor/utils.py +824 -824
  24. voxcity/models.py +113 -113
  25. voxcity/simulator/solar/__init__.py +66 -43
  26. voxcity/simulator/solar/integration.py +336 -336
  27. voxcity/simulator/solar/sky.py +668 -0
  28. voxcity/simulator/solar/temporal.py +792 -434
  29. voxcity/utils/__init__.py +11 -0
  30. voxcity/utils/classes.py +194 -0
  31. voxcity/utils/lc.py +80 -39
  32. voxcity/utils/shape.py +230 -0
  33. voxcity/visualizer/__init__.py +24 -24
  34. voxcity/visualizer/builder.py +43 -43
  35. voxcity/visualizer/grids.py +141 -141
  36. voxcity/visualizer/maps.py +187 -187
  37. voxcity/visualizer/renderer.py +1145 -928
  38. {voxcity-0.7.0.dist-info → voxcity-1.0.2.dist-info}/METADATA +90 -49
  39. {voxcity-0.7.0.dist-info → voxcity-1.0.2.dist-info}/RECORD +42 -38
  40. {voxcity-0.7.0.dist-info → voxcity-1.0.2.dist-info}/WHEEL +0 -0
  41. {voxcity-0.7.0.dist-info → voxcity-1.0.2.dist-info}/licenses/AUTHORS.rst +0 -0
  42. {voxcity-0.7.0.dist-info → voxcity-1.0.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,429 @@
1
+ """Functions for updating VoxCity objects with new grid data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, Union
6
+ import numpy as np
7
+
8
+ from ..models import (
9
+ GridMetadata,
10
+ BuildingGrid,
11
+ LandCoverGrid,
12
+ DemGrid,
13
+ VoxelGrid,
14
+ CanopyGrid,
15
+ VoxCity,
16
+ )
17
+ from .voxelizer import Voxelizer
18
+
19
+
20
+ def update_voxcity(
21
+ city: VoxCity,
22
+ *,
23
+ buildings: Optional[BuildingGrid] = None,
24
+ building_heights: Optional[np.ndarray] = None,
25
+ building_min_heights: Optional[np.ndarray] = None,
26
+ building_ids: Optional[np.ndarray] = None,
27
+ land_cover: Optional[Union[LandCoverGrid, np.ndarray]] = None,
28
+ dem: Optional[Union[DemGrid, np.ndarray]] = None,
29
+ tree_canopy: Optional[Union[CanopyGrid, np.ndarray]] = None,
30
+ canopy_top: Optional[np.ndarray] = None,
31
+ canopy_bottom: Optional[np.ndarray] = None,
32
+ building_gdf=None,
33
+ tree_gdf=None,
34
+ tree_gdf_mode: str = "replace",
35
+ land_cover_source: Optional[str] = None,
36
+ trunk_height_ratio: Optional[float] = None,
37
+ voxel_dtype=None,
38
+ max_voxel_ram_mb: Optional[float] = None,
39
+ inplace: bool = False,
40
+ ) -> VoxCity:
41
+ """
42
+ Update a VoxCity object with new grid data and regenerate the VoxelGrid.
43
+
44
+ This function allows partial updates - only the grids you provide will be
45
+ updated, while the rest will be taken from the existing VoxCity object.
46
+ The VoxelGrid is always regenerated from the (updated) component grids.
47
+
48
+ Parameters
49
+ ----------
50
+ city : VoxCity
51
+ The existing VoxCity object to update.
52
+
53
+ buildings : BuildingGrid, optional
54
+ Complete replacement for the building grid. If provided, takes precedence
55
+ over individual building_heights/building_min_heights/building_ids.
56
+
57
+ building_heights : np.ndarray, optional
58
+ 2D array of building heights. If buildings is not provided, this updates
59
+ only the heights while keeping existing min_heights and ids.
60
+
61
+ building_min_heights : np.ndarray, optional
62
+ 2D object-dtype array of lists containing [min_height, max_height] pairs
63
+ for each building segment per cell.
64
+
65
+ building_ids : np.ndarray, optional
66
+ 2D array of building IDs per cell.
67
+
68
+ land_cover : LandCoverGrid or np.ndarray, optional
69
+ New land cover data. Can be a LandCoverGrid or a raw numpy array.
70
+
71
+ dem : DemGrid or np.ndarray, optional
72
+ New DEM/elevation data. Can be a DemGrid or a raw numpy array.
73
+
74
+ tree_canopy : CanopyGrid or np.ndarray, optional
75
+ New tree canopy data. Can be a CanopyGrid (with top/bottom) or a raw
76
+ numpy array (interpreted as canopy top heights).
77
+
78
+ canopy_top : np.ndarray, optional
79
+ 2D array of tree canopy top heights. Takes precedence over tree_canopy
80
+ for top heights if both are provided.
81
+
82
+ canopy_bottom : np.ndarray, optional
83
+ 2D array of tree canopy bottom heights (crown base).
84
+
85
+ building_gdf : GeoDataFrame, optional
86
+ Updated building GeoDataFrame. If provided without building grids
87
+ (building_heights, building_min_heights, building_ids), the function
88
+ will automatically generate the building grids from the GeoDataFrame
89
+ using create_building_height_grid_from_gdf_polygon. The GeoDataFrame
90
+ is also stored in city.extras['building_gdf'].
91
+
92
+ tree_gdf : GeoDataFrame, optional
93
+ Updated tree GeoDataFrame. If provided without tree canopy data
94
+ (tree_canopy, canopy_top, canopy_bottom), the function will
95
+ automatically generate the canopy grids from the GeoDataFrame
96
+ using create_canopy_grids_from_tree_gdf. The GeoDataFrame must
97
+ contain 'top_height', 'bottom_height', 'crown_diameter', and
98
+ 'geometry' columns. The GeoDataFrame is stored in city.extras['tree_gdf'].
99
+
100
+ tree_gdf_mode : str, default "replace"
101
+ How to combine tree_gdf with existing canopy data. Options:
102
+ - "replace": Replace the existing canopy grids with new ones from tree_gdf.
103
+ - "add": Merge the tree_gdf grids with existing canopy grids by taking
104
+ the maximum height at each cell (preserves existing trees).
105
+
106
+ land_cover_source : str, optional
107
+ The land cover source name for proper voxelization. If not provided,
108
+ attempts to use the source from city.extras or defaults to 'OpenStreetMap'.
109
+
110
+ trunk_height_ratio : float, optional
111
+ Ratio of trunk height to total tree height for canopy bottom calculation.
112
+ Default is approximately 0.588 (11.76/19.98).
113
+
114
+ voxel_dtype : dtype, optional
115
+ NumPy dtype for the voxel grid. Defaults to np.int8.
116
+
117
+ max_voxel_ram_mb : float, optional
118
+ Maximum RAM in MB for voxel grid allocation. Raises MemoryError if exceeded.
119
+
120
+ inplace : bool, default False
121
+ If True, modifies the input city object directly and returns it.
122
+ If False, creates and returns a new VoxCity object.
123
+
124
+ Returns
125
+ -------
126
+ VoxCity
127
+ The updated VoxCity object with regenerated VoxelGrid.
128
+
129
+ Examples
130
+ --------
131
+ Update building heights and regenerate voxels:
132
+
133
+ >>> import numpy as np
134
+ >>> new_heights = city.buildings.heights.copy()
135
+ >>> new_heights[10:20, 10:20] = 50.0 # Increase height in a region
136
+ >>> updated = update_voxcity(city, building_heights=new_heights)
137
+
138
+ Update with a complete new BuildingGrid:
139
+
140
+ >>> from voxcity.models import BuildingGrid
141
+ >>> new_buildings = BuildingGrid(heights=..., min_heights=..., ids=..., meta=city.buildings.meta)
142
+ >>> updated = update_voxcity(city, buildings=new_buildings)
143
+
144
+ Update land cover and DEM together:
145
+
146
+ >>> updated = update_voxcity(city, land_cover=new_lc_array, dem=new_dem_array)
147
+
148
+ Update buildings from GeoDataFrame (automatic grid generation):
149
+
150
+ >>> updated = update_voxcity(city, building_gdf=updated_building_gdf)
151
+
152
+ Update trees from GeoDataFrame (replace existing canopy):
153
+
154
+ >>> updated = update_voxcity(city, tree_gdf=updated_tree_gdf)
155
+
156
+ Add trees from GeoDataFrame to existing canopy:
157
+
158
+ >>> updated = update_voxcity(city, tree_gdf=new_tree_gdf, tree_gdf_mode="add")
159
+ """
160
+ # Resolve metadata from existing city
161
+ meta = city.buildings.meta
162
+ meshsize = meta.meshsize
163
+
164
+ # --- Auto-generate building grids from GeoDataFrame if provided ---
165
+ if building_gdf is not None and buildings is None and building_heights is None:
166
+ # Auto-generate building grids from the GeoDataFrame
167
+ from ..geoprocessor.raster import create_building_height_grid_from_gdf_polygon
168
+
169
+ rectangle_vertices = city.extras.get("rectangle_vertices")
170
+ if rectangle_vertices is None:
171
+ raise ValueError(
172
+ "Cannot auto-generate building grids: 'rectangle_vertices' not found in city.extras. "
173
+ "Provide building_heights, building_min_heights, and building_ids explicitly."
174
+ )
175
+
176
+ building_heights, building_min_heights, building_ids, _ = (
177
+ create_building_height_grid_from_gdf_polygon(
178
+ building_gdf,
179
+ meshsize,
180
+ rectangle_vertices,
181
+ )
182
+ )
183
+
184
+ # --- Auto-generate canopy grids from tree GeoDataFrame if provided ---
185
+ if tree_gdf is not None and tree_canopy is None and canopy_top is None:
186
+ # Validate tree_gdf_mode
187
+ if tree_gdf_mode not in ("replace", "add"):
188
+ raise ValueError(
189
+ f"Invalid tree_gdf_mode '{tree_gdf_mode}'. Must be 'replace' or 'add'."
190
+ )
191
+
192
+ # Auto-generate canopy grids from the tree GeoDataFrame
193
+ from ..geoprocessor.raster import create_canopy_grids_from_tree_gdf
194
+
195
+ rectangle_vertices = city.extras.get("rectangle_vertices")
196
+ if rectangle_vertices is None:
197
+ raise ValueError(
198
+ "Cannot auto-generate canopy grids: 'rectangle_vertices' not found in city.extras. "
199
+ "Provide canopy_top and canopy_bottom explicitly."
200
+ )
201
+
202
+ new_canopy_top, new_canopy_bottom = create_canopy_grids_from_tree_gdf(
203
+ tree_gdf,
204
+ meshsize,
205
+ rectangle_vertices,
206
+ )
207
+
208
+ if tree_gdf_mode == "add":
209
+ # Merge with existing canopy by taking maximum values
210
+ existing_top = city.tree_canopy.top
211
+ existing_bottom = city.tree_canopy.bottom
212
+ if existing_top is not None:
213
+ canopy_top = np.maximum(existing_top, new_canopy_top)
214
+ else:
215
+ canopy_top = new_canopy_top
216
+ if existing_bottom is not None:
217
+ canopy_bottom = np.maximum(existing_bottom, new_canopy_bottom)
218
+ else:
219
+ canopy_bottom = new_canopy_bottom
220
+ else:
221
+ # Replace mode: use new canopy grids directly
222
+ canopy_top = new_canopy_top
223
+ canopy_bottom = new_canopy_bottom
224
+
225
+ # --- Resolve building data ---
226
+ if buildings is not None:
227
+ # Use provided BuildingGrid directly
228
+ final_building_heights = buildings.heights
229
+ final_building_min_heights = buildings.min_heights
230
+ final_building_ids = buildings.ids
231
+ final_building_meta = buildings.meta
232
+ else:
233
+ # Use individual arrays or fall back to existing
234
+ final_building_heights = (
235
+ building_heights if building_heights is not None else city.buildings.heights
236
+ )
237
+ final_building_min_heights = (
238
+ building_min_heights
239
+ if building_min_heights is not None
240
+ else city.buildings.min_heights
241
+ )
242
+ final_building_ids = (
243
+ building_ids if building_ids is not None else city.buildings.ids
244
+ )
245
+ final_building_meta = meta
246
+
247
+ # --- Resolve land cover data ---
248
+ if land_cover is not None:
249
+ if isinstance(land_cover, LandCoverGrid):
250
+ final_land_cover = land_cover.classes
251
+ else:
252
+ final_land_cover = land_cover
253
+ else:
254
+ final_land_cover = city.land_cover.classes
255
+
256
+ # --- Resolve DEM data ---
257
+ if dem is not None:
258
+ if isinstance(dem, DemGrid):
259
+ final_dem = dem.elevation
260
+ else:
261
+ final_dem = dem
262
+ else:
263
+ final_dem = city.dem.elevation
264
+
265
+ # --- Resolve canopy data ---
266
+ # Priority: canopy_top/canopy_bottom > tree_canopy > existing
267
+ if canopy_top is not None:
268
+ final_canopy_top = canopy_top
269
+ elif tree_canopy is not None:
270
+ if isinstance(tree_canopy, CanopyGrid):
271
+ final_canopy_top = tree_canopy.top
272
+ else:
273
+ final_canopy_top = tree_canopy
274
+ else:
275
+ final_canopy_top = city.tree_canopy.top
276
+
277
+ if canopy_bottom is not None:
278
+ final_canopy_bottom = canopy_bottom
279
+ elif tree_canopy is not None and isinstance(tree_canopy, CanopyGrid):
280
+ final_canopy_bottom = tree_canopy.bottom
281
+ else:
282
+ final_canopy_bottom = city.tree_canopy.bottom
283
+
284
+ # --- Determine land cover source ---
285
+ if land_cover_source is None:
286
+ # Try to get from extras
287
+ land_cover_source = city.extras.get("land_cover_source")
288
+ if land_cover_source is None:
289
+ selected = city.extras.get("selected_sources", {})
290
+ land_cover_source = selected.get("land_cover_source", "OpenStreetMap")
291
+
292
+ # --- Build updated extras ---
293
+ new_extras = dict(city.extras)
294
+ if building_gdf is not None:
295
+ new_extras["building_gdf"] = building_gdf
296
+ if tree_gdf is not None:
297
+ new_extras["tree_gdf"] = tree_gdf
298
+ new_extras["canopy_top"] = final_canopy_top
299
+ new_extras["canopy_bottom"] = final_canopy_bottom
300
+
301
+ # --- Shape validation ---
302
+ expected_shape = final_land_cover.shape
303
+ shapes = {
304
+ "building_heights": final_building_heights.shape,
305
+ "building_min_heights": final_building_min_heights.shape,
306
+ "building_ids": final_building_ids.shape,
307
+ "land_cover": final_land_cover.shape,
308
+ "dem": final_dem.shape,
309
+ "canopy_top": final_canopy_top.shape if final_canopy_top is not None else None,
310
+ }
311
+
312
+ mismatched = {k: v for k, v in shapes.items() if v is not None and v != expected_shape}
313
+ if mismatched:
314
+ raise ValueError(
315
+ f"Grid shape mismatch! Expected {expected_shape}, but got: {mismatched}. "
316
+ f"All grids must have the same shape."
317
+ )
318
+
319
+ # --- Create Voxelizer and regenerate voxel grid ---
320
+ _voxel_dtype = voxel_dtype if voxel_dtype is not None else np.int8
321
+ voxelizer = Voxelizer(
322
+ voxel_size=meshsize,
323
+ land_cover_source=land_cover_source,
324
+ trunk_height_ratio=trunk_height_ratio,
325
+ voxel_dtype=_voxel_dtype,
326
+ max_voxel_ram_mb=max_voxel_ram_mb,
327
+ )
328
+
329
+ new_voxel_classes = voxelizer.generate_combined(
330
+ building_height_grid_ori=final_building_heights,
331
+ building_min_height_grid_ori=final_building_min_heights,
332
+ building_id_grid_ori=final_building_ids,
333
+ land_cover_grid_ori=final_land_cover,
334
+ dem_grid_ori=final_dem,
335
+ tree_grid_ori=final_canopy_top,
336
+ canopy_bottom_height_grid_ori=final_canopy_bottom,
337
+ )
338
+
339
+ # --- Assemble result ---
340
+ new_voxels = VoxelGrid(classes=new_voxel_classes, meta=meta)
341
+ new_buildings = BuildingGrid(
342
+ heights=final_building_heights,
343
+ min_heights=final_building_min_heights,
344
+ ids=final_building_ids,
345
+ meta=final_building_meta,
346
+ )
347
+ new_land_cover = LandCoverGrid(classes=final_land_cover, meta=meta)
348
+ new_dem = DemGrid(elevation=final_dem, meta=meta)
349
+ new_canopy = CanopyGrid(
350
+ top=final_canopy_top,
351
+ bottom=final_canopy_bottom,
352
+ meta=meta,
353
+ )
354
+
355
+ if inplace:
356
+ city.voxels = new_voxels
357
+ city.buildings = new_buildings
358
+ city.land_cover = new_land_cover
359
+ city.dem = new_dem
360
+ city.tree_canopy = new_canopy
361
+ city.extras = new_extras
362
+ return city
363
+ else:
364
+ return VoxCity(
365
+ voxels=new_voxels,
366
+ buildings=new_buildings,
367
+ land_cover=new_land_cover,
368
+ dem=new_dem,
369
+ tree_canopy=new_canopy,
370
+ extras=new_extras,
371
+ )
372
+
373
+
374
+ def regenerate_voxels(
375
+ city: VoxCity,
376
+ *,
377
+ land_cover_source: Optional[str] = None,
378
+ trunk_height_ratio: Optional[float] = None,
379
+ voxel_dtype=None,
380
+ max_voxel_ram_mb: Optional[float] = None,
381
+ inplace: bool = False,
382
+ ) -> VoxCity:
383
+ """
384
+ Regenerate only the VoxelGrid from existing component grids.
385
+
386
+ This is a convenience function for when you've modified the grids in-place
387
+ and need to regenerate the voxels without passing all parameters.
388
+
389
+ Parameters
390
+ ----------
391
+ city : VoxCity
392
+ The VoxCity object whose voxels should be regenerated.
393
+
394
+ land_cover_source : str, optional
395
+ Land cover source for voxelization. Defaults to source from extras.
396
+
397
+ trunk_height_ratio : float, optional
398
+ Trunk height ratio for tree canopy calculation.
399
+
400
+ voxel_dtype : dtype, optional
401
+ NumPy dtype for voxel grid.
402
+
403
+ max_voxel_ram_mb : float, optional
404
+ Maximum RAM in MB for voxel allocation.
405
+
406
+ inplace : bool, default False
407
+ If True, modifies city directly; otherwise returns a new object.
408
+
409
+ Returns
410
+ -------
411
+ VoxCity
412
+ The VoxCity object with regenerated VoxelGrid.
413
+
414
+ Examples
415
+ --------
416
+ >>> # Modify building heights in place
417
+ >>> city.buildings.heights[50:60, 50:60] = 100.0
418
+ >>> # Regenerate voxels to reflect the change
419
+ >>> city = regenerate_voxels(city, inplace=True)
420
+ """
421
+ return update_voxcity(
422
+ city,
423
+ land_cover_source=land_cover_source,
424
+ trunk_height_ratio=trunk_height_ratio,
425
+ voxel_dtype=voxel_dtype,
426
+ max_voxel_ram_mb=max_voxel_ram_mb,
427
+ inplace=inplace,
428
+ )
429
+
@@ -22,6 +22,7 @@ from ..geoprocessor.raster import (
22
22
  )
23
23
  from ..utils.orientation import ensure_orientation, ORIENTATION_NORTH_UP, ORIENTATION_SOUTH_UP
24
24
  from ..utils.lc import convert_land_cover
25
+ from ..utils.classes import VOXEL_CODE_DESCRIPTIONS, LAND_COVER_DESCRIPTIONS
25
26
 
26
27
 
27
28
  # -----------------------------
@@ -155,7 +156,7 @@ class Voxelizer:
155
156
 
156
157
  def _convert_land_cover(self, land_cover_grid_ori: np.ndarray) -> np.ndarray:
157
158
  if self.land_cover_source == 'OpenStreetMap':
158
- return land_cover_grid_ori
159
+ return land_cover_grid_ori + 1 # Shift to 1-based indices
159
160
  return convert_land_cover(land_cover_grid_ori, land_cover_source=self.land_cover_source)
160
161
 
161
162
  def generate_combined(
@@ -170,6 +171,11 @@ class Voxelizer:
170
171
  **kwargs,
171
172
  ) -> np.ndarray:
172
173
  print("Generating 3D voxel data")
174
+
175
+ # Print class definitions if requested
176
+ if kwargs.get("print_class_info", True):
177
+ print(VOXEL_CODE_DESCRIPTIONS)
178
+ print(LAND_COVER_DESCRIPTIONS)
173
179
 
174
180
  land_cover_grid_converted = self._convert_land_cover(land_cover_grid_ori)
175
181
 
@@ -192,7 +198,7 @@ class Voxelizer:
192
198
  land_cover_grid_converted.copy(),
193
199
  ORIENTATION_NORTH_UP,
194
200
  ORIENTATION_SOUTH_UP,
195
- ) + 1
201
+ )
196
202
  dem_grid = ensure_orientation(
197
203
  dem_grid_ori.copy(),
198
204
  ORIENTATION_NORTH_UP,
@@ -278,13 +284,19 @@ class Voxelizer:
278
284
  dem_grid_ori: np.ndarray,
279
285
  tree_grid_ori: np.ndarray,
280
286
  layered_interval: Optional[int] = None,
287
+ print_class_info: bool = True,
281
288
  ):
282
289
  print("Generating 3D voxel data")
290
+ if print_class_info:
291
+ print(VOXEL_CODE_DESCRIPTIONS)
292
+ print(LAND_COVER_DESCRIPTIONS)
283
293
 
284
- if self.land_cover_source != 'OpenEarthMapJapan':
285
- land_cover_grid_converted = convert_land_cover(land_cover_grid_ori, land_cover_source=self.land_cover_source)
294
+ if self.land_cover_source == 'OpenStreetMap':
295
+ # OpenStreetMap uses Standard classification, just shift to 1-based
296
+ land_cover_grid_converted = land_cover_grid_ori + 1
286
297
  else:
287
- land_cover_grid_converted = land_cover_grid_ori
298
+ # All other sources need remapping to standard indices
299
+ land_cover_grid_converted = convert_land_cover(land_cover_grid_ori, land_cover_source=self.land_cover_source)
288
300
 
289
301
  building_height_grid = ensure_orientation(
290
302
  building_height_grid_ori.copy(),
@@ -295,7 +307,7 @@ class Voxelizer:
295
307
  land_cover_grid_converted.copy(),
296
308
  ORIENTATION_NORTH_UP,
297
309
  ORIENTATION_SOUTH_UP,
298
- ) + 1
310
+ )
299
311
  dem_grid = ensure_orientation(
300
312
  dem_grid_ori.copy(),
301
313
  ORIENTATION_NORTH_UP,
@@ -1,75 +1,75 @@
1
- from . import (
2
- draw,
3
- utils,
4
- network,
5
- mesh,
6
- raster,
7
- conversion,
8
- io,
9
- heights,
10
- selection,
11
- overlap,
12
- merge_utils,
13
- )
14
-
15
- # Re-export frequently used functions at package level for convenience
16
- from .conversion import (
17
- filter_and_convert_gdf_to_geojson,
18
- geojson_to_gdf,
19
- gdf_to_geojson_dicts,
20
- )
21
- from .io import (
22
- get_geojson_from_gpkg,
23
- get_gdf_from_gpkg,
24
- load_gdf_from_multiple_gz,
25
- swap_coordinates,
26
- save_geojson,
27
- )
28
- from .heights import (
29
- extract_building_heights_from_gdf,
30
- extract_building_heights_from_geotiff,
31
- complement_building_heights_from_gdf,
32
- )
33
- from .selection import (
34
- filter_buildings,
35
- find_building_containing_point,
36
- get_buildings_in_drawn_polygon,
37
- )
38
- from .overlap import (
39
- process_building_footprints_by_overlap,
40
- )
41
- from .merge_utils import (
42
- merge_gdfs_with_id_conflict_resolution,
43
- )
44
-
45
- __all__ = [
46
- # submodules
47
- "draw",
48
- "utils",
49
- "network",
50
- "mesh",
51
- "raster",
52
- "conversion",
53
- "io",
54
- "heights",
55
- "selection",
56
- "overlap",
57
- "merge_utils",
58
- # functions
59
- "filter_and_convert_gdf_to_geojson",
60
- "geojson_to_gdf",
61
- "gdf_to_geojson_dicts",
62
- "get_geojson_from_gpkg",
63
- "get_gdf_from_gpkg",
64
- "load_gdf_from_multiple_gz",
65
- "swap_coordinates",
66
- "save_geojson",
67
- "extract_building_heights_from_gdf",
68
- "extract_building_heights_from_geotiff",
69
- "complement_building_heights_from_gdf",
70
- "filter_buildings",
71
- "find_building_containing_point",
72
- "get_buildings_in_drawn_polygon",
73
- "process_building_footprints_by_overlap",
74
- "merge_gdfs_with_id_conflict_resolution",
75
- ]
1
+ from . import (
2
+ draw,
3
+ utils,
4
+ network,
5
+ mesh,
6
+ raster,
7
+ conversion,
8
+ io,
9
+ heights,
10
+ selection,
11
+ overlap,
12
+ merge_utils,
13
+ )
14
+
15
+ # Re-export frequently used functions at package level for convenience
16
+ from .conversion import (
17
+ filter_and_convert_gdf_to_geojson,
18
+ geojson_to_gdf,
19
+ gdf_to_geojson_dicts,
20
+ )
21
+ from .io import (
22
+ get_geojson_from_gpkg,
23
+ get_gdf_from_gpkg,
24
+ load_gdf_from_multiple_gz,
25
+ swap_coordinates,
26
+ save_geojson,
27
+ )
28
+ from .heights import (
29
+ extract_building_heights_from_gdf,
30
+ extract_building_heights_from_geotiff,
31
+ complement_building_heights_from_gdf,
32
+ )
33
+ from .selection import (
34
+ filter_buildings,
35
+ find_building_containing_point,
36
+ get_buildings_in_drawn_polygon,
37
+ )
38
+ from .overlap import (
39
+ process_building_footprints_by_overlap,
40
+ )
41
+ from .merge_utils import (
42
+ merge_gdfs_with_id_conflict_resolution,
43
+ )
44
+
45
+ __all__ = [
46
+ # submodules
47
+ "draw",
48
+ "utils",
49
+ "network",
50
+ "mesh",
51
+ "raster",
52
+ "conversion",
53
+ "io",
54
+ "heights",
55
+ "selection",
56
+ "overlap",
57
+ "merge_utils",
58
+ # functions
59
+ "filter_and_convert_gdf_to_geojson",
60
+ "geojson_to_gdf",
61
+ "gdf_to_geojson_dicts",
62
+ "get_geojson_from_gpkg",
63
+ "get_gdf_from_gpkg",
64
+ "load_gdf_from_multiple_gz",
65
+ "swap_coordinates",
66
+ "save_geojson",
67
+ "extract_building_heights_from_gdf",
68
+ "extract_building_heights_from_geotiff",
69
+ "complement_building_heights_from_gdf",
70
+ "filter_buildings",
71
+ "find_building_containing_point",
72
+ "get_buildings_in_drawn_polygon",
73
+ "process_building_footprints_by_overlap",
74
+ "merge_gdfs_with_id_conflict_resolution",
75
+ ]