voxcity 0.6.26__py3-none-any.whl → 1.0.2__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.
- voxcity/__init__.py +10 -4
- voxcity/downloader/__init__.py +2 -1
- voxcity/downloader/gba.py +210 -0
- voxcity/downloader/gee.py +5 -1
- voxcity/downloader/mbfp.py +1 -1
- voxcity/downloader/oemj.py +80 -8
- voxcity/downloader/utils.py +73 -73
- voxcity/errors.py +30 -0
- voxcity/exporter/__init__.py +9 -1
- voxcity/exporter/cityles.py +129 -34
- voxcity/exporter/envimet.py +51 -26
- voxcity/exporter/magicavoxel.py +42 -5
- voxcity/exporter/netcdf.py +27 -0
- voxcity/exporter/obj.py +103 -28
- voxcity/generator/__init__.py +47 -0
- voxcity/generator/api.py +721 -0
- voxcity/generator/grids.py +381 -0
- voxcity/generator/io.py +94 -0
- voxcity/generator/pipeline.py +282 -0
- voxcity/generator/update.py +429 -0
- voxcity/generator/voxelizer.py +392 -0
- voxcity/geoprocessor/__init__.py +75 -6
- voxcity/geoprocessor/conversion.py +153 -0
- voxcity/geoprocessor/draw.py +1488 -1169
- voxcity/geoprocessor/heights.py +199 -0
- voxcity/geoprocessor/io.py +101 -0
- voxcity/geoprocessor/merge_utils.py +91 -0
- voxcity/geoprocessor/mesh.py +26 -10
- voxcity/geoprocessor/network.py +35 -6
- voxcity/geoprocessor/overlap.py +84 -0
- voxcity/geoprocessor/raster/__init__.py +82 -0
- voxcity/geoprocessor/raster/buildings.py +435 -0
- voxcity/geoprocessor/raster/canopy.py +258 -0
- voxcity/geoprocessor/raster/core.py +150 -0
- voxcity/geoprocessor/raster/export.py +93 -0
- voxcity/geoprocessor/raster/landcover.py +159 -0
- voxcity/geoprocessor/raster/raster.py +110 -0
- voxcity/geoprocessor/selection.py +85 -0
- voxcity/geoprocessor/utils.py +824 -820
- voxcity/models.py +113 -0
- voxcity/simulator/common/__init__.py +22 -0
- voxcity/simulator/common/geometry.py +98 -0
- voxcity/simulator/common/raytracing.py +450 -0
- voxcity/simulator/solar/__init__.py +66 -0
- voxcity/simulator/solar/integration.py +336 -0
- voxcity/simulator/solar/kernels.py +62 -0
- voxcity/simulator/solar/radiation.py +648 -0
- voxcity/simulator/solar/sky.py +668 -0
- voxcity/simulator/solar/temporal.py +792 -0
- voxcity/simulator/view.py +36 -2286
- voxcity/simulator/visibility/__init__.py +29 -0
- voxcity/simulator/visibility/landmark.py +392 -0
- voxcity/simulator/visibility/view.py +508 -0
- voxcity/utils/__init__.py +11 -0
- voxcity/utils/classes.py +194 -0
- voxcity/utils/lc.py +80 -39
- voxcity/utils/logging.py +61 -0
- voxcity/utils/orientation.py +51 -0
- voxcity/utils/shape.py +230 -0
- voxcity/utils/weather/__init__.py +26 -0
- voxcity/utils/weather/epw.py +146 -0
- voxcity/utils/weather/files.py +36 -0
- voxcity/utils/weather/onebuilding.py +486 -0
- voxcity/visualizer/__init__.py +24 -0
- voxcity/visualizer/builder.py +43 -0
- voxcity/visualizer/grids.py +141 -0
- voxcity/visualizer/maps.py +187 -0
- voxcity/visualizer/palette.py +228 -0
- voxcity/visualizer/renderer.py +1145 -0
- {voxcity-0.6.26.dist-info → voxcity-1.0.2.dist-info}/METADATA +162 -48
- voxcity-1.0.2.dist-info/RECORD +81 -0
- voxcity/generator.py +0 -1302
- voxcity/geoprocessor/grid.py +0 -1739
- voxcity/geoprocessor/polygon.py +0 -1344
- voxcity/simulator/solar.py +0 -2339
- voxcity/utils/visualization.py +0 -2849
- voxcity/utils/weather.py +0 -1038
- voxcity-0.6.26.dist-info/RECORD +0 -38
- {voxcity-0.6.26.dist-info → voxcity-1.0.2.dist-info}/WHEEL +0 -0
- {voxcity-0.6.26.dist-info → voxcity-1.0.2.dist-info}/licenses/AUTHORS.rst +0 -0
- {voxcity-0.6.26.dist-info → voxcity-1.0.2.dist-info}/licenses/LICENSE +0 -0
voxcity/exporter/obj.py
CHANGED
|
@@ -23,6 +23,13 @@ Dependencies:
|
|
|
23
23
|
- numpy: For array operations
|
|
24
24
|
- matplotlib: For colormap handling
|
|
25
25
|
- trimesh: For mesh operations
|
|
26
|
+
|
|
27
|
+
Orientation contract:
|
|
28
|
+
- Export functions assume input 2D grids are north_up (row 0 = north/top) with
|
|
29
|
+
columns increasing eastward (col 0 = west/left), and voxel arrays use
|
|
30
|
+
(row, col, z) = (north→south, west→east, ground→up).
|
|
31
|
+
- Internal flips may be applied to match OBJ coordinate conventions; these do
|
|
32
|
+
not change the semantic orientation of the data.
|
|
26
33
|
"""
|
|
27
34
|
|
|
28
35
|
import numpy as np
|
|
@@ -31,7 +38,7 @@ from numba import njit, prange
|
|
|
31
38
|
import matplotlib.pyplot as plt
|
|
32
39
|
import trimesh
|
|
33
40
|
import numpy as np
|
|
34
|
-
from ..
|
|
41
|
+
from ..visualizer import get_voxel_color_map
|
|
35
42
|
|
|
36
43
|
def convert_colormap_indices(original_map):
|
|
37
44
|
"""
|
|
@@ -266,7 +273,7 @@ def mesh_faces(mask, layer_index, axis, positive_direction, normal_idx, voxel_si
|
|
|
266
273
|
|
|
267
274
|
v += width
|
|
268
275
|
|
|
269
|
-
def export_obj(array, output_dir, file_name, voxel_size, voxel_color_map=None):
|
|
276
|
+
def export_obj(array, output_dir, file_name, voxel_size=None, voxel_color_map=None):
|
|
270
277
|
"""
|
|
271
278
|
Export a voxel array to OBJ format with materials and proper face orientations.
|
|
272
279
|
|
|
@@ -275,14 +282,14 @@ def export_obj(array, output_dir, file_name, voxel_size, voxel_color_map=None):
|
|
|
275
282
|
both OBJ and MTL files with all necessary components for rendering.
|
|
276
283
|
|
|
277
284
|
Args:
|
|
278
|
-
array (ndarray): 3D numpy array
|
|
285
|
+
array (ndarray | VoxCity): 3D numpy array of voxel values or a VoxCity instance.
|
|
279
286
|
Non-zero values indicate voxel presence and material type.
|
|
280
287
|
output_dir (str): Directory to save the OBJ and MTL files.
|
|
281
288
|
Will be created if it doesn't exist.
|
|
282
289
|
file_name (str): Base name for the output files.
|
|
283
290
|
Will be used for both .obj and .mtl files.
|
|
284
|
-
voxel_size (float): Size of each voxel in meters.
|
|
285
|
-
|
|
291
|
+
voxel_size (float | None): Size of each voxel in meters. If a VoxCity is provided,
|
|
292
|
+
this is inferred from the object and this parameter is ignored.
|
|
286
293
|
voxel_color_map (dict, optional): Dictionary mapping voxel values to RGB colors.
|
|
287
294
|
If None, uses default color map. Colors should be RGB lists (0-255).
|
|
288
295
|
|
|
@@ -307,6 +314,15 @@ def export_obj(array, output_dir, file_name, voxel_size, voxel_color_map=None):
|
|
|
307
314
|
- Transparency settings
|
|
308
315
|
- Illumination model definitions
|
|
309
316
|
"""
|
|
317
|
+
# Accept VoxCity instance as first argument
|
|
318
|
+
try:
|
|
319
|
+
from ..models import VoxCity as _VoxCity
|
|
320
|
+
if isinstance(array, _VoxCity):
|
|
321
|
+
voxel_size = float(array.voxels.meta.meshsize)
|
|
322
|
+
array = array.voxels.classes
|
|
323
|
+
except Exception:
|
|
324
|
+
pass
|
|
325
|
+
|
|
310
326
|
if voxel_color_map is None:
|
|
311
327
|
voxel_color_map = get_voxel_color_map()
|
|
312
328
|
|
|
@@ -681,6 +697,7 @@ def export_netcdf_to_obj(
|
|
|
681
697
|
classes_to_show=None,
|
|
682
698
|
voxel_color_scheme="default",
|
|
683
699
|
max_faces_warn=1_000_000,
|
|
700
|
+
export_vox_base=True,
|
|
684
701
|
):
|
|
685
702
|
"""
|
|
686
703
|
Export two OBJ/MTL files using the same local meter frame:
|
|
@@ -718,6 +735,8 @@ def export_netcdf_to_obj(
|
|
|
718
735
|
classes_to_show (set[int]|None): Optional subset of voxel classes to export; None -> all present (except 0).
|
|
719
736
|
voxel_color_scheme (str): Color scheme name passed to get_voxel_color_map.
|
|
720
737
|
max_faces_warn (int): Warn if a single class exceeds this many faces.
|
|
738
|
+
export_vox_base (bool): If False, skip exporting VoxCity OBJ/MTL; VoxCity input
|
|
739
|
+
is still used to define the shared coordinate system for scalar OBJ.
|
|
721
740
|
|
|
722
741
|
Returns:
|
|
723
742
|
dict: Paths of written files: keys 'vox_obj','vox_mtl','tm_obj','tm_mtl' (values may be None).
|
|
@@ -1345,27 +1364,28 @@ def export_netcdf_to_obj(
|
|
|
1345
1364
|
vox_meshes = {}
|
|
1346
1365
|
tm_meshes = {}
|
|
1347
1366
|
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1367
|
+
if export_vox_base:
|
|
1368
|
+
present = set(np.unique(Av_kji))
|
|
1369
|
+
present.discard(0)
|
|
1370
|
+
if classes_to_show is not None:
|
|
1371
|
+
present &= set(classes_to_show)
|
|
1372
|
+
present = sorted(present)
|
|
1373
|
+
|
|
1374
|
+
faces_total = 0
|
|
1375
|
+
voxel_color_map = get_voxel_color_map(color_scheme=voxel_color_scheme)
|
|
1376
|
+
for cls in present:
|
|
1377
|
+
mask = Av_kji == cls
|
|
1378
|
+
if not np.any(mask):
|
|
1379
|
+
continue
|
|
1380
|
+
rgb = voxel_color_map.get(int(cls), [200, 200, 200])
|
|
1381
|
+
if greedy_vox:
|
|
1382
|
+
m_cls, faces = make_voxel_mesh_uniform_color_greedy(mask, Xv_s, Yv_s, Zv_s, rgb=rgb, name=f"class_{int(cls)}")
|
|
1383
|
+
else:
|
|
1384
|
+
m_cls, faces = make_voxel_mesh_uniform_color(mask, Xv_s, Yv_s, Zv_s, rgb=rgb, name=f"class_{int(cls)}")
|
|
1385
|
+
if m_cls is not None:
|
|
1386
|
+
vox_meshes[f"voxclass_{int(cls)}"] = m_cls
|
|
1387
|
+
faces_total += faces
|
|
1388
|
+
print(f"[VoxCity] total voxel faces: {faces_total:,}")
|
|
1369
1389
|
|
|
1370
1390
|
iso_meshes = build_tm_isosurfaces_regular_grid(
|
|
1371
1391
|
A_scalar=A_s,
|
|
@@ -1390,7 +1410,7 @@ def export_netcdf_to_obj(
|
|
|
1390
1410
|
|
|
1391
1411
|
os.makedirs(output_dir, exist_ok=True)
|
|
1392
1412
|
obj_vox = mtl_vox = obj_tm = mtl_tm = None
|
|
1393
|
-
if vox_meshes:
|
|
1413
|
+
if export_vox_base and vox_meshes:
|
|
1394
1414
|
obj_vox, mtl_vox = save_obj_with_mtl_and_normals(vox_meshes, output_dir, vox_base_filename)
|
|
1395
1415
|
if tm_meshes:
|
|
1396
1416
|
obj_tm, mtl_tm = save_obj_with_mtl_and_normals(tm_meshes, output_dir, tm_base_filename)
|
|
@@ -1403,4 +1423,59 @@ def export_netcdf_to_obj(
|
|
|
1403
1423
|
print(f"Scalar Iso OBJ: {obj_tm}")
|
|
1404
1424
|
print(f"Scalar Iso MTL: {mtl_tm}")
|
|
1405
1425
|
|
|
1406
|
-
return {"vox_obj": obj_vox, "vox_mtl": mtl_vox, "tm_obj": obj_tm, "tm_mtl": mtl_tm}
|
|
1426
|
+
return {"vox_obj": obj_vox, "vox_mtl": mtl_vox, "tm_obj": obj_tm, "tm_mtl": mtl_tm}
|
|
1427
|
+
|
|
1428
|
+
|
|
1429
|
+
class OBJExporter:
|
|
1430
|
+
"""Exporter that writes mesh collections or trimesh dicts to OBJ/MTL.
|
|
1431
|
+
|
|
1432
|
+
Accepts either a MeshCollection (voxcity.models) or dict[str, trimesh.Trimesh].
|
|
1433
|
+
"""
|
|
1434
|
+
|
|
1435
|
+
def export(self, obj, output_directory: str, base_filename: str, **kwargs):
|
|
1436
|
+
os.makedirs(output_directory, exist_ok=True)
|
|
1437
|
+
# VoxCity or MeshCollection path
|
|
1438
|
+
try:
|
|
1439
|
+
from ..models import MeshCollection, VoxCity
|
|
1440
|
+
if isinstance(obj, VoxCity):
|
|
1441
|
+
# Delegate to file-writing path using voxels
|
|
1442
|
+
export_obj(
|
|
1443
|
+
array=obj.voxels.classes,
|
|
1444
|
+
output_dir=output_directory,
|
|
1445
|
+
file_name=base_filename,
|
|
1446
|
+
voxel_size=float(obj.voxels.meta.meshsize),
|
|
1447
|
+
voxel_color_map=kwargs.get("voxel_color_map"),
|
|
1448
|
+
)
|
|
1449
|
+
return os.path.join(output_directory, f"{base_filename}.obj")
|
|
1450
|
+
is_collection = isinstance(obj, MeshCollection)
|
|
1451
|
+
except Exception:
|
|
1452
|
+
is_collection = False
|
|
1453
|
+
|
|
1454
|
+
if is_collection:
|
|
1455
|
+
tm = {}
|
|
1456
|
+
for key, mm in obj.items.items():
|
|
1457
|
+
if getattr(mm, "vertices", None) is None or getattr(mm, "faces", None) is None:
|
|
1458
|
+
continue
|
|
1459
|
+
if mm.vertices.size == 0 or mm.faces.size == 0:
|
|
1460
|
+
continue
|
|
1461
|
+
tri = trimesh.Trimesh(vertices=mm.vertices, faces=mm.faces, process=False)
|
|
1462
|
+
if getattr(mm, "colors", None) is not None:
|
|
1463
|
+
tri.visual.face_colors = mm.colors
|
|
1464
|
+
tm[key] = tri
|
|
1465
|
+
if not tm:
|
|
1466
|
+
return None
|
|
1467
|
+
combined = trimesh.util.concatenate(list(tm.values()))
|
|
1468
|
+
out = os.path.join(output_directory, f"{base_filename}.obj")
|
|
1469
|
+
combined.export(out)
|
|
1470
|
+
return out
|
|
1471
|
+
|
|
1472
|
+
# Dict[str, trimesh.Trimesh] path
|
|
1473
|
+
if isinstance(obj, dict) and all(hasattr(m, "vertices") for m in obj.values()):
|
|
1474
|
+
if not obj:
|
|
1475
|
+
return None
|
|
1476
|
+
combined = trimesh.util.concatenate(list(obj.values()))
|
|
1477
|
+
out = os.path.join(output_directory, f"{base_filename}.obj")
|
|
1478
|
+
combined.export(out)
|
|
1479
|
+
return out
|
|
1480
|
+
|
|
1481
|
+
raise TypeError("OBJExporter.export expects MeshCollection or dict[str, trimesh.Trimesh]")
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""VoxCity generator subpackage.
|
|
2
|
+
|
|
3
|
+
This package organizes the voxel city generation pipeline into focused modules
|
|
4
|
+
while preserving the original public API under `voxcity.generator`.
|
|
5
|
+
|
|
6
|
+
Orientation contract:
|
|
7
|
+
- All 2D grids use north_up orientation (row 0 = north/top; columns increase eastward).
|
|
8
|
+
- 3D indexing follows (row, col, z) = (north→south, west→east, ground→up).
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .api import get_voxcity, get_voxcity_CityGML, auto_select_data_sources
|
|
12
|
+
from .grids import (
|
|
13
|
+
get_land_cover_grid,
|
|
14
|
+
get_building_height_grid,
|
|
15
|
+
get_canopy_height_grid,
|
|
16
|
+
get_dem_grid,
|
|
17
|
+
)
|
|
18
|
+
from .voxelizer import (
|
|
19
|
+
Voxelizer,
|
|
20
|
+
GROUND_CODE,
|
|
21
|
+
TREE_CODE,
|
|
22
|
+
BUILDING_CODE,
|
|
23
|
+
)
|
|
24
|
+
from .pipeline import VoxCityPipeline
|
|
25
|
+
from .io import save_voxcity, load_voxcity
|
|
26
|
+
from .update import update_voxcity, regenerate_voxels
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"get_voxcity",
|
|
30
|
+
"auto_select_data_sources",
|
|
31
|
+
"get_voxcity_CityGML",
|
|
32
|
+
"get_land_cover_grid",
|
|
33
|
+
"get_building_height_grid",
|
|
34
|
+
"get_canopy_height_grid",
|
|
35
|
+
"get_dem_grid",
|
|
36
|
+
"Voxelizer",
|
|
37
|
+
"GROUND_CODE",
|
|
38
|
+
"TREE_CODE",
|
|
39
|
+
"BUILDING_CODE",
|
|
40
|
+
"VoxCityPipeline",
|
|
41
|
+
"save_voxcity",
|
|
42
|
+
"load_voxcity",
|
|
43
|
+
"update_voxcity",
|
|
44
|
+
"regenerate_voxels",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|