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

@@ -147,8 +147,8 @@ def prepare_grids(building_height_grid_ori, building_id_grid_ori, canopy_height_
147
147
  veg_translation_dict = {
148
148
  1: '', # Bareland
149
149
  2: '0200XX', # Rangeland
150
- 3: '', # Shrub
151
- 4: '', # Moss and lichen
150
+ 3: '0200H1', # Shrub
151
+ 4: '0200XX', # Moss and lichen
152
152
  5: '0200XX', # Agriculture land
153
153
  6: '', # Tree
154
154
  7: '0200XX', # Wet land
@@ -171,7 +171,7 @@ def prepare_grids(building_height_grid_ori, building_id_grid_ori, canopy_height_
171
171
  11: '0200PG', # Developed space
172
172
  12: '0200ST', # Road
173
173
  13: '000000', # Building
174
- 14: '0200SD', # No Data
174
+ 14: '000000', # No Data
175
175
  }
176
176
  land_cover_mat_grid = translate_array(land_cover_grid, mat_translation_dict)
177
177
 
@@ -590,7 +590,7 @@ def split_vertices_manual(mesh):
590
590
  out_mesh = trimesh.util.concatenate(new_meshes)
591
591
  return out_mesh
592
592
 
593
- def save_obj_from_colored_mesh(meshes, output_path, base_filename):
593
+ def save_obj_from_colored_mesh(meshes, output_path, base_filename, max_materials=None):
594
594
  """
595
595
  Save a collection of colored meshes as OBJ and MTL files with material support.
596
596
 
@@ -620,6 +620,12 @@ def save_obj_from_colored_mesh(meshes, output_path, base_filename):
620
620
  - {base_filename}.obj : The main geometry file
621
621
  - {base_filename}.mtl : The material definitions file
622
622
 
623
+ max_materials : int, optional
624
+ Maximum number of materials/colors to create. If specified and the number
625
+ of unique colors exceeds this limit, colors will be quantized using
626
+ k-means clustering to reduce them to the specified number.
627
+ If None (default), all unique colors are preserved as separate materials.
628
+
623
629
  Returns
624
630
  -------
625
631
  tuple
@@ -644,6 +650,11 @@ def save_obj_from_colored_mesh(meshes, output_path, base_filename):
644
650
  ... meshes, 'output/models', 'city'
645
651
  ... )
646
652
 
653
+ Limiting materials to 5:
654
+ >>> obj_path, mtl_path = save_obj_from_colored_mesh(
655
+ ... meshes, 'output/models', 'city', max_materials=5
656
+ ... )
657
+
647
658
  Notes
648
659
  -----
649
660
  - Creates unique materials for each distinct face color
@@ -653,6 +664,9 @@ def save_obj_from_colored_mesh(meshes, output_path, base_filename):
653
664
  - Vertices are written in OBJ's 1-based indexing format
654
665
  - Faces are grouped by material for efficient rendering
655
666
  - The MTL file is automatically referenced in the OBJ file
667
+ - If max_materials is specified, k-means clustering is used for color quantization
668
+ - Color quantization preserves the overall color distribution while reducing material count
669
+ - Requires scikit-learn package when max_materials is specified (install with: pip install scikit-learn)
656
670
 
657
671
  File Format Details
658
672
  -----------------
@@ -674,8 +688,38 @@ def save_obj_from_colored_mesh(meshes, output_path, base_filename):
674
688
  combined_mesh = trimesh.util.concatenate(list(meshes.values()))
675
689
  combined_mesh = split_vertices_manual(combined_mesh)
676
690
 
677
- # Create unique materials for each unique face color
691
+ # Get face colors
678
692
  face_colors = combined_mesh.visual.face_colors
693
+
694
+ # Apply color quantization if max_materials is specified
695
+ if max_materials is not None:
696
+ try:
697
+ from sklearn.cluster import KMeans
698
+ except ImportError:
699
+ raise ImportError("scikit-learn is required for color quantization. "
700
+ "Install it with: pip install scikit-learn")
701
+
702
+ # Prepare colors for clustering (use only RGB, not alpha)
703
+ colors_rgb = face_colors[:, :3].astype(float)
704
+
705
+ # Perform k-means clustering
706
+ kmeans = KMeans(n_clusters=max_materials, random_state=42, n_init=10)
707
+ color_labels = kmeans.fit_predict(colors_rgb)
708
+
709
+ # Get the cluster centers as the new colors
710
+ quantized_colors_rgb = kmeans.cluster_centers_.astype(np.uint8)
711
+
712
+ # Create new face colors with quantized RGB and original alpha
713
+ quantized_face_colors = np.zeros_like(face_colors)
714
+ # Assign each face to its nearest cluster center
715
+ quantized_face_colors[:, :3] = quantized_colors_rgb[color_labels]
716
+ quantized_face_colors[:, 3] = face_colors[:, 3] # Preserve original alpha
717
+
718
+ # Update the mesh with quantized colors
719
+ combined_mesh.visual.face_colors = quantized_face_colors
720
+ face_colors = quantized_face_colors
721
+
722
+ # Create unique materials for each unique face color
679
723
  unique_colors = np.unique(face_colors, axis=0)
680
724
 
681
725
  # Write MTL file
@@ -2504,7 +2504,8 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
2504
2504
  if save_obj:
2505
2505
  output_directory = kwargs.get('output_directory', 'output')
2506
2506
  output_file_name = kwargs.get('output_file_name', 'voxcity_mesh')
2507
- obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name)
2507
+ max_materials = kwargs.get('max_materials', 20)
2508
+ obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name, max_materials=max_materials)
2508
2509
  print(f"Saved mesh files to:\n {obj_path}\n {mtl_path}")
2509
2510
 
2510
2511
  if show_views:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voxcity
3
- Version: 0.5.24
3
+ Version: 0.5.26
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>
@@ -52,6 +52,7 @@ Requires-Dist: trimesh
52
52
  Requires-Dist: pyvista
53
53
  Requires-Dist: IPython
54
54
  Requires-Dist: lxml
55
+ Requires-Dist: scikit-learn
55
56
  Provides-Extra: dev
56
57
  Requires-Dist: coverage; extra == "dev"
57
58
  Requires-Dist: mypy; extra == "dev"
@@ -10,13 +10,13 @@ voxcity/downloader/osm.py,sha256=kXiUedT7dwPOQ_4nxXptfeqNb5RKTIsQLG5km7Q8kKk,416
10
10
  voxcity/downloader/overture.py,sha256=4YG2DMwUSSyZKUw_o8cGhMmAkPJon82aPqOFBvrre-Y,11987
11
11
  voxcity/downloader/utils.py,sha256=tz6wt4B9BhEOyvoF5OYXlr8rUd5cBEDedWL3j__oT70,3099
12
12
  voxcity/exporter/__init__.py,sha256=2htEXMrq6knO8JMROtfz-0HFGddsAJkXqNUB2_MhqvM,70
13
- voxcity/exporter/envimet.py,sha256=un8Ay55XttGWFkFzkxLymHoRuA-UJkowQ4bMCjod_Jk,31093
13
+ voxcity/exporter/envimet.py,sha256=Sh7s1JdQ6SgT_L2Xd_c4gtEGWK2hTS87bccaoIqik-s,31105
14
14
  voxcity/exporter/magicavoxel.py,sha256=SfGEgTZRlossKx3Xrv9d3iKSX-HmfQJEL9lZHgWMDX4,12782
15
15
  voxcity/exporter/obj.py,sha256=h1_aInpemcsu96fSTwjKMqX2VZAFYbZbElWd4M1ogyI,27973
16
16
  voxcity/geoprocessor/__init__.py,sha256=JzPVhhttxBWvaZ0IGX2w7OWL5bCo_TIvpHefWeNXruA,133
17
17
  voxcity/geoprocessor/draw.py,sha256=lcBtf4VAWXjk46cInPdSBeiLCTESdxzLOayr_0YOnus,22184
18
18
  voxcity/geoprocessor/grid.py,sha256=Uy8Oz4iL7vnPMqp_qtp4dQrs00yd2L9CSIPmF5KnbtI,63961
19
- voxcity/geoprocessor/mesh.py,sha256=MK2rkBfVyLQ_nkhgLMa6R3_SxYFE2-MHaSiRP6bJ7no,28584
19
+ voxcity/geoprocessor/mesh.py,sha256=ElqAE2MA8KZs7yD7B1P88XYmryC6F9nkkP6cXv7FzIk,30777
20
20
  voxcity/geoprocessor/network.py,sha256=YynqR0nq_NUra_cQ3Z_56KxfRia1b6-hIzGCj3QT-wE,25137
21
21
  voxcity/geoprocessor/polygon.py,sha256=-LonxtW5du3UP61oygqtDJl6GGsCYnUuN9KYwl1UFdc,53707
22
22
  voxcity/geoprocessor/utils.py,sha256=DVg3EMRytLQLEQeXLvNgjt1Ynoa689EsD-To-14xgSQ,30369
@@ -27,11 +27,11 @@ voxcity/simulator/view.py,sha256=F2c-XFdRN811_RJvsznRjbUu5Uv7C6iniezsUMD-Olw,594
27
27
  voxcity/utils/__init__.py,sha256=Q-NYCqYnAAaF80KuNwpqIjbE7Ec3Gr4y_khMLIMhJrg,68
28
28
  voxcity/utils/lc.py,sha256=h2yOWLUIrrummkyMyhRK5VbyrsPtslS0MJov_y0WGIQ,18925
29
29
  voxcity/utils/material.py,sha256=H8K8Lq4wBL6dQtgj7esUW2U6wLCOTeOtelkTDJoRgMo,10007
30
- voxcity/utils/visualization.py,sha256=DWdkViMhwzoKf7qBD47uti5Qw7wYsI041jFePZm63KQ,112832
30
+ voxcity/utils/visualization.py,sha256=T-jKrCA4UMm93p-1O678RWM7e99iE0_Lj4wD07efcwI,112918
31
31
  voxcity/utils/weather.py,sha256=2Jtg-rIVJcsTtiKE-KuDnhIqS1-MSS16_zFRzj6zmu4,36435
32
- voxcity-0.5.24.dist-info/licenses/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
33
- voxcity-0.5.24.dist-info/licenses/LICENSE,sha256=s_jE1Df1nTPL4A_5GCGic5Zwex0CVaPKcAmSilxJPPE,1089
34
- voxcity-0.5.24.dist-info/METADATA,sha256=ADjf1Z75Z0BVCKAo2s-QbD2C890hbOkHpMx9FJNv5w4,26695
35
- voxcity-0.5.24.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- voxcity-0.5.24.dist-info/top_level.txt,sha256=00b2U-LKfDllt6RL1R33MXie5MvxzUFye0NGD96t_8I,8
37
- voxcity-0.5.24.dist-info/RECORD,,
32
+ voxcity-0.5.26.dist-info/licenses/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
33
+ voxcity-0.5.26.dist-info/licenses/LICENSE,sha256=s_jE1Df1nTPL4A_5GCGic5Zwex0CVaPKcAmSilxJPPE,1089
34
+ voxcity-0.5.26.dist-info/METADATA,sha256=EdbTSCTEB_oW67EK3cHHmBB5V7LrlDECoEsB5ATGgVI,26724
35
+ voxcity-0.5.26.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ voxcity-0.5.26.dist-info/top_level.txt,sha256=00b2U-LKfDllt6RL1R33MXie5MvxzUFye0NGD96t_8I,8
37
+ voxcity-0.5.26.dist-info/RECORD,,