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

@@ -1,3 +1,4 @@
1
1
  from .envimet import *
2
2
  from .magicavoxel import *
3
- from .obj import *
3
+ from .obj import *
4
+ from .cityles import *
@@ -0,0 +1,368 @@
1
+ """
2
+ CityLES Exporter Module for VoxCity
3
+
4
+ This module provides functionality to export VoxCity grid data to the CityLES input file format.
5
+ CityLES is a large-eddy simulation (LES) model for urban environments, requiring specific input files
6
+ describing land use, building geometry, vegetation, and terrain.
7
+
8
+ Key Features:
9
+ - Converts VoxCity grids to CityLES-compatible input files (topog.txt, landuse.txt, dem.txt, vmap.txt, lonlat.txt)
10
+ - Handles land cover, building heights, canopy heights, and digital elevation models
11
+ - Supports flexible mapping from land cover and building types to CityLES codes
12
+ - Generates all required text files and metadata for CityLES runs
13
+
14
+ Main Functions:
15
+ - export_cityles: Main function to export all required CityLES input files
16
+ - export_topog: Exports building geometry (topog.txt)
17
+ - export_landuse: Exports land use grid (landuse.txt)
18
+ - export_dem: Exports digital elevation model (dem.txt)
19
+ - export_vmap: Exports vegetation map (vmap.txt)
20
+ - export_lonlat: Exports longitude/latitude grid (lonlat.txt)
21
+
22
+ Dependencies:
23
+ - numpy: For array operations
24
+ - pathlib: For file and directory management
25
+ - os: For file system operations
26
+ """
27
+
28
+ import os
29
+ import numpy as np
30
+ from pathlib import Path
31
+
32
+
33
+ # Land cover to CityLES land use mapping
34
+ # Based on common land cover classifications to CityLES codes
35
+ LANDCOVER_TO_CITYLES_LANDUSE = {
36
+ # Built-up areas
37
+ 'building': 4, # Concrete building
38
+ 'road': 2, # High reflective asphalt without AH
39
+ 'parking': 2, # High reflective asphalt without AH
40
+ 'pavement': 11, # Concrete (proxy of block)
41
+
42
+ # Vegetation
43
+ 'grass': 10, # Grassland
44
+ 'forest': 16, # Deciduous broadleaf forest
45
+ 'tree': 16, # Deciduous broadleaf forest
46
+ 'agriculture': 7, # Dryland cropland and pasture
47
+ 'cropland': 7, # Dryland cropland and pasture
48
+ 'paddy': 6, # Paddy
49
+
50
+ # Water and bare land
51
+ 'water': 9, # Water
52
+ 'bare_soil': 8, # Barren or sparsely vegetated
53
+ 'sand': 8, # Barren or sparsely vegetated
54
+
55
+ # Default
56
+ 'default': 10 # Grassland as default
57
+ }
58
+
59
+ # Building material mapping
60
+ # Maps building types to CityLES building attribute codes
61
+ BUILDING_MATERIAL_MAPPING = {
62
+ 'concrete': 104, # Concrete building
63
+ 'residential': 105, # Slate roof (ordinal wooden house)
64
+ 'commercial': 104, # Concrete building
65
+ 'industrial': 104, # Concrete building
66
+ 'default': 104 # Default to concrete building
67
+ }
68
+
69
+ # Tree type mapping
70
+ TREE_TYPE_MAPPING = {
71
+ 'deciduous': 101, # Leaf
72
+ 'evergreen': 101, # Leaf (simplified)
73
+ 'default': 101 # Default to leaf
74
+ }
75
+
76
+
77
+ def create_cityles_directories(output_directory):
78
+ """Create necessary directories for CityLES output"""
79
+ output_path = Path(output_directory)
80
+ output_path.mkdir(parents=True, exist_ok=True)
81
+ return output_path
82
+
83
+
84
+ def get_land_use_code(land_cover_value, land_cover_source=None):
85
+ """
86
+ Convert land cover value to CityLES land use code
87
+
88
+ Parameters:
89
+ -----------
90
+ land_cover_value : int or str
91
+ Land cover value from VoxCity
92
+ land_cover_source : str, optional
93
+ Source of land cover data (e.g., 'esri', 'esa', 'osm')
94
+
95
+ Returns:
96
+ --------
97
+ int : CityLES land use code (1-17)
98
+ """
99
+ # If using numeric codes, you might need source-specific mappings
100
+ # This is a simplified example
101
+ if isinstance(land_cover_value, str):
102
+ return LANDCOVER_TO_CITYLES_LANDUSE.get(land_cover_value.lower(),
103
+ LANDCOVER_TO_CITYLES_LANDUSE['default'])
104
+
105
+ # Example mapping for ESRI land cover (adjust based on actual data source)
106
+ if land_cover_source == 'esri':
107
+ esri_mapping = {
108
+ 1: 9, # Water -> Water
109
+ 2: 16, # Trees -> Deciduous broadleaf forest
110
+ 4: 8, # Flooded vegetation -> Barren
111
+ 5: 10, # Crops -> Grassland (simplified)
112
+ 7: 4, # Built Area -> Concrete building
113
+ 8: 8, # Bare ground -> Barren
114
+ 9: 3, # Snow/Ice -> Concrete (proxy of jari)
115
+ 10: 9, # Clouds -> Water (simplified)
116
+ 11: 10 # Rangeland -> Grassland
117
+ }
118
+ return esri_mapping.get(land_cover_value, LANDCOVER_TO_CITYLES_LANDUSE['default'])
119
+
120
+ # Default mapping
121
+ return LANDCOVER_TO_CITYLES_LANDUSE['default']
122
+
123
+
124
+ def export_topog(building_height_grid, building_id_grid, output_path,
125
+ building_material='default'):
126
+ """
127
+ Export topog.txt file for CityLES
128
+
129
+ Parameters:
130
+ -----------
131
+ building_height_grid : numpy.ndarray
132
+ 2D array of building heights
133
+ building_id_grid : numpy.ndarray
134
+ 2D array of building IDs
135
+ output_path : Path
136
+ Output directory path
137
+ building_material : str
138
+ Building material type for mapping
139
+ """
140
+ filename = output_path / 'topog.txt'
141
+
142
+ # Get building positions (where height > 0)
143
+ building_positions = np.argwhere(building_height_grid > 0)
144
+ n_buildings = len(building_positions)
145
+
146
+ material_code = BUILDING_MATERIAL_MAPPING.get(building_material,
147
+ BUILDING_MATERIAL_MAPPING['default'])
148
+
149
+ with open(filename, 'w') as f:
150
+ # Write number of buildings
151
+ f.write(f"{n_buildings}\n")
152
+
153
+ # Write building data
154
+ for idx, (j, i) in enumerate(building_positions):
155
+ # CityLES uses 1-based indexing
156
+ i_1based = i + 1
157
+ j_1based = j + 1
158
+ height = building_height_grid[j, i]
159
+
160
+ # Format: i j height material_code depth1 depth2 changed_material
161
+ f.write(f"{i_1based} {j_1based} {height:.1f} {material_code} 0.0 0.0 102\n")
162
+
163
+
164
+ def export_landuse(land_cover_grid, output_path, land_cover_source=None):
165
+ """
166
+ Export landuse.txt file for CityLES
167
+
168
+ Parameters:
169
+ -----------
170
+ land_cover_grid : numpy.ndarray
171
+ 2D array of land cover values
172
+ output_path : Path
173
+ Output directory path
174
+ land_cover_source : str, optional
175
+ Source of land cover data
176
+ """
177
+ filename = output_path / 'landuse.txt'
178
+
179
+ # Flatten the grid and convert to CityLES codes
180
+ flat_grid = land_cover_grid.flatten()
181
+
182
+ with open(filename, 'w') as f:
183
+ for value in flat_grid:
184
+ cityles_code = get_land_use_code(value, land_cover_source)
185
+ f.write(f"{cityles_code}\n")
186
+
187
+
188
+ def export_dem(dem_grid, output_path):
189
+ """
190
+ Export dem.txt file for CityLES
191
+
192
+ Parameters:
193
+ -----------
194
+ dem_grid : numpy.ndarray
195
+ 2D array of elevation values
196
+ output_path : Path
197
+ Output directory path
198
+ """
199
+ filename = output_path / 'dem.txt'
200
+
201
+ ny, nx = dem_grid.shape
202
+
203
+ with open(filename, 'w') as f:
204
+ for j in range(ny):
205
+ for i in range(nx):
206
+ # CityLES uses 1-based indexing
207
+ i_1based = i + 1
208
+ j_1based = j + 1
209
+ elevation = dem_grid[j, i]
210
+
211
+ f.write(f"{i_1based} {j_1based} {elevation:.1f}\n")
212
+
213
+
214
+ def export_vmap(canopy_height_grid, output_path, tree_base_ratio=0.3, tree_type='default'):
215
+ """
216
+ Export vmap.txt file for CityLES
217
+
218
+ Parameters:
219
+ -----------
220
+ canopy_height_grid : numpy.ndarray
221
+ 2D array of canopy heights
222
+ output_path : Path
223
+ Output directory path
224
+ tree_base_ratio : float
225
+ Ratio of tree base height to total canopy height
226
+ tree_type : str
227
+ Tree type for mapping
228
+ """
229
+ filename = output_path / 'vmap.txt'
230
+
231
+ # Get tree positions (where canopy height > 0)
232
+ tree_positions = np.argwhere(canopy_height_grid > 0)
233
+ n_trees = len(tree_positions)
234
+
235
+ tree_code = TREE_TYPE_MAPPING.get(tree_type, TREE_TYPE_MAPPING['default'])
236
+
237
+ with open(filename, 'w') as f:
238
+ # Write number of trees
239
+ f.write(f"{n_trees}\n")
240
+
241
+ # Write tree data
242
+ for idx, (j, i) in enumerate(tree_positions):
243
+ # CityLES uses 1-based indexing
244
+ i_1based = i + 1
245
+ j_1based = j + 1
246
+ total_height = canopy_height_grid[j, i]
247
+ lower_height = total_height * tree_base_ratio
248
+ upper_height = total_height
249
+
250
+ # Format: i j lower_height upper_height tree_type
251
+ f.write(f"{i_1based} {j_1based} {lower_height:.1f} {upper_height:.1f} {tree_code}\n")
252
+
253
+
254
+ def export_lonlat(rectangle_vertices, grid_shape, output_path):
255
+ """
256
+ Export lonlat.txt file for CityLES
257
+
258
+ Parameters:
259
+ -----------
260
+ rectangle_vertices : list of tuples
261
+ List of (lon, lat) vertices defining the area
262
+ grid_shape : tuple
263
+ Shape of the grid (ny, nx)
264
+ output_path : Path
265
+ Output directory path
266
+ """
267
+ filename = output_path / 'lonlat.txt'
268
+
269
+ ny, nx = grid_shape
270
+
271
+ # Extract bounds from vertices
272
+ lons = [v[0] for v in rectangle_vertices]
273
+ lats = [v[1] for v in rectangle_vertices]
274
+ min_lon, max_lon = min(lons), max(lons)
275
+ min_lat, max_lat = min(lats), max(lats)
276
+
277
+ # Create coordinate grids
278
+ lon_vals = np.linspace(min_lon, max_lon, nx)
279
+ lat_vals = np.linspace(min_lat, max_lat, ny)
280
+
281
+ with open(filename, 'w') as f:
282
+ for j in range(ny):
283
+ for i in range(nx):
284
+ # CityLES uses 1-based indexing
285
+ i_1based = i + 1
286
+ j_1based = j + 1
287
+ lon = lon_vals[i]
288
+ lat = lat_vals[j]
289
+
290
+ f.write(f"{i_1based} {j_1based} {lon:.7f} {lat:.8f}\n")
291
+
292
+
293
+ def export_cityles(building_height_grid, building_id_grid, canopy_height_grid,
294
+ land_cover_grid, dem_grid, meshsize, land_cover_source,
295
+ rectangle_vertices, output_directory="output/cityles",
296
+ building_material='default', tree_type='default',
297
+ tree_base_ratio=0.3, **kwargs):
298
+ """
299
+ Export VoxCity data to CityLES format
300
+
301
+ Parameters:
302
+ -----------
303
+ building_height_grid : numpy.ndarray
304
+ 2D array of building heights
305
+ building_id_grid : numpy.ndarray
306
+ 2D array of building IDs
307
+ canopy_height_grid : numpy.ndarray
308
+ 2D array of canopy heights
309
+ land_cover_grid : numpy.ndarray
310
+ 2D array of land cover values
311
+ dem_grid : numpy.ndarray
312
+ 2D array of elevation values
313
+ meshsize : float
314
+ Grid cell size in meters
315
+ land_cover_source : str
316
+ Source of land cover data (e.g., 'esri', 'esa', 'osm')
317
+ rectangle_vertices : list of tuples
318
+ List of (lon, lat) vertices defining the area
319
+ output_directory : str
320
+ Output directory path
321
+ building_material : str
322
+ Building material type for mapping
323
+ tree_type : str
324
+ Tree type for mapping
325
+ tree_base_ratio : float
326
+ Ratio of tree base height to total canopy height
327
+ **kwargs : dict
328
+ Additional parameters (for compatibility)
329
+
330
+ Returns:
331
+ --------
332
+ str : Path to output directory
333
+ """
334
+ # Create output directory
335
+ output_path = create_cityles_directories(output_directory)
336
+
337
+ print(f"Exporting CityLES files to: {output_path}")
338
+
339
+ # Export individual files
340
+ print("Exporting topog.txt...")
341
+ export_topog(building_height_grid, building_id_grid, output_path, building_material)
342
+
343
+ print("Exporting landuse.txt...")
344
+ export_landuse(land_cover_grid, output_path, land_cover_source)
345
+
346
+ print("Exporting dem.txt...")
347
+ export_dem(dem_grid, output_path)
348
+
349
+ print("Exporting vmap.txt...")
350
+ export_vmap(canopy_height_grid, output_path, tree_base_ratio, tree_type)
351
+
352
+ print("Exporting lonlat.txt...")
353
+ export_lonlat(rectangle_vertices, building_height_grid.shape, output_path)
354
+
355
+ # Create metadata file for reference
356
+ metadata_file = output_path / 'cityles_metadata.txt'
357
+ with open(metadata_file, 'w') as f:
358
+ f.write("CityLES Export Metadata\n")
359
+ f.write("====================\n")
360
+ f.write(f"Grid shape: {building_height_grid.shape}\n")
361
+ f.write(f"Mesh size: {meshsize} m\n")
362
+ f.write(f"Land cover source: {land_cover_source}\n")
363
+ f.write(f"Building material: {building_material}\n")
364
+ f.write(f"Tree type: {tree_type}\n")
365
+ f.write(f"Bounds: {rectangle_vertices}\n")
366
+
367
+ print(f"CityLES export completed successfully!")
368
+ return str(output_path)
voxcity/generator.py CHANGED
@@ -6,12 +6,19 @@ It handles land cover, building heights, canopy heights, and digital elevation m
6
6
 
7
7
  The main functions are:
8
8
  - get_land_cover_grid: Creates a grid of land cover classifications
9
- - get_building_height_grid: Creates a grid of building heights
9
+ - get_building_height_grid: Creates a grid of building heights (supports GeoDataFrame input)
10
10
  - get_canopy_height_grid: Creates a grid of tree canopy heights
11
11
  - get_dem_grid: Creates a digital elevation model grid
12
12
  - create_3d_voxel: Combines the grids into a 3D voxel representation
13
13
  - create_3d_voxel_individuals: Creates separate voxel grids for each component
14
- - get_voxcity: Main function to generate a complete voxel city model
14
+ - get_voxcity: Main function to generate a complete voxel city model (supports GeoDataFrame input)
15
+
16
+ Key Features:
17
+ - Support for multiple data sources (OpenStreetMap, ESA WorldCover, Google Earth Engine, etc.)
18
+ - Direct GeoDataFrame input for building data (useful for custom datasets)
19
+ - 3D voxel generation with configurable resolution
20
+ - Visualization capabilities for both 2D grids and 3D models
21
+ - Data export in various formats (GeoTIFF, GeoJSON, pickle)
15
22
  """
16
23
 
17
24
  # Standard library imports
@@ -87,6 +94,7 @@ def get_land_cover_grid(rectangle_vertices, meshsize, source, output_dir, **kwar
87
94
  - esri_landcover_year: Year for ESRI land cover data
88
95
  - dynamic_world_date: Date for Dynamic World data
89
96
  - gridvis: Whether to visualize the grid
97
+ - default_land_cover_class: Default class for grid cells with no intersecting polygons (default: 'Developed space')
90
98
 
91
99
  Returns:
92
100
  numpy.ndarray: Grid of land cover classifications as integer values
@@ -142,7 +150,8 @@ def get_land_cover_grid(rectangle_vertices, meshsize, source, output_dir, **kwar
142
150
  # Different processing for vector vs raster data sources
143
151
  if source == 'OpenStreetMap':
144
152
  # Process vector data directly from GeoDataFrame
145
- land_cover_grid_str = create_land_cover_grid_from_gdf_polygon(land_cover_gdf, meshsize, source, rectangle_vertices)
153
+ default_class = kwargs.get('default_land_cover_class', 'Developed space')
154
+ land_cover_grid_str = create_land_cover_grid_from_gdf_polygon(land_cover_gdf, meshsize, source, rectangle_vertices, default_class=default_class)
146
155
  else:
147
156
  # Process raster data from GeoTIFF file
148
157
  land_cover_grid_str = create_land_cover_grid_from_geotiff_polygon(geotiff_path, meshsize, land_cover_classes, rectangle_vertices)
@@ -164,14 +173,15 @@ def get_land_cover_grid(rectangle_vertices, meshsize, source, output_dir, **kwar
164
173
  return land_cover_grid_int
165
174
 
166
175
  # def get_building_height_grid(rectangle_vertices, meshsize, source, output_dir="output", visualization=True, maptiler_API_key=None, file_path=None):
167
- def get_building_height_grid(rectangle_vertices, meshsize, source, output_dir, **kwargs):
176
+ def get_building_height_grid(rectangle_vertices, meshsize, source, output_dir, building_gdf=None, **kwargs):
168
177
  """Creates a grid of building heights.
169
178
 
170
179
  Args:
171
180
  rectangle_vertices: List of coordinates defining the area of interest
172
181
  meshsize: Size of each grid cell in meters
173
- source: Data source for buildings (e.g. 'OpenStreetMap', 'Microsoft Building Footprints')
182
+ source: Data source for buildings (e.g. 'OpenStreetMap', 'Microsoft Building Footprints', 'GeoDataFrame')
174
183
  output_dir: Directory to save output files
184
+ building_gdf: Optional GeoDataFrame with building footprint, height and other information
175
185
  **kwargs: Additional arguments including:
176
186
  - maptiler_API_key: API key for MapTiler
177
187
  - building_path: Path to local building data file
@@ -187,7 +197,7 @@ def get_building_height_grid(rectangle_vertices, meshsize, source, output_dir, *
187
197
  """
188
198
 
189
199
  # Initialize Earth Engine for satellite-based building data sources
190
- if source not in ["OpenStreetMap", "Overture", "Local file"]:
200
+ if source not in ["OpenStreetMap", "Overture", "Local file", "GeoDataFrame"]:
191
201
  initialize_earth_engine()
192
202
 
193
203
  print("Creating Building Height grid\n ")
@@ -195,32 +205,40 @@ def get_building_height_grid(rectangle_vertices, meshsize, source, output_dir, *
195
205
 
196
206
  os.makedirs(output_dir, exist_ok=True)
197
207
 
198
- # Fetch building data from primary source
199
- # Each source has different data formats and processing requirements
200
- if source == 'Microsoft Building Footprints':
201
- # Machine learning-derived building footprints from satellite imagery
202
- gdf = get_mbfp_gdf(output_dir, rectangle_vertices)
203
- elif source == 'OpenStreetMap':
204
- # Crowd-sourced building data with varying completeness
205
- gdf = load_gdf_from_openstreetmap(rectangle_vertices)
206
- elif source == "Open Building 2.5D Temporal":
207
- # Special case: this source provides both footprints and heights
208
- # Skip GeoDataFrame processing and create grids directly
209
- building_height_grid, building_min_height_grid, building_id_grid, filtered_buildings = create_building_height_grid_from_open_building_temporal_polygon(meshsize, rectangle_vertices, output_dir)
210
- elif source == 'EUBUCCO v0.1':
211
- # European building database with height information
212
- gdf = load_gdf_from_eubucco(rectangle_vertices, output_dir)
213
- # elif source == "OpenMapTiles":
214
- # # Vector tiles service for building data
215
- # gdf = load_gdf_from_openmaptiles(rectangle_vertices, kwargs["maptiler_API_key"])
216
- elif source == "Overture":
217
- # Open building dataset from Overture Maps Foundation
218
- gdf = load_gdf_from_overture(rectangle_vertices)
219
- elif source == "Local file":
220
- # Handle user-provided local building data files
221
- _, extension = os.path.splitext(kwargs["building_path"])
222
- if extension == ".gpkg":
223
- gdf = get_gdf_from_gpkg(kwargs["building_path"], rectangle_vertices)
208
+ # If building_gdf is provided, use it directly
209
+ if building_gdf is not None:
210
+ gdf = building_gdf
211
+ print("Using provided GeoDataFrame for building data")
212
+ else:
213
+ # Fetch building data from primary source
214
+ # Each source has different data formats and processing requirements
215
+ if source == 'Microsoft Building Footprints':
216
+ # Machine learning-derived building footprints from satellite imagery
217
+ gdf = get_mbfp_gdf(output_dir, rectangle_vertices)
218
+ elif source == 'OpenStreetMap':
219
+ # Crowd-sourced building data with varying completeness
220
+ gdf = load_gdf_from_openstreetmap(rectangle_vertices)
221
+ elif source == "Open Building 2.5D Temporal":
222
+ # Special case: this source provides both footprints and heights
223
+ # Skip GeoDataFrame processing and create grids directly
224
+ building_height_grid, building_min_height_grid, building_id_grid, filtered_buildings = create_building_height_grid_from_open_building_temporal_polygon(meshsize, rectangle_vertices, output_dir)
225
+ elif source == 'EUBUCCO v0.1':
226
+ # European building database with height information
227
+ gdf = load_gdf_from_eubucco(rectangle_vertices, output_dir)
228
+ # elif source == "OpenMapTiles":
229
+ # # Vector tiles service for building data
230
+ # gdf = load_gdf_from_openmaptiles(rectangle_vertices, kwargs["maptiler_API_key"])
231
+ elif source == "Overture":
232
+ # Open building dataset from Overture Maps Foundation
233
+ gdf = load_gdf_from_overture(rectangle_vertices)
234
+ elif source == "Local file":
235
+ # Handle user-provided local building data files
236
+ _, extension = os.path.splitext(kwargs["building_path"])
237
+ if extension == ".gpkg":
238
+ gdf = get_gdf_from_gpkg(kwargs["building_path"], rectangle_vertices)
239
+ elif source == "GeoDataFrame":
240
+ # This case is handled by the building_gdf parameter above
241
+ raise ValueError("When source is 'GeoDataFrame', building_gdf parameter must be provided")
224
242
 
225
243
  # Handle complementary data sources to fill gaps or provide additional information
226
244
  # This allows combining multiple sources for better coverage or accuracy
@@ -590,16 +608,17 @@ def create_3d_voxel_individuals(building_height_grid_ori, land_cover_grid_ori, d
590
608
 
591
609
  return land_cover_voxel_grid, building_voxel_grid, tree_voxel_grid, dem_voxel_grid, layered_voxel_grid
592
610
 
593
- def get_voxcity(rectangle_vertices, building_source, land_cover_source, canopy_height_source, dem_source, meshsize, **kwargs):
611
+ def get_voxcity(rectangle_vertices, building_source, land_cover_source, canopy_height_source, dem_source, meshsize, building_gdf=None, **kwargs):
594
612
  """Main function to generate a complete voxel city model.
595
613
 
596
614
  Args:
597
615
  rectangle_vertices: List of coordinates defining the area of interest
598
- building_source: Source for building height data (e.g. 'OSM', 'EUBUCCO')
616
+ building_source: Source for building height data (e.g. 'OSM', 'EUBUCCO', 'GeoDataFrame')
599
617
  land_cover_source: Source for land cover data (e.g. 'ESA', 'ESRI')
600
618
  canopy_height_source: Source for tree canopy height data
601
619
  dem_source: Source for digital elevation model data ('Flat' or other source)
602
620
  meshsize: Size of each grid cell in meters
621
+ building_gdf: Optional GeoDataFrame with building footprint, height and other information
603
622
  **kwargs: Additional keyword arguments including:
604
623
  - output_dir: Directory to save output files (default: 'output')
605
624
  - min_canopy_height: Minimum height threshold for tree canopy
@@ -607,6 +626,7 @@ def get_voxcity(rectangle_vertices, building_source, land_cover_source, canopy_h
607
626
  - mapvis: Whether to visualize grids on map
608
627
  - voxelvis: Whether to visualize 3D voxel model
609
628
  - voxelvis_img_save_path: Path to save 3D visualization
629
+ - default_land_cover_class: Default class for land cover grid cells with no intersecting polygons (default: 'Developed space')
610
630
 
611
631
  Returns:
612
632
  tuple containing:
@@ -633,7 +653,7 @@ def get_voxcity(rectangle_vertices, building_source, land_cover_source, canopy_h
633
653
  land_cover_grid = get_land_cover_grid(rectangle_vertices, meshsize, land_cover_source, output_dir, **kwargs)
634
654
 
635
655
  # Building footprints and height information
636
- building_height_grid, building_min_height_grid, building_id_grid, building_gdf = get_building_height_grid(rectangle_vertices, meshsize, building_source, output_dir, **kwargs)
656
+ building_height_grid, building_min_height_grid, building_id_grid, building_gdf = get_building_height_grid(rectangle_vertices, meshsize, building_source, output_dir, building_gdf=building_gdf, **kwargs)
637
657
 
638
658
  # Save building data to file for later analysis or visualization
639
659
  if not building_gdf.empty: