voxcity 0.5.9__tar.gz → 0.5.10__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.9 → voxcity-0.5.10}/PKG-INFO +5 -1
  2. {voxcity-0.5.9 → voxcity-0.5.10}/README.md +4 -0
  3. {voxcity-0.5.9 → voxcity-0.5.10}/pyproject.toml +1 -1
  4. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/mesh.py +105 -1
  5. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/utils/visualization.py +62 -116
  6. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity.egg-info/PKG-INFO +5 -1
  7. {voxcity-0.5.9 → voxcity-0.5.10}/AUTHORS.rst +0 -0
  8. {voxcity-0.5.9 → voxcity-0.5.10}/CONTRIBUTING.rst +0 -0
  9. {voxcity-0.5.9 → voxcity-0.5.10}/HISTORY.rst +0 -0
  10. {voxcity-0.5.9 → voxcity-0.5.10}/LICENSE +0 -0
  11. {voxcity-0.5.9 → voxcity-0.5.10}/MANIFEST.in +0 -0
  12. {voxcity-0.5.9 → voxcity-0.5.10}/docs/Makefile +0 -0
  13. {voxcity-0.5.9 → voxcity-0.5.10}/docs/archive/README.rst +0 -0
  14. {voxcity-0.5.9 → voxcity-0.5.10}/docs/authors.rst +0 -0
  15. {voxcity-0.5.9 → voxcity-0.5.10}/docs/conf.py +0 -0
  16. {voxcity-0.5.9 → voxcity-0.5.10}/docs/index.rst +0 -0
  17. {voxcity-0.5.9 → voxcity-0.5.10}/docs/make.bat +0 -0
  18. {voxcity-0.5.9 → voxcity-0.5.10}/setup.cfg +0 -0
  19. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/__init__.py +0 -0
  20. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/__init__.py +0 -0
  21. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/citygml.py +0 -0
  22. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/eubucco.py +0 -0
  23. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/gee.py +0 -0
  24. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/mbfp.py +0 -0
  25. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/oemj.py +0 -0
  26. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/omt.py +0 -0
  27. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/osm.py +0 -0
  28. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/overture.py +0 -0
  29. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/downloader/utils.py +0 -0
  30. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/exporter/__init_.py +0 -0
  31. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/exporter/envimet.py +0 -0
  32. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/exporter/magicavoxel.py +0 -0
  33. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/exporter/obj.py +0 -0
  34. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/generator.py +0 -0
  35. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/__init_.py +0 -0
  36. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/draw.py +0 -0
  37. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/grid.py +0 -0
  38. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/network.py +0 -0
  39. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/polygon.py +0 -0
  40. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/geoprocessor/utils.py +0 -0
  41. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/simulator/__init_.py +0 -0
  42. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/simulator/solar.py +0 -0
  43. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/simulator/utils.py +0 -0
  44. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/simulator/view.py +0 -0
  45. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/utils/__init_.py +0 -0
  46. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/utils/lc.py +0 -0
  47. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/utils/material.py +0 -0
  48. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity/utils/weather.py +0 -0
  49. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity.egg-info/SOURCES.txt +0 -0
  50. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity.egg-info/dependency_links.txt +0 -0
  51. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity.egg-info/requires.txt +0 -0
  52. {voxcity-0.5.9 → voxcity-0.5.10}/src/voxcity.egg-info/top_level.txt +0 -0
  53. {voxcity-0.5.9 → voxcity-0.5.10}/tests/__init__.py +0 -0
  54. {voxcity-0.5.9 → voxcity-0.5.10}/tests/voxelcity.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voxcity
3
- Version: 0.5.9
3
+ Version: 0.5.10
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>
@@ -72,6 +72,10 @@ Dynamic: license-file
72
72
  Tutorial preview: <a href="https://colab.research.google.com/drive/1Lofd3RawKMr6QuUsamGaF48u2MN0hfrP?usp=sharing">[Google Colab]</a>
73
73
  </p>
74
74
 
75
+ <p align="center">
76
+ <img src="https://raw.githubusercontent.com/kunifujiwara/VoxCity/main/images/logo.png" alt="Voxcity logo" width="800">
77
+ </p>
78
+
75
79
  # VoxCity
76
80
 
77
81
  **voxcity** is a Python package that provides a seamless solution for grid-based 3D city model generation and urban simulation for cities worldwide. VoxCity's generator module automatically downloads building heights, tree canopy heights, land cover, and terrain elevation within a specified target area, and voxelizes buildings, trees, land cover, and terrain to generate an integrated voxel city model. The simulator module enables users to conduct environmental simulations, including solar radiation and view index analyses. Users can export the generated models using several file formats compatible with external software, such as ENVI-met (INX), Blender, and Rhino (OBJ). Try it out using the [Google Colab Demo](https://colab.research.google.com/drive/1Lofd3RawKMr6QuUsamGaF48u2MN0hfrP?usp=sharing) or your local environment.
@@ -9,6 +9,10 @@
9
9
  Tutorial preview: <a href="https://colab.research.google.com/drive/1Lofd3RawKMr6QuUsamGaF48u2MN0hfrP?usp=sharing">[Google Colab]</a>
10
10
  </p>
11
11
 
12
+ <p align="center">
13
+ <img src="https://raw.githubusercontent.com/kunifujiwara/VoxCity/main/images/logo.png" alt="Voxcity logo" width="800">
14
+ </p>
15
+
12
16
  # VoxCity
13
17
 
14
18
  **voxcity** is a Python package that provides a seamless solution for grid-based 3D city model generation and urban simulation for cities worldwide. VoxCity's generator module automatically downloads building heights, tree canopy heights, land cover, and terrain elevation within a specified target area, and voxelizes buildings, trees, land cover, and terrain to generate an integrated voxel city model. The simulator module enables users to conduct environmental simulations, including solar radiation and view index analyses. Users can export the generated models using several file formats compatible with external software, such as ENVI-met (INX), Blender, and Rhino (OBJ). Try it out using the [Google Colab Demo](https://colab.research.google.com/drive/1Lofd3RawKMr6QuUsamGaF48u2MN0hfrP?usp=sharing) or your local environment.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "voxcity"
3
- version = "0.5.9"
3
+ version = "0.5.10"
4
4
  requires-python = ">=3.10,<3.13"
5
5
  classifiers = [
6
6
  "Programming Language :: Python :: 3.10",
@@ -1,4 +1,5 @@
1
1
  import numpy as np
2
+ import os
2
3
  import trimesh
3
4
  import matplotlib.colors as mcolors
4
5
  import matplotlib.cm as cm
@@ -304,4 +305,107 @@ def export_meshes(meshes, output_directory, base_filename):
304
305
  # Export individual meshes as STL
305
306
  for class_id, mesh in meshes.items():
306
307
  # Convert class_id to a string for filename
307
- mesh.export(f"{output_directory}/{base_filename}_{class_id}.stl")
308
+ mesh.export(f"{output_directory}/{base_filename}_{class_id}.stl")
309
+
310
+ def split_vertices_manual(mesh):
311
+ """
312
+ Imitate trimesh's split_vertices() by giving each face its own copy of vertices.
313
+ This ensures every face is truly disconnected, preventing smooth shading in Rhino.
314
+ """
315
+ new_meshes = []
316
+
317
+ # For each face, build a small, one-face mesh
318
+ for face_idx, face in enumerate(mesh.faces):
319
+ face_coords = mesh.vertices[face]
320
+
321
+ # Create mini-mesh without colors first
322
+ mini_mesh = trimesh.Trimesh(
323
+ vertices=face_coords,
324
+ faces=[[0, 1, 2]],
325
+ process=False # skip merging/cleaning
326
+ )
327
+
328
+ # If the mesh has per-face colors, set the face color properly
329
+ if (mesh.visual.face_colors is not None
330
+ and len(mesh.visual.face_colors) == len(mesh.faces)):
331
+ # Create a visual object with the face color (for one face)
332
+ face_color = mesh.visual.face_colors[face_idx]
333
+ color_visual = trimesh.visual.ColorVisuals(
334
+ mesh=mini_mesh,
335
+ face_colors=np.array([face_color]), # One face, one color
336
+ vertex_colors=None
337
+ )
338
+ mini_mesh.visual = color_visual
339
+
340
+ new_meshes.append(mini_mesh)
341
+
342
+ # Concatenate all the single-face meshes
343
+ out_mesh = trimesh.util.concatenate(new_meshes)
344
+ return out_mesh
345
+
346
+ def save_obj_from_colored_mesh(meshes, output_path, base_filename):
347
+ """
348
+ Save colored meshes as OBJ and MTL files.
349
+
350
+ Parameters
351
+ ----------
352
+ meshes : dict
353
+ Dictionary of trimesh.Trimesh objects with face colors.
354
+ output_path : str
355
+ Directory path where to save the files.
356
+ base_filename : str
357
+ Base name for the output files (without extension).
358
+
359
+ Returns
360
+ -------
361
+ tuple
362
+ Paths to the saved (obj_file, mtl_file).
363
+ """
364
+
365
+ os.makedirs(output_path, exist_ok=True)
366
+ obj_path = os.path.join(output_path, f"{base_filename}.obj")
367
+ mtl_path = os.path.join(output_path, f"{base_filename}.mtl")
368
+
369
+ # Combine all meshes
370
+ combined_mesh = trimesh.util.concatenate(list(meshes.values()))
371
+ combined_mesh = split_vertices_manual(combined_mesh)
372
+
373
+ # Create unique materials for each unique face color
374
+ face_colors = combined_mesh.visual.face_colors
375
+ unique_colors = np.unique(face_colors, axis=0)
376
+
377
+ # Write MTL file
378
+ with open(mtl_path, 'w') as mtl_file:
379
+ for i, color in enumerate(unique_colors):
380
+ material_name = f'material_{i}'
381
+ mtl_file.write(f'newmtl {material_name}\n')
382
+ # Convert RGBA to RGB float values
383
+ rgb = color[:3].astype(float) / 255.0
384
+ mtl_file.write(f'Kd {rgb[0]:.6f} {rgb[1]:.6f} {rgb[2]:.6f}\n')
385
+ mtl_file.write(f'd {color[3]/255.0:.6f}\n\n') # Alpha value
386
+
387
+ # Create material groups based on face colors
388
+ color_to_material = {tuple(c): f'material_{i}' for i, c in enumerate(unique_colors)}
389
+
390
+ # Write OBJ file
391
+ with open(obj_path, 'w') as obj_file:
392
+ obj_file.write(f'mtllib {os.path.basename(mtl_path)}\n')
393
+
394
+ # Write vertices
395
+ for vertex in combined_mesh.vertices:
396
+ obj_file.write(f'v {vertex[0]:.6f} {vertex[1]:.6f} {vertex[2]:.6f}\n')
397
+
398
+ # Write faces grouped by material
399
+ current_material = None
400
+ for face_idx, face in enumerate(combined_mesh.faces):
401
+ face_color = tuple(face_colors[face_idx])
402
+ material_name = color_to_material[face_color]
403
+
404
+ if material_name != current_material:
405
+ obj_file.write(f'usemtl {material_name}\n')
406
+ current_material = material_name
407
+
408
+ # OBJ indices are 1-based
409
+ obj_file.write(f'f {face[0]+1} {face[1]+1} {face[2]+1}\n')
410
+
411
+ return obj_path, mtl_path
@@ -41,7 +41,8 @@ from ..geoprocessor.mesh import (
41
41
  create_voxel_mesh,
42
42
  create_sim_surface_mesh,
43
43
  create_city_meshes,
44
- export_meshes
44
+ export_meshes,
45
+ save_obj_from_colored_mesh
45
46
  )
46
47
  # from ..exporter.obj import save_obj_from_colored_mesh
47
48
  from .material import get_material_dict
@@ -1531,6 +1532,7 @@ def visualize_voxcity_multi_view(voxel_array, meshsize, **kwargs):
1531
1532
  projection_type = kwargs.get("projection_type", "perspective")
1532
1533
  distance_factor = kwargs.get("distance_factor", 1.0)
1533
1534
  save_obj = kwargs.get("save_obj", False)
1535
+ show_views = kwargs.get("show_views", True)
1534
1536
 
1535
1537
  # Create meshes
1536
1538
  print("Creating voxel meshes...")
@@ -1567,21 +1569,22 @@ def visualize_voxcity_multi_view(voxel_array, meshsize, **kwargs):
1567
1569
  os.makedirs(output_directory, exist_ok=True)
1568
1570
  export_meshes(meshes, output_directory, base_filename)
1569
1571
 
1570
- # Create and save multiple views
1571
- print("Creating multiple views...")
1572
- # Create output directory if it doesn't exist
1573
- os.makedirs(output_directory, exist_ok=True)
1574
- image_files = create_multi_view_scene(meshes, output_directory=output_directory, projection_type=projection_type, distance_factor=distance_factor)
1575
-
1576
- # Display each view separately
1577
- for view_name, img_file in image_files:
1578
- plt.figure(figsize=(12, 8))
1579
- img = plt.imread(img_file)
1580
- plt.imshow(img)
1581
- plt.title(view_name.replace('_', ' ').title(), pad=20)
1582
- plt.axis('off')
1583
- plt.show()
1584
- plt.close()
1572
+ if show_views:
1573
+ # Create and save multiple views
1574
+ print("Creating multiple views...")
1575
+ # Create output directory if it doesn't exist
1576
+ os.makedirs(output_directory, exist_ok=True)
1577
+ image_files = create_multi_view_scene(meshes, output_directory=output_directory, projection_type=projection_type, distance_factor=distance_factor)
1578
+
1579
+ # Display each view separately
1580
+ for view_name, img_file in image_files:
1581
+ plt.figure(figsize=(12, 8))
1582
+ img = plt.imread(img_file)
1583
+ plt.imshow(img)
1584
+ plt.title(view_name.replace('_', ' ').title(), pad=20)
1585
+ plt.axis('off')
1586
+ plt.show()
1587
+ plt.close()
1585
1588
 
1586
1589
  # After creating the meshes and before visualization
1587
1590
  if save_obj:
@@ -1633,6 +1636,8 @@ def visualize_voxcity_multi_view_with_multiple_sim_grids(voxel_array, meshsize,
1633
1636
  dem_grid_ori = kwargs.get("dem_grid", None)
1634
1637
  projection_type = kwargs.get("projection_type", "perspective")
1635
1638
  distance_factor = kwargs.get("distance_factor", 1.0)
1639
+ show_views = kwargs.get("show_views", True)
1640
+ save_obj = kwargs.get("save_obj", False)
1636
1641
 
1637
1642
  if dem_grid_ori is not None:
1638
1643
  dem_grid = dem_grid_ori - np.min(dem_grid_ori)
@@ -1681,27 +1686,33 @@ def visualize_voxcity_multi_view_with_multiple_sim_grids(voxel_array, meshsize,
1681
1686
  os.makedirs(output_directory, exist_ok=True)
1682
1687
  export_meshes(meshes, output_directory, base_filename)
1683
1688
 
1684
- # Create and save multiple views
1685
- print("Creating multiple views...")
1686
- os.makedirs(output_directory, exist_ok=True)
1687
- image_files = create_multi_view_scene(
1688
- meshes,
1689
- output_directory=output_directory,
1690
- projection_type=projection_type,
1691
- distance_factor=distance_factor
1692
- )
1689
+ if show_views:
1690
+ # Create and save multiple views
1691
+ print("Creating multiple views...")
1692
+ os.makedirs(output_directory, exist_ok=True)
1693
+ image_files = create_multi_view_scene(
1694
+ meshes,
1695
+ output_directory=output_directory,
1696
+ projection_type=projection_type,
1697
+ distance_factor=distance_factor
1698
+ )
1693
1699
 
1694
- # Display each view separately
1695
- for view_name, img_file in image_files:
1696
- plt.figure(figsize=(12, 8))
1697
- img = plt.imread(img_file)
1698
- plt.imshow(img)
1699
- plt.title(view_name.replace('_', ' ').title(), pad=20)
1700
- plt.axis('off')
1701
- plt.show()
1702
- plt.close()
1700
+ # Display each view separately
1701
+ for view_name, img_file in image_files:
1702
+ plt.figure(figsize=(12, 8))
1703
+ img = plt.imread(img_file)
1704
+ plt.imshow(img)
1705
+ plt.title(view_name.replace('_', ' ').title(), pad=20)
1706
+ plt.axis('off')
1707
+ plt.show()
1708
+ plt.close()
1703
1709
 
1704
- return meshes
1710
+ # After creating the meshes and before visualization
1711
+ if save_obj:
1712
+ output_directory = kwargs.get('output_directory', 'output')
1713
+ output_file_name = kwargs.get('output_file_name', 'voxcity_mesh')
1714
+ obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name)
1715
+ print(f"Saved mesh files to:\n {obj_path}\n {mtl_path}")
1705
1716
 
1706
1717
  def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None, **kwargs):
1707
1718
  """
@@ -1778,6 +1789,7 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
1778
1789
  colorbar_title = kwargs.get("colorbar_title", "")
1779
1790
  value_name = kwargs.get("value_name", None)
1780
1791
  nan_color = kwargs.get("nan_color", "gray")
1792
+ show_views = kwargs.get("show_views", True)
1781
1793
  save_obj = kwargs.get("save_obj", False)
1782
1794
 
1783
1795
  if value_name is None:
@@ -1900,28 +1912,28 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
1900
1912
  print("Creating multiple views...")
1901
1913
  # Create output directory if it doesn't exist
1902
1914
  os.makedirs(output_directory, exist_ok=True)
1903
- image_files = create_multi_view_scene(meshes, output_directory=output_directory,
1915
+
1916
+ if show_views:
1917
+ image_files = create_multi_view_scene(meshes, output_directory=output_directory,
1904
1918
  projection_type=projection_type,
1905
1919
  distance_factor=distance_factor)
1906
1920
 
1907
- # Display each view separately
1908
- for view_name, img_file in image_files:
1909
- plt.figure(figsize=(24, 16))
1910
- img = plt.imread(img_file)
1911
- plt.imshow(img)
1912
- plt.title(view_name.replace('_', ' ').title(), pad=20)
1913
- plt.axis('off')
1914
- plt.show()
1915
- plt.close()
1921
+ # Display each view separately
1922
+ for view_name, img_file in image_files:
1923
+ plt.figure(figsize=(24, 16))
1924
+ img = plt.imread(img_file)
1925
+ plt.imshow(img)
1926
+ plt.title(view_name.replace('_', ' ').title(), pad=20)
1927
+ plt.axis('off')
1928
+ plt.show()
1929
+ plt.close()
1916
1930
 
1917
1931
  # After creating the meshes and before visualization
1918
1932
  if save_obj:
1919
1933
  output_directory = kwargs.get('output_directory', 'output')
1920
1934
  output_file_name = kwargs.get('output_file_name', 'voxcity_mesh')
1921
1935
  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
-
1924
- return image_files
1936
+ print(f"Saved mesh files to:\n {obj_path}\n {mtl_path}")
1925
1937
 
1926
1938
  def visualize_building_sim_results(voxel_array, meshsize, building_sim_mesh, **kwargs):
1927
1939
  """
@@ -1969,75 +1981,9 @@ def visualize_building_sim_results(voxel_array, meshsize, building_sim_mesh, **k
1969
1981
  kwargs["colorbar_title"] = pretty_name
1970
1982
 
1971
1983
  # Call the more general visualization function
1972
- return visualize_voxcity_with_sim_meshes(
1984
+ visualize_voxcity_with_sim_meshes(
1973
1985
  voxel_array,
1974
1986
  meshsize,
1975
1987
  custom_meshes=custom_meshes,
1976
1988
  **kwargs
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
1989
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voxcity
3
- Version: 0.5.9
3
+ Version: 0.5.10
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>
@@ -72,6 +72,10 @@ Dynamic: license-file
72
72
  Tutorial preview: <a href="https://colab.research.google.com/drive/1Lofd3RawKMr6QuUsamGaF48u2MN0hfrP?usp=sharing">[Google Colab]</a>
73
73
  </p>
74
74
 
75
+ <p align="center">
76
+ <img src="https://raw.githubusercontent.com/kunifujiwara/VoxCity/main/images/logo.png" alt="Voxcity logo" width="800">
77
+ </p>
78
+
75
79
  # VoxCity
76
80
 
77
81
  **voxcity** is a Python package that provides a seamless solution for grid-based 3D city model generation and urban simulation for cities worldwide. VoxCity's generator module automatically downloads building heights, tree canopy heights, land cover, and terrain elevation within a specified target area, and voxelizes buildings, trees, land cover, and terrain to generate an integrated voxel city model. The simulator module enables users to conduct environmental simulations, including solar radiation and view index analyses. Users can export the generated models using several file formats compatible with external software, such as ENVI-met (INX), Blender, and Rhino (OBJ). Try it out using the [Google Colab Demo](https://colab.research.google.com/drive/1Lofd3RawKMr6QuUsamGaF48u2MN0hfrP?usp=sharing) or your local environment.
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