voxcity 0.5.5__tar.gz → 0.5.8__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 (54) hide show
  1. {voxcity-0.5.5 → voxcity-0.5.8}/PKG-INFO +2 -2
  2. {voxcity-0.5.5 → voxcity-0.5.8}/pyproject.toml +2 -3
  3. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/citygml.py +6 -6
  4. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/exporter/obj.py +2 -0
  5. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/generator.py +8 -3
  6. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/grid.py +1 -0
  7. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/utils/visualization.py +92 -4
  8. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity.egg-info/PKG-INFO +2 -2
  9. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity.egg-info/requires.txt +1 -1
  10. {voxcity-0.5.5 → voxcity-0.5.8}/AUTHORS.rst +0 -0
  11. {voxcity-0.5.5 → voxcity-0.5.8}/CONTRIBUTING.rst +0 -0
  12. {voxcity-0.5.5 → voxcity-0.5.8}/HISTORY.rst +0 -0
  13. {voxcity-0.5.5 → voxcity-0.5.8}/LICENSE +0 -0
  14. {voxcity-0.5.5 → voxcity-0.5.8}/MANIFEST.in +0 -0
  15. {voxcity-0.5.5 → voxcity-0.5.8}/README.md +0 -0
  16. {voxcity-0.5.5 → voxcity-0.5.8}/docs/Makefile +0 -0
  17. {voxcity-0.5.5 → voxcity-0.5.8}/docs/archive/README.rst +0 -0
  18. {voxcity-0.5.5 → voxcity-0.5.8}/docs/authors.rst +0 -0
  19. {voxcity-0.5.5 → voxcity-0.5.8}/docs/conf.py +0 -0
  20. {voxcity-0.5.5 → voxcity-0.5.8}/docs/index.rst +0 -0
  21. {voxcity-0.5.5 → voxcity-0.5.8}/docs/make.bat +0 -0
  22. {voxcity-0.5.5 → voxcity-0.5.8}/setup.cfg +0 -0
  23. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/__init__.py +0 -0
  24. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/__init__.py +0 -0
  25. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/eubucco.py +0 -0
  26. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/gee.py +0 -0
  27. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/mbfp.py +0 -0
  28. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/oemj.py +0 -0
  29. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/omt.py +0 -0
  30. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/osm.py +0 -0
  31. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/overture.py +0 -0
  32. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/downloader/utils.py +0 -0
  33. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/exporter/__init_.py +0 -0
  34. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/exporter/envimet.py +0 -0
  35. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/exporter/magicavoxel.py +0 -0
  36. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/__init_.py +0 -0
  37. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/draw.py +0 -0
  38. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/mesh.py +0 -0
  39. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/network.py +0 -0
  40. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/polygon.py +0 -0
  41. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/geoprocessor/utils.py +0 -0
  42. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/simulator/__init_.py +0 -0
  43. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/simulator/solar.py +0 -0
  44. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/simulator/utils.py +0 -0
  45. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/simulator/view.py +0 -0
  46. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/utils/__init_.py +0 -0
  47. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/utils/lc.py +0 -0
  48. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/utils/material.py +0 -0
  49. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity/utils/weather.py +0 -0
  50. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity.egg-info/SOURCES.txt +0 -0
  51. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity.egg-info/dependency_links.txt +0 -0
  52. {voxcity-0.5.5 → voxcity-0.5.8}/src/voxcity.egg-info/top_level.txt +0 -0
  53. {voxcity-0.5.5 → voxcity-0.5.8}/tests/__init__.py +0 -0
  54. {voxcity-0.5.5 → voxcity-0.5.8}/tests/voxelcity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voxcity
3
- Version: 0.5.5
3
+ Version: 0.5.8
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
  Author-email: Kunihiko Fujiwara <kunihiko@nus.edu.sg>
6
6
  Maintainer-email: Kunihiko Fujiwara <kunihiko@nus.edu.sg>
@@ -45,7 +45,7 @@ Requires-Dist: pycountry
45
45
  Requires-Dist: osm2geojson
46
46
  Requires-Dist: seaborn
47
47
  Requires-Dist: overturemaps
48
- Requires-Dist: protobuf<=3.20.3
48
+ Requires-Dist: protobuf<6,>=4.21
49
49
  Requires-Dist: timezonefinder
50
50
  Requires-Dist: astral
51
51
  Requires-Dist: osmnx
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "voxcity"
3
- version = "0.5.5"
3
+ version = "0.5.8"
4
4
  requires-python = ">=3.10,<3.13"
5
5
  classifiers = [
6
6
  "Programming Language :: Python :: 3.10",
@@ -47,7 +47,7 @@ dependencies = [
47
47
  "osm2geojson",
48
48
  "seaborn",
49
49
  "overturemaps",
50
- "protobuf<=3.20.3",
50
+ "protobuf>=4.21,<6",
51
51
  "timezonefinder",
52
52
  "astral",
53
53
  "osmnx",
@@ -56,7 +56,6 @@ dependencies = [
56
56
  "pyvista",
57
57
  "IPython",
58
58
  "lxml"
59
-
60
59
  ]
61
60
 
62
61
  [project.optional-dependencies]
@@ -488,8 +488,8 @@ def load_plateau_with_terrain(url, base_dir):
488
488
 
489
489
  # Save
490
490
  gdf_buildings_swapped['id'] = gdf_buildings_swapped.index
491
- gdf_buildings_swapped.to_file('all_buildings_with_elevation.geojson', driver='GeoJSON')
492
- print(f"\nBuildings saved to all_buildings_with_elevation.geojson")
491
+ # gdf_buildings_swapped.to_file('all_buildings_with_elevation.geojson', driver='GeoJSON')
492
+ # print(f"\nBuildings saved to all_buildings_with_elevation.geojson")
493
493
  else:
494
494
  gdf_buildings_swapped = None
495
495
 
@@ -520,8 +520,8 @@ def load_plateau_with_terrain(url, base_dir):
520
520
  }
521
521
 
522
522
  gdf_terrain_swapped = gpd.GeoDataFrame(terrain_data, geometry='geometry', crs='EPSG:6697')
523
- gdf_terrain_swapped.to_file('terrain_elevation.geojson', driver='GeoJSON')
524
- print(f"Terrain saved to terrain_elevation.geojson")
523
+ # gdf_terrain_swapped.to_file('terrain_elevation.geojson', driver='GeoJSON')
524
+ # print(f"Terrain saved to terrain_elevation.geojson")
525
525
  else:
526
526
  gdf_terrain_swapped = None
527
527
 
@@ -548,8 +548,8 @@ def load_plateau_with_terrain(url, base_dir):
548
548
  'geometry': swapped_geometries
549
549
  }
550
550
  gdf_vegetation_swapped = gpd.GeoDataFrame(vegetation_data, geometry='geometry', crs='EPSG:6697')
551
- gdf_vegetation_swapped.to_file('vegetation_elevation.geojson', driver='GeoJSON')
552
- print(f"Vegetation saved to vegetation_elevation.geojson")
551
+ # gdf_vegetation_swapped.to_file('vegetation_elevation.geojson', driver='GeoJSON')
552
+ # print(f"Vegetation saved to vegetation_elevation.geojson")
553
553
  else:
554
554
  gdf_vegetation_swapped = None
555
555
 
@@ -9,6 +9,8 @@ import numpy as np
9
9
  import os
10
10
  from numba import njit, prange
11
11
  import matplotlib.pyplot as plt
12
+ import trimesh
13
+ import numpy as np
12
14
  from ..utils.visualization import get_voxel_color_map
13
15
 
14
16
  def convert_colormap_indices(original_map):
@@ -218,8 +218,6 @@ def get_building_height_grid(rectangle_vertices, meshsize, source, output_dir, *
218
218
  gdf_comp = get_mbfp_gdf(output_dir, rectangle_vertices)
219
219
  elif building_complementary_source == 'OpenStreetMap':
220
220
  gdf_comp = load_gdf_from_openstreetmap(rectangle_vertices)
221
- elif building_complementary_source == 'OSM Buildings':
222
- gdf_comp = load_gdf_from_osmbuildings(rectangle_vertices)
223
221
  elif building_complementary_source == 'EUBUCCO v0.1':
224
222
  gdf_comp = load_gdf_from_eubucco(rectangle_vertices, output_dir)
225
223
  elif building_complementary_source == "OpenMapTiles":
@@ -764,7 +762,14 @@ def get_voxcity_CityGML(rectangle_vertices, url_citygml, land_cover_source, cano
764
762
  else:
765
763
  canopy_height_grid_comp = get_canopy_height_grid(rectangle_vertices, meshsize, canopy_height_source, output_dir, **kwargs)
766
764
 
767
- canopy_height_grid = create_vegetation_height_grid_from_gdf_polygon(vegetation_gdf, meshsize, rectangle_vertices)
765
+ # In the get_voxcity_CityGML function, modify it to handle None vegetation_gdf
766
+ if vegetation_gdf is not None:
767
+ canopy_height_grid = create_vegetation_height_grid_from_gdf_polygon(vegetation_gdf, meshsize, rectangle_vertices)
768
+ else:
769
+ # Create an empty canopy_height_grid with the same shape as your other grids
770
+ # This depends on the expected shape, you might need to adjust
771
+ canopy_height_grid = np.zeros_like(building_height_grid)
772
+
768
773
  mask = (canopy_height_grid == 0) & (canopy_height_grid_comp != 0)
769
774
  canopy_height_grid[mask] = canopy_height_grid_comp[mask]
770
775
 
@@ -11,6 +11,7 @@ from pyproj import Geod, Transformer, CRS
11
11
  import rasterio
12
12
  from affine import Affine
13
13
  from shapely.geometry import box, Polygon, Point, MultiPolygon
14
+ import warnings
14
15
 
15
16
  from scipy.interpolate import griddata
16
17
  from shapely.errors import GEOSException
@@ -43,6 +43,7 @@ from ..geoprocessor.mesh import (
43
43
  create_city_meshes,
44
44
  export_meshes
45
45
  )
46
+ # from ..exporter.obj import save_obj_from_colored_mesh
46
47
  from .material import get_material_dict
47
48
 
48
49
  # def get_voxel_color_map():
@@ -74,6 +75,7 @@ from .material import get_material_dict
74
75
  # 13: [150, 166, 190], # 'Building (ground surface)'
75
76
  # 14: [239, 228, 176], # 'No Data (ground surface)'
76
77
  # }
78
+
77
79
  def get_voxel_color_map(color_scheme='default'):
78
80
  """
79
81
  Returns a color map for voxel visualization based on the specified color scheme.
@@ -1528,6 +1530,7 @@ def visualize_voxcity_multi_view(voxel_array, meshsize, **kwargs):
1528
1530
  vmax = kwargs.get("vmax", np.nanmax(sim_grid))
1529
1531
  projection_type = kwargs.get("projection_type", "perspective")
1530
1532
  distance_factor = kwargs.get("distance_factor", 1.0)
1533
+ save_obj = kwargs.get("save_obj", False)
1531
1534
 
1532
1535
  # Create meshes
1533
1536
  print("Creating voxel meshes...")
@@ -1579,6 +1582,13 @@ def visualize_voxcity_multi_view(voxel_array, meshsize, **kwargs):
1579
1582
  plt.axis('off')
1580
1583
  plt.show()
1581
1584
  plt.close()
1585
+
1586
+ # After creating the meshes and before visualization
1587
+ if save_obj:
1588
+ output_directory = kwargs.get('output_directory', 'output')
1589
+ output_file_name = kwargs.get('output_file_name', 'voxcity_mesh')
1590
+ obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name)
1591
+ print(f"Saved mesh files to:\n {obj_path}\n {mtl_path}")
1582
1592
 
1583
1593
  def visualize_voxcity_multi_view_with_multiple_sim_grids(voxel_array, meshsize, sim_configs, **kwargs):
1584
1594
  """
@@ -1610,7 +1620,9 @@ def visualize_voxcity_multi_view_with_multiple_sim_grids(voxel_array, meshsize,
1610
1620
  # Configure PyVista settings
1611
1621
  pv.set_plot_theme('document')
1612
1622
  pv.global_theme.background = 'white'
1613
- pv.global_theme.window_size = [1024, 768]
1623
+ window_width = kwargs.get("window_width", 1024)
1624
+ window_height = kwargs.get("window_height", 768)
1625
+ pv.global_theme.window_size = [window_width, window_height]
1614
1626
  pv.global_theme.jupyter_backend = 'static'
1615
1627
 
1616
1628
  # Parse general kwargs
@@ -1743,7 +1755,9 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
1743
1755
  # Configure PyVista settings
1744
1756
  pv.set_plot_theme('document')
1745
1757
  pv.global_theme.background = 'white'
1746
- pv.global_theme.window_size = [1024, 768]
1758
+ window_width = kwargs.get("window_width", 1024)
1759
+ window_height = kwargs.get("window_height", 768)
1760
+ pv.global_theme.window_size = [window_width, window_height]
1747
1761
  pv.global_theme.jupyter_backend = 'static'
1748
1762
 
1749
1763
  # Parse kwargs
@@ -1764,6 +1778,7 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
1764
1778
  colorbar_title = kwargs.get("colorbar_title", "")
1765
1779
  value_name = kwargs.get("value_name", None)
1766
1780
  nan_color = kwargs.get("nan_color", "gray")
1781
+ save_obj = kwargs.get("save_obj", False)
1767
1782
 
1768
1783
  if value_name is None:
1769
1784
  print("Set value_name")
@@ -1891,7 +1906,7 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
1891
1906
 
1892
1907
  # Display each view separately
1893
1908
  for view_name, img_file in image_files:
1894
- plt.figure(figsize=(12, 8))
1909
+ plt.figure(figsize=(24, 16))
1895
1910
  img = plt.imread(img_file)
1896
1911
  plt.imshow(img)
1897
1912
  plt.title(view_name.replace('_', ' ').title(), pad=20)
@@ -1899,6 +1914,13 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
1899
1914
  plt.show()
1900
1915
  plt.close()
1901
1916
 
1917
+ # After creating the meshes and before visualization
1918
+ if save_obj:
1919
+ output_directory = kwargs.get('output_directory', 'output')
1920
+ output_file_name = kwargs.get('output_file_name', 'voxcity_mesh')
1921
+ obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name)
1922
+ print(f"Saved mesh files to:\n {obj_path}\n {mtl_path}")
1923
+
1902
1924
  return image_files
1903
1925
 
1904
1926
  def visualize_building_sim_results(voxel_array, meshsize, building_sim_mesh, **kwargs):
@@ -1952,4 +1974,70 @@ def visualize_building_sim_results(voxel_array, meshsize, building_sim_mesh, **k
1952
1974
  meshsize,
1953
1975
  custom_meshes=custom_meshes,
1954
1976
  **kwargs
1955
- )
1977
+ )
1978
+
1979
+ def save_obj_from_colored_mesh(meshes, output_path, base_filename):
1980
+ """
1981
+ Save colored meshes as OBJ and MTL files.
1982
+
1983
+ Parameters
1984
+ ----------
1985
+ meshes : dict
1986
+ Dictionary of trimesh.Trimesh objects with face colors.
1987
+ output_path : str
1988
+ Directory path where to save the files.
1989
+ base_filename : str
1990
+ Base name for the output files (without extension).
1991
+
1992
+ Returns
1993
+ -------
1994
+ tuple
1995
+ Paths to the saved (obj_file, mtl_file).
1996
+ """
1997
+
1998
+ os.makedirs(output_path, exist_ok=True)
1999
+ obj_path = os.path.join(output_path, f"{base_filename}.obj")
2000
+ mtl_path = os.path.join(output_path, f"{base_filename}.mtl")
2001
+
2002
+ # Combine all meshes
2003
+ combined_mesh = trimesh.util.concatenate(list(meshes.values()))
2004
+
2005
+ # Create unique materials for each unique face color
2006
+ face_colors = combined_mesh.visual.face_colors
2007
+ unique_colors = np.unique(face_colors, axis=0)
2008
+
2009
+ # Write MTL file
2010
+ with open(mtl_path, 'w') as mtl_file:
2011
+ for i, color in enumerate(unique_colors):
2012
+ material_name = f'material_{i}'
2013
+ mtl_file.write(f'newmtl {material_name}\n')
2014
+ # Convert RGBA to RGB float values
2015
+ rgb = color[:3].astype(float) / 255.0
2016
+ mtl_file.write(f'Kd {rgb[0]:.6f} {rgb[1]:.6f} {rgb[2]:.6f}\n')
2017
+ mtl_file.write(f'd {color[3]/255.0:.6f}\n\n') # Alpha value
2018
+
2019
+ # Create material groups based on face colors
2020
+ color_to_material = {tuple(c): f'material_{i}' for i, c in enumerate(unique_colors)}
2021
+
2022
+ # Write OBJ file
2023
+ with open(obj_path, 'w') as obj_file:
2024
+ obj_file.write(f'mtllib {os.path.basename(mtl_path)}\n')
2025
+
2026
+ # Write vertices
2027
+ for vertex in combined_mesh.vertices:
2028
+ obj_file.write(f'v {vertex[0]:.6f} {vertex[1]:.6f} {vertex[2]:.6f}\n')
2029
+
2030
+ # Write faces grouped by material
2031
+ current_material = None
2032
+ for face_idx, face in enumerate(combined_mesh.faces):
2033
+ face_color = tuple(face_colors[face_idx])
2034
+ material_name = color_to_material[face_color]
2035
+
2036
+ if material_name != current_material:
2037
+ obj_file.write(f'usemtl {material_name}\n')
2038
+ current_material = material_name
2039
+
2040
+ # OBJ indices are 1-based
2041
+ obj_file.write(f'f {face[0]+1} {face[1]+1} {face[2]+1}\n')
2042
+
2043
+ return obj_path, mtl_path
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voxcity
3
- Version: 0.5.5
3
+ Version: 0.5.8
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
  Author-email: Kunihiko Fujiwara <kunihiko@nus.edu.sg>
6
6
  Maintainer-email: Kunihiko Fujiwara <kunihiko@nus.edu.sg>
@@ -45,7 +45,7 @@ Requires-Dist: pycountry
45
45
  Requires-Dist: osm2geojson
46
46
  Requires-Dist: seaborn
47
47
  Requires-Dist: overturemaps
48
- Requires-Dist: protobuf<=3.20.3
48
+ Requires-Dist: protobuf<6,>=4.21
49
49
  Requires-Dist: timezonefinder
50
50
  Requires-Dist: astral
51
51
  Requires-Dist: osmnx
@@ -28,7 +28,7 @@ pycountry
28
28
  osm2geojson
29
29
  seaborn
30
30
  overturemaps
31
- protobuf<=3.20.3
31
+ protobuf<6,>=4.21
32
32
  timezonefinder
33
33
  astral
34
34
  osmnx
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes