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

voxcity/utils/lc.py CHANGED
@@ -183,114 +183,35 @@ def get_land_cover_classes(source):
183
183
 
184
184
  def convert_land_cover(input_array, land_cover_source='Urbanwatch'):
185
185
  """
186
- Convert land cover classification from source-specific indices to standardized indices.
187
-
188
- This function maps land cover classes from various data sources to a standardized
189
- classification system. Each source has different class definitions and indices,
190
- so this conversion enables consistent processing across different data sources.
191
-
192
- Args:
193
- input_array (numpy.ndarray): Input array with source-specific land cover indices
194
- land_cover_source (str): Name of the source land cover classification system
195
- Default is 'Urbanwatch'
196
-
197
- Returns:
198
- numpy.ndarray: Array with standardized land cover indices
199
-
200
- Standardized Classification System:
201
- 0: Bareland
202
- 1: Rangeland
203
- 2: Shrub
204
- 3: Agriculture land
205
- 4: Tree
206
- 5: Moss and lichen
207
- 6: Wet land
208
- 7: Mangrove
209
- 8: Water
210
- 9: Snow and ice
211
- 10: Developed space
212
- 11: Road
213
- 12: Building
214
- 13: No Data
186
+ Optimized version using direct numpy array indexing instead of np.vectorize.
187
+ This is 10-100x faster than the original.
215
188
  """
216
-
189
+ # Define mappings
217
190
  if land_cover_source == 'Urbanwatch':
218
- # Define the mapping from Urbanwatch to new standardized classes
219
- convert_dict = {
220
- 0: 12, # Building
221
- 1: 11, # Road
222
- 2: 10, # Parking Lot -> Developed space
223
- 3: 4, # Tree Canopy -> Tree
224
- 4: 1, # Grass/Shrub -> Rangeland
225
- 5: 3, # Agriculture -> Agriculture land
226
- 6: 8, # Water
227
- 7: 0, # Barren -> Bareland
228
- 8: 13, # Unknown -> No Data
229
- 9: 8 # Sea -> Water
230
- }
191
+ mapping = {0: 12, 1: 11, 2: 10, 3: 4, 4: 1, 5: 3, 6: 8, 7: 0, 8: 13, 9: 8}
231
192
  elif land_cover_source == 'ESA WorldCover':
232
- # ESA WorldCover to standardized mapping
233
- convert_dict = {
234
- 0: 4, # Trees -> Tree
235
- 1: 2, # Shrubland -> Shrub
236
- 2: 1, # Grassland -> Rangeland
237
- 3: 3, # Cropland -> Agriculture land
238
- 4: 10, # Built-up -> Developed space
239
- 5: 0, # Barren / sparse vegetation -> Bareland
240
- 6: 9, # Snow and ice
241
- 7: 8, # Open water -> Water
242
- 8: 6, # Herbaceous wetland -> Wet land
243
- 9: 7, # Mangroves
244
- 10: 5 # Moss and lichen
245
- }
193
+ mapping = {0: 4, 1: 2, 2: 1, 3: 3, 4: 10, 5: 0, 6: 9, 7: 8, 8: 6, 9: 7, 10: 5}
246
194
  elif land_cover_source == "ESRI 10m Annual Land Cover":
247
- # ESRI 10m to standardized mapping
248
- convert_dict = {
249
- 0: 13, # No Data
250
- 1: 8, # Water
251
- 2: 4, # Trees -> Tree
252
- 3: 1, # Grass -> Rangeland
253
- 4: 6, # Flooded Vegetation -> Wet land
254
- 5: 3, # Crops -> Agriculture land
255
- 6: 2, # Scrub/Shrub -> Shrub
256
- 7: 10, # Built Area -> Developed space
257
- 8: 0, # Bare Ground -> Bareland
258
- 9: 9, # Snow/Ice
259
- 10: 13 # Clouds -> No Data
260
- }
195
+ mapping = {0: 13, 1: 8, 2: 4, 3: 1, 4: 6, 5: 3, 6: 2, 7: 10, 8: 0, 9: 9, 10: 13}
261
196
  elif land_cover_source == "Dynamic World V1":
262
- # Dynamic World to standardized mapping
263
- convert_dict = {
264
- 0: 8, # Water
265
- 1: 4, # Trees -> Tree
266
- 2: 1, # Grass -> Rangeland
267
- 3: 6, # Flooded Vegetation -> Wet land
268
- 4: 3, # Crops -> Agriculture land
269
- 5: 2, # Shrub and Scrub -> Shrub
270
- 6: 10, # Built -> Developed space
271
- 7: 0, # Bare -> Bareland
272
- 8: 9 # Snow and Ice
273
- }
197
+ mapping = {0: 8, 1: 4, 2: 1, 3: 6, 4: 3, 5: 2, 6: 10, 7: 0, 8: 9}
274
198
  elif land_cover_source == "OpenEarthMapJapan":
275
- # OpenEarthMapJapan to standardized mapping
276
- convert_dict = {
277
- 0: 0, # Bareland
278
- 1: 1, # Rangeland
279
- 2: 10, # Developed space
280
- 3: 11, # Road
281
- 4: 4, # Tree
282
- 5: 8, # Water
283
- 6: 3, # Agriculture land
284
- 7: 12, # Building
285
- }
286
-
287
- # Create a vectorized function for the conversion
288
- vectorized_convert = np.vectorize(lambda x: convert_dict.get(x, x))
199
+ mapping = {0: 0, 1: 1, 2: 10, 3: 11, 4: 4, 5: 8, 6: 3, 7: 12}
200
+ else:
201
+ # If unknown source, return as-is
202
+ return input_array.copy()
203
+
204
+ # Create a full mapping array for all possible values (0-255 for uint8)
205
+ max_val = max(max(mapping.keys()), input_array.max()) + 1
206
+ lookup = np.arange(max_val, dtype=input_array.dtype)
289
207
 
290
- # Apply the conversion to the input array
291
- converted_array = vectorized_convert(input_array)
208
+ # Apply the mapping
209
+ for old_val, new_val in mapping.items():
210
+ if old_val < max_val:
211
+ lookup[old_val] = new_val
292
212
 
293
- return converted_array
213
+ # Use fancy indexing for fast conversion
214
+ return lookup[input_array]
294
215
 
295
216
  def get_class_priority(source):
296
217
  """
@@ -45,6 +45,7 @@ import trimesh
45
45
  import pyvista as pv
46
46
  from IPython.display import display
47
47
  import os
48
+ import sys
48
49
 
49
50
  # Import utility functions for land cover classification
50
51
  from .lc import get_land_cover_classes
@@ -1839,13 +1840,34 @@ def create_multi_view_scene(meshes, output_directory="output", projection_type="
1839
1840
  >>> views = create_multi_view_scene(meshes, "renders/", "orthographic", 1.5)
1840
1841
  >>> print(f"Generated {len(views)} views")
1841
1842
  """
1842
- # Compute overall bounding box across all meshes
1843
- vertices_list = [mesh.vertices for mesh in meshes.values()]
1844
- all_vertices = np.vstack(vertices_list)
1845
- bbox = np.array([
1846
- [all_vertices[:, 0].min(), all_vertices[:, 1].min(), all_vertices[:, 2].min()],
1847
- [all_vertices[:, 0].max(), all_vertices[:, 1].max(), all_vertices[:, 2].max()]
1848
- ])
1843
+ # Precompute PyVista meshes once to avoid repeated conversion per view
1844
+ pv_meshes = {}
1845
+ for class_id, mesh in meshes.items():
1846
+ if mesh is None or len(mesh.vertices) == 0 or len(mesh.faces) == 0:
1847
+ continue
1848
+ # PyVista expects a faces array where each face is prefixed by its vertex count (3 for triangles)
1849
+ faces = np.hstack([[3, *face] for face in mesh.faces])
1850
+ pv_mesh = pv.PolyData(mesh.vertices, faces)
1851
+ # Attach per-cell colors if provided
1852
+ colors = getattr(mesh.visual, 'face_colors', None)
1853
+ if colors is not None:
1854
+ colors = np.asarray(colors)
1855
+ if colors.size and colors.max() > 1:
1856
+ colors = colors / 255.0
1857
+ pv_mesh.cell_data['colors'] = colors
1858
+ pv_meshes[class_id] = pv_mesh
1859
+
1860
+ # Compute overall bounding box across all meshes without stacking
1861
+ min_xyz = np.array([np.inf, np.inf, np.inf], dtype=float)
1862
+ max_xyz = np.array([-np.inf, -np.inf, -np.inf], dtype=float)
1863
+ for mesh in meshes.values():
1864
+ if mesh is None or len(mesh.vertices) == 0:
1865
+ continue
1866
+ v = mesh.vertices
1867
+ # update mins and maxs
1868
+ min_xyz = np.minimum(min_xyz, v.min(axis=0))
1869
+ max_xyz = np.maximum(max_xyz, v.max(axis=0))
1870
+ bbox = np.vstack([min_xyz, max_xyz])
1849
1871
 
1850
1872
  # Compute the center and diagonal of the bounding box
1851
1873
  center = (bbox[1] + bbox[0]) / 2
@@ -1897,21 +1919,14 @@ def create_multi_view_scene(meshes, output_directory="output", projection_type="
1897
1919
  elif projection_type.lower() != "perspective":
1898
1920
  print(f"Warning: Unknown projection_type '{projection_type}'. Using perspective projection.")
1899
1921
 
1900
- # Add each mesh to the scene
1901
- for class_id, mesh in meshes.items():
1902
- vertices = mesh.vertices
1903
- faces = np.hstack([[3, *face] for face in mesh.faces])
1904
- pv_mesh = pv.PolyData(vertices, faces)
1905
-
1906
- if hasattr(mesh.visual, 'face_colors'):
1907
- colors = mesh.visual.face_colors
1908
- if colors.max() > 1:
1909
- colors = colors / 255.0
1910
- pv_mesh.cell_data['colors'] = colors
1911
-
1912
- plotter.add_mesh(pv_mesh,
1913
- rgb=True,
1914
- scalars='colors' if hasattr(mesh.visual, 'face_colors') else None)
1922
+ # Add each precomputed mesh to the scene
1923
+ for class_id, pv_mesh in pv_meshes.items():
1924
+ has_colors = 'colors' in pv_mesh.cell_data
1925
+ plotter.add_mesh(
1926
+ pv_mesh,
1927
+ rgb=True,
1928
+ scalars='colors' if has_colors else None
1929
+ )
1915
1930
 
1916
1931
  # Set camera position for this view
1917
1932
  plotter.camera_position = camera_pos
@@ -2350,8 +2365,11 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
2350
2365
  - visualize_voxcity_multi_view(): Basic voxel visualization without custom meshes
2351
2366
  - create_multi_view_scene(): Lower-level rendering function
2352
2367
  """
2353
- os.system('Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &')
2354
- os.environ['DISPLAY'] = ':99'
2368
+ # Setup offscreen rendering only when needed and supported
2369
+ if sys.platform.startswith('linux'):
2370
+ if 'DISPLAY' not in os.environ:
2371
+ os.system('Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &')
2372
+ os.environ['DISPLAY'] = ':99'
2355
2373
 
2356
2374
  # Configure PyVista settings
2357
2375
  pv.set_plot_theme('document')
@@ -2381,13 +2399,25 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
2381
2399
  nan_color = kwargs.get("nan_color", "gray")
2382
2400
  show_views = kwargs.get("show_views", True)
2383
2401
  save_obj = kwargs.get("save_obj", False)
2402
+ include_classes = kwargs.get("include_classes", None)
2403
+ exclude_classes = kwargs.get("exclude_classes", None)
2404
+ copy_custom_mesh = kwargs.get("copy_custom_mesh", False)
2384
2405
 
2385
2406
  if value_name is None:
2386
2407
  print("Set value_name")
2387
2408
 
2388
2409
  # Create meshes from voxel data
2389
2410
  print("Creating voxel meshes...")
2390
- meshes = create_city_meshes(voxel_array, vox_dict, meshsize=meshsize)
2411
+ # Skip generating voxel meshes for classes that will be replaced by custom meshes
2412
+ if exclude_classes is None and custom_meshes is not None:
2413
+ exclude_classes = list(custom_meshes.keys())
2414
+ meshes = create_city_meshes(
2415
+ voxel_array,
2416
+ vox_dict,
2417
+ meshsize=meshsize,
2418
+ include_classes=include_classes,
2419
+ exclude_classes=exclude_classes,
2420
+ )
2391
2421
 
2392
2422
  # Replace specific voxel class meshes with custom simulation meshes
2393
2423
  if custom_meshes is not None:
@@ -2399,18 +2429,23 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
2399
2429
  import matplotlib.colors as mcolors
2400
2430
 
2401
2431
  # Get values from metadata
2402
- values = custom_mesh.metadata[value_name]
2432
+ values = np.asarray(custom_mesh.metadata[value_name])
2403
2433
 
2404
2434
  # Set vmin/vmax if not provided
2405
- local_vmin = vmin if vmin is not None else np.nanmin(values[~np.isnan(values)])
2406
- local_vmax = vmax if vmax is not None else np.nanmax(values[~np.isnan(values)])
2435
+ finite_mask = np.isfinite(values)
2436
+ if not np.any(finite_mask):
2437
+ local_vmin = 0.0 if vmin is None else vmin
2438
+ local_vmax = 1.0 if vmax is None else vmax
2439
+ else:
2440
+ local_vmin = vmin if vmin is not None else float(np.nanmin(values[finite_mask]))
2441
+ local_vmax = vmax if vmax is not None else float(np.nanmax(values[finite_mask]))
2407
2442
 
2408
2443
  # Create colors
2409
2444
  cmap = cm.get_cmap(cmap_name)
2410
2445
  norm = mcolors.Normalize(vmin=local_vmin, vmax=local_vmax)
2411
2446
 
2412
2447
  # Handle NaN values with custom color
2413
- face_colors = np.zeros((len(values), 4))
2448
+ face_colors = np.zeros((len(values), 4), dtype=float)
2414
2449
 
2415
2450
  # Convert string color to RGBA if needed
2416
2451
  if isinstance(nan_color, str):
@@ -2421,25 +2456,25 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
2421
2456
  nan_rgba = np.array(nan_color)
2422
2457
 
2423
2458
  # Apply colors: NaN values get nan_color, others get colormap colors
2424
- nan_mask = np.isnan(values)
2459
+ nan_mask = ~finite_mask
2425
2460
  face_colors[~nan_mask] = cmap(norm(values[~nan_mask]))
2426
2461
  face_colors[nan_mask] = nan_rgba
2427
2462
 
2428
2463
  # Create a copy with colors
2429
- vis_mesh = custom_mesh.copy()
2464
+ vis_mesh = custom_mesh.copy() if copy_custom_mesh else custom_mesh
2430
2465
  vis_mesh.visual.face_colors = face_colors
2431
2466
 
2432
- # Prepare the colormap and create colorbar
2433
- norm = mcolors.Normalize(vmin=local_vmin, vmax=local_vmax)
2434
- scalar_map = cm.ScalarMappable(norm=norm, cmap=cmap_name)
2435
-
2436
- # Create a figure and axis for the colorbar but don't display
2437
- fig, ax = plt.subplots(figsize=(6, 1))
2438
- cbar = plt.colorbar(scalar_map, cax=ax, orientation='horizontal')
2439
- if colorbar_title:
2440
- cbar.set_label(colorbar_title)
2441
- plt.tight_layout()
2442
- plt.show()
2467
+ # Prepare the colormap and create colorbar if views will be shown
2468
+ if show_views:
2469
+ norm = mcolors.Normalize(vmin=local_vmin, vmax=local_vmax)
2470
+ scalar_map = cm.ScalarMappable(norm=norm, cmap=cmap_name)
2471
+ fig, ax = plt.subplots(figsize=(6, 1))
2472
+ cbar = plt.colorbar(scalar_map, cax=ax, orientation='horizontal')
2473
+ if colorbar_title:
2474
+ cbar.set_label(colorbar_title)
2475
+ plt.tight_layout()
2476
+ plt.show()
2477
+ plt.close(fig)
2443
2478
 
2444
2479
  if class_id in meshes:
2445
2480
  print(f"Replacing voxel class {class_id} with colored custom simulation mesh")
@@ -2504,8 +2539,12 @@ def visualize_voxcity_with_sim_meshes(voxel_array, meshsize, custom_meshes=None,
2504
2539
  if save_obj:
2505
2540
  output_directory = kwargs.get('output_directory', 'output')
2506
2541
  output_file_name = kwargs.get('output_file_name', 'voxcity_mesh')
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)
2542
+ # Default: do NOT quantize to preserve exact face colors like in images
2543
+ max_materials = kwargs.get('max_materials', None)
2544
+ if max_materials is None:
2545
+ obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name)
2546
+ else:
2547
+ obj_path, mtl_path = save_obj_from_colored_mesh(meshes, output_directory, output_file_name, max_materials=max_materials)
2509
2548
  print(f"Saved mesh files to:\n {obj_path}\n {mtl_path}")
2510
2549
 
2511
2550
  if show_views:
@@ -2559,6 +2598,13 @@ def visualize_building_sim_results(voxel_array, meshsize, building_sim_mesh, **k
2559
2598
 
2560
2599
  # Create custom meshes dictionary with the building simulation mesh
2561
2600
  custom_meshes = {building_class_id: building_sim_mesh}
2601
+
2602
+ # Performance: skip voxel mesh generation for building class by default
2603
+ if "include_classes" not in kwargs and "exclude_classes" not in kwargs:
2604
+ kwargs["exclude_classes"] = [building_class_id]
2605
+
2606
+ # Memory-safety: do not duplicate the provided mesh unless requested
2607
+ kwargs.setdefault("copy_custom_mesh", False)
2562
2608
 
2563
2609
  # Add colorbar title if not provided
2564
2610
  if "colorbar_title" not in kwargs:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: voxcity
3
- Version: 0.5.31
3
+ Version: 0.6.0
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>
@@ -1,5 +1,5 @@
1
1
  voxcity/__init__.py,sha256=el9v3gfybHOF_GUYPeSOqN0-vCrTW0eU1mcvi0sEfeU,252
2
- voxcity/generator.py,sha256=mEggM4FxE7LChTPCspAQmJlAoUz1PVcbaUfY11cMfzQ,54176
2
+ voxcity/generator.py,sha256=mnwlmfFr-23_ZytPSeU-5SpHklO30w5b8NbNOQLemik,54983
3
3
  voxcity/downloader/__init__.py,sha256=o_T_EU7hZLGyXxX9wVWn1x-OAa3ThGYdnpgB1_2v3AE,151
4
4
  voxcity/downloader/citygml.py,sha256=jVeHCLlJTf7k55OQGX0lZGQAngz_DD2V5TldSqRFlvc,36024
5
5
  voxcity/downloader/eubucco.py,sha256=ln1YNaaOgJfxNfCtVbYaMm775-bUvpAA_LDv60_i22w,17875
@@ -16,23 +16,23 @@ voxcity/exporter/magicavoxel.py,sha256=SfGEgTZRlossKx3Xrv9d3iKSX-HmfQJEL9lZHgWMD
16
16
  voxcity/exporter/obj.py,sha256=h1_aInpemcsu96fSTwjKMqX2VZAFYbZbElWd4M1ogyI,27973
17
17
  voxcity/geoprocessor/__init__.py,sha256=JzPVhhttxBWvaZ0IGX2w7OWL5bCo_TIvpHefWeNXruA,133
18
18
  voxcity/geoprocessor/draw.py,sha256=avXQwbGQWG3ZPPI8mwy0YN0K_aG4NMBdXI0vDg7yad0,35837
19
- voxcity/geoprocessor/grid.py,sha256=lhELyznlk4Jt7vnd0uOpMCLPCjrYQjX7qtQq-xHkYE4,64161
20
- voxcity/geoprocessor/mesh.py,sha256=ElqAE2MA8KZs7yD7B1P88XYmryC6F9nkkP6cXv7FzIk,30777
19
+ voxcity/geoprocessor/grid.py,sha256=uIfEE-2rfT274QKN9qnyXu3E2nCwaUHiuxHra2iclZM,71186
20
+ voxcity/geoprocessor/mesh.py,sha256=-r_3EGL3PZr-QngMOXrwKllYA4o0moDj0-bZvU0mlOo,31237
21
21
  voxcity/geoprocessor/network.py,sha256=YynqR0nq_NUra_cQ3Z_56KxfRia1b6-hIzGCj3QT-wE,25137
22
- voxcity/geoprocessor/polygon.py,sha256=-obeI0KprySwwRXT4j3vs_xsk5sd9p9YzYdle1jJbCE,59845
22
+ voxcity/geoprocessor/polygon.py,sha256=DfzXf6R-qoWXEZv1z1aHCVfr-DCuCFw6lieQT5cNHPA,61188
23
23
  voxcity/geoprocessor/utils.py,sha256=DVg3EMRytLQLEQeXLvNgjt1Ynoa689EsD-To-14xgSQ,30369
24
24
  voxcity/simulator/__init__.py,sha256=APdkcdaovj0v_RPOaA4SBvFUKT2RM7Hxuuz3Sux4gCo,65
25
- voxcity/simulator/solar.py,sha256=t7zOSxBObQMuWvQSiHN9AViJstzfkXqAg3BpIppn0KA,82694
25
+ voxcity/simulator/solar.py,sha256=h_jVPjJ8wKl8z8Tx0EcVJBzNRKSr8WXBL9bVCgJpyZE,101788
26
26
  voxcity/simulator/utils.py,sha256=sEYBB2-hLJxTiXQps1_-Fi7t1HN3-1OPOvBCWtgIisA,130
27
- voxcity/simulator/view.py,sha256=Xnr-NoKGyO9D02bF104ZKzNhsnjNCkzc-B1yPtDEqpQ,75898
27
+ voxcity/simulator/view.py,sha256=W6QtoSnLSQPEPtTy5B6BB5l0UGQ5Vs-Bt1ISNZKdGTI,92020
28
28
  voxcity/utils/__init__.py,sha256=Q-NYCqYnAAaF80KuNwpqIjbE7Ec3Gr4y_khMLIMhJrg,68
29
- voxcity/utils/lc.py,sha256=h2yOWLUIrrummkyMyhRK5VbyrsPtslS0MJov_y0WGIQ,18925
29
+ voxcity/utils/lc.py,sha256=722Gz3lPbgAp0mmTZ-g-QKBbAnbxrcgaYwb1sa7q8Sk,16189
30
30
  voxcity/utils/material.py,sha256=H8K8Lq4wBL6dQtgj7esUW2U6wLCOTeOtelkTDJoRgMo,10007
31
- voxcity/utils/visualization.py,sha256=OvfZ67dg3rHr1LD0xTPYK_LBtoZwjJe-H0Nhcwkd_HA,112919
31
+ voxcity/utils/visualization.py,sha256=Hk31pVOZ3q8K0QaudAMSqKUquF1Hd1Hug8626D4JJ74,115143
32
32
  voxcity/utils/weather.py,sha256=2Jtg-rIVJcsTtiKE-KuDnhIqS1-MSS16_zFRzj6zmu4,36435
33
- voxcity-0.5.31.dist-info/licenses/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
34
- voxcity-0.5.31.dist-info/licenses/LICENSE,sha256=s_jE1Df1nTPL4A_5GCGic5Zwex0CVaPKcAmSilxJPPE,1089
35
- voxcity-0.5.31.dist-info/METADATA,sha256=1edKOnYTIW2Io9E0QuRMhEXmEqHEZnT4-lrShCMNUvM,26724
36
- voxcity-0.5.31.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
- voxcity-0.5.31.dist-info/top_level.txt,sha256=00b2U-LKfDllt6RL1R33MXie5MvxzUFye0NGD96t_8I,8
38
- voxcity-0.5.31.dist-info/RECORD,,
33
+ voxcity-0.6.0.dist-info/licenses/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
34
+ voxcity-0.6.0.dist-info/licenses/LICENSE,sha256=s_jE1Df1nTPL4A_5GCGic5Zwex0CVaPKcAmSilxJPPE,1089
35
+ voxcity-0.6.0.dist-info/METADATA,sha256=C-1oyOYd7anMYVqYfTIH_zJWNDqpRL5v5bvcYgsYPkI,26723
36
+ voxcity-0.6.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
37
+ voxcity-0.6.0.dist-info/top_level.txt,sha256=00b2U-LKfDllt6RL1R33MXie5MvxzUFye0NGD96t_8I,8
38
+ voxcity-0.6.0.dist-info/RECORD,,