voxcity 0.6.15__tar.gz → 0.6.17__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (37) hide show
  1. {voxcity-0.6.15 → voxcity-0.6.17}/PKG-INFO +1 -1
  2. {voxcity-0.6.15 → voxcity-0.6.17}/pyproject.toml +1 -1
  3. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/osm.py +23 -7
  4. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/overture.py +26 -1
  5. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/exporter/cityles.py +7 -4
  6. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/generator.py +1291 -1136
  7. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/grid.py +1739 -1568
  8. {voxcity-0.6.15 → voxcity-0.6.17}/AUTHORS.rst +0 -0
  9. {voxcity-0.6.15 → voxcity-0.6.17}/LICENSE +0 -0
  10. {voxcity-0.6.15 → voxcity-0.6.17}/README.md +0 -0
  11. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/__init__.py +0 -0
  12. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/__init__.py +0 -0
  13. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/citygml.py +0 -0
  14. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/eubucco.py +0 -0
  15. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/gee.py +0 -0
  16. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/mbfp.py +0 -0
  17. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/oemj.py +0 -0
  18. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/downloader/utils.py +0 -0
  19. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/exporter/__init__.py +0 -0
  20. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/exporter/envimet.py +0 -0
  21. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/exporter/magicavoxel.py +0 -0
  22. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/exporter/obj.py +0 -0
  23. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/__init__.py +0 -0
  24. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/draw.py +0 -0
  25. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/mesh.py +0 -0
  26. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/network.py +0 -0
  27. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/polygon.py +0 -0
  28. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/geoprocessor/utils.py +0 -0
  29. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/simulator/__init__.py +0 -0
  30. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/simulator/solar.py +0 -0
  31. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/simulator/utils.py +0 -0
  32. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/simulator/view.py +0 -0
  33. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/utils/__init__.py +0 -0
  34. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/utils/lc.py +0 -0
  35. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/utils/material.py +0 -0
  36. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/utils/visualization.py +0 -0
  37. {voxcity-0.6.15 → voxcity-0.6.17}/src/voxcity/utils/weather.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: voxcity
3
- Version: 0.6.15
3
+ Version: 0.6.17
4
4
  Summary: voxcity is an easy and one-stop tool to output 3d city models for microclimate simulation by integrating multiple geospatial open-data
5
5
  License: MIT
6
6
  Author: Kunihiko Fujiwara
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "voxcity"
3
- version = "0.6.15"
3
+ version = "0.6.17"
4
4
  description = "voxcity is an easy and one-stop tool to output 3d city models for microclimate simulation by integrating multiple geospatial open-data"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -370,7 +370,7 @@ def create_rings_from_ways(way_ids, ways, nodes):
370
370
 
371
371
  return rings
372
372
 
373
- def load_gdf_from_openstreetmap(rectangle_vertices):
373
+ def load_gdf_from_openstreetmap(rectangle_vertices, floor_height=3.0):
374
374
  """Download and process building footprint data from OpenStreetMap.
375
375
 
376
376
  This function:
@@ -471,7 +471,7 @@ def load_gdf_from_openstreetmap(rectangle_vertices):
471
471
  """
472
472
  return [coord for coord in geometry] # Keep original order since already (lon, lat)
473
473
 
474
- def get_height_from_properties(properties):
474
+ def get_height_from_properties(properties, floor_height=3.0):
475
475
  """Helper function to extract height from properties.
476
476
 
477
477
  Args:
@@ -487,9 +487,25 @@ def load_gdf_from_openstreetmap(rectangle_vertices):
487
487
  except ValueError:
488
488
  pass
489
489
 
490
+ # Infer from floors when available
491
+ floors_candidates = [
492
+ properties.get('building:levels'),
493
+ properties.get('levels'),
494
+ properties.get('num_floors')
495
+ ]
496
+ for floors in floors_candidates:
497
+ if floors is None:
498
+ continue
499
+ try:
500
+ floors_val = float(floors)
501
+ if floors_val > 0:
502
+ return float(floor_height) * floors_val
503
+ except ValueError:
504
+ continue
505
+
490
506
  return 0 # Default height if no valid height found
491
507
 
492
- def extract_properties(element):
508
+ def extract_properties(element, floor_height=3.0):
493
509
  """Helper function to extract and process properties from an element.
494
510
 
495
511
  Args:
@@ -501,7 +517,7 @@ def load_gdf_from_openstreetmap(rectangle_vertices):
501
517
  properties = element.get('tags', {})
502
518
 
503
519
  # Get height (now using the helper function)
504
- height = get_height_from_properties(properties)
520
+ height = get_height_from_properties(properties, floor_height=floor_height)
505
521
 
506
522
  # Get min_height and min_level
507
523
  min_height = properties.get('min_height', '0')
@@ -526,7 +542,7 @@ def load_gdf_from_openstreetmap(rectangle_vertices):
526
542
  "is_inner": False,
527
543
  "levels": levels,
528
544
  "height_source": "explicit" if properties.get('height') or properties.get('building:height')
529
- else "levels" if levels is not None
545
+ else "levels" if (levels is not None) or (properties.get('num_floors') is not None)
530
546
  else "default",
531
547
  "min_level": min_level if min_level != '0' else None,
532
548
  "building": properties.get('building', 'no'),
@@ -584,13 +600,13 @@ def load_gdf_from_openstreetmap(rectangle_vertices):
584
600
  if element['type'] == 'way':
585
601
  if 'geometry' in element:
586
602
  coords = [(node['lon'], node['lat']) for node in element['geometry']]
587
- properties = extract_properties(element)
603
+ properties = extract_properties(element, floor_height=floor_height)
588
604
  feature = create_polygon_feature(coords, properties)
589
605
  if feature:
590
606
  features.append(feature)
591
607
 
592
608
  elif element['type'] == 'relation':
593
- properties = extract_properties(element)
609
+ properties = extract_properties(element, floor_height=floor_height)
594
610
 
595
611
  # Process each member of the relation
596
612
  for member in element['members']:
@@ -254,7 +254,7 @@ def join_gdfs_vertically(gdf1, gdf2):
254
254
 
255
255
  return combined_gdf
256
256
 
257
- def load_gdf_from_overture(rectangle_vertices):
257
+ def load_gdf_from_overture(rectangle_vertices, floor_height=3.0):
258
258
  """
259
259
  Download and process building footprint data from Overture Maps.
260
260
 
@@ -287,6 +287,31 @@ def load_gdf_from_overture(rectangle_vertices):
287
287
  # Combine both datasets into a single comprehensive building dataset
288
288
  joined_building_gdf = join_gdfs_vertically(building_gdf, building_part_gdf)
289
289
 
290
+ # Ensure numeric height and infer from floors when missing
291
+ try:
292
+ joined_building_gdf['height'] = pd.to_numeric(joined_building_gdf.get('height', None), errors='coerce')
293
+ except Exception:
294
+ # Create height column if missing
295
+ joined_building_gdf['height'] = None
296
+ joined_building_gdf['height'] = pd.to_numeric(joined_building_gdf['height'], errors='coerce')
297
+
298
+ # Combine possible floors columns (first non-null among candidates)
299
+ floors_candidates = []
300
+ for col in ['building:levels', 'levels', 'num_floors', 'floors']:
301
+ if col in joined_building_gdf.columns:
302
+ floors_candidates.append(pd.to_numeric(joined_building_gdf[col], errors='coerce'))
303
+ if floors_candidates:
304
+ floors_series = floors_candidates[0]
305
+ for s in floors_candidates[1:]:
306
+ floors_series = floors_series.combine_first(s)
307
+ # Infer height where height is NaN/<=0 and floors > 0
308
+ mask_missing_height = (~joined_building_gdf['height'].notna()) | (joined_building_gdf['height'] <= 0)
309
+ if isinstance(floor_height, (int, float)):
310
+ inferred = floors_series * float(floor_height)
311
+ else:
312
+ inferred = floors_series * 3.0
313
+ joined_building_gdf.loc[mask_missing_height & (floors_series > 0), 'height'] = inferred
314
+
290
315
  # Assign sequential IDs based on the final dataset index
291
316
  joined_building_gdf['id'] = joined_building_gdf.index
292
317
 
@@ -322,7 +322,7 @@ def export_dem(dem_grid, output_path):
322
322
  f.write(f"{i_1based} {j_1based} {elevation:.1f}\n")
323
323
 
324
324
 
325
- def export_vmap(canopy_height_grid, output_path, tree_base_ratio=0.3, tree_type='default', building_height_grid=None):
325
+ def export_vmap(canopy_height_grid, output_path, tree_base_ratio=0.3, tree_type='default', building_height_grid=None, canopy_bottom_height_grid=None):
326
326
  """
327
327
  Export vmap.txt file for CityLES
328
328
 
@@ -363,7 +363,10 @@ def export_vmap(canopy_height_grid, output_path, tree_base_ratio=0.3, tree_type=
363
363
  i_1based = i + 1
364
364
  j_1based = j + 1
365
365
  total_height = float(effective_canopy[j, i])
366
- lower_height = total_height * tree_base_ratio
366
+ if canopy_bottom_height_grid is not None:
367
+ lower_height = float(np.clip(canopy_bottom_height_grid[j, i], 0.0, total_height))
368
+ else:
369
+ lower_height = total_height * tree_base_ratio
367
370
  upper_height = total_height
368
371
  # Format: i j lower_height upper_height tree_type
369
372
  f.write(f"{i_1based} {j_1based} {lower_height:.1f} {upper_height:.1f} {tree_code}\n")
@@ -413,7 +416,7 @@ def export_cityles(building_height_grid, building_id_grid, canopy_height_grid,
413
416
  land_cover_grid, dem_grid, meshsize, land_cover_source,
414
417
  rectangle_vertices, output_directory="output/cityles",
415
418
  building_material='default', tree_type='default',
416
- tree_base_ratio=0.3, **kwargs):
419
+ tree_base_ratio=0.3, canopy_bottom_height_grid=None, **kwargs):
417
420
  """
418
421
  Export VoxCity data to CityLES format
419
422
 
@@ -473,7 +476,7 @@ def export_cityles(building_height_grid, building_id_grid, canopy_height_grid,
473
476
  export_dem(dem_grid, output_path)
474
477
 
475
478
  print("\nExporting vmap.txt...")
476
- export_vmap(canopy_height_grid, output_path, tree_base_ratio, tree_type, building_height_grid=building_height_grid)
479
+ export_vmap(canopy_height_grid, output_path, tree_base_ratio, tree_type, building_height_grid=building_height_grid, canopy_bottom_height_grid=canopy_bottom_height_grid)
477
480
 
478
481
  print("\nExporting lonlat.txt...")
479
482
  export_lonlat(rectangle_vertices, building_height_grid.shape, output_path)