voxcity 0.6.22__tar.gz → 0.6.23__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.
- {voxcity-0.6.22 → voxcity-0.6.23}/PKG-INFO +1 -1
- {voxcity-0.6.22 → voxcity-0.6.23}/pyproject.toml +1 -1
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/utils/visualization.py +189 -1
- {voxcity-0.6.22 → voxcity-0.6.23}/AUTHORS.rst +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/LICENSE +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/README.md +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/__init__.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/__init__.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/citygml.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/eubucco.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/gee.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/mbfp.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/oemj.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/osm.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/overture.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/downloader/utils.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/exporter/__init__.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/exporter/cityles.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/exporter/envimet.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/exporter/magicavoxel.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/exporter/netcdf.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/exporter/obj.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/generator.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/__init__.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/draw.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/grid.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/mesh.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/network.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/polygon.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/geoprocessor/utils.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/simulator/__init__.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/simulator/solar.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/simulator/utils.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/simulator/view.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/utils/__init__.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/utils/lc.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/utils/material.py +0 -0
- {voxcity-0.6.22 → voxcity-0.6.23}/src/voxcity/utils/weather.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "voxcity"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.23"
|
|
4
4
|
description = "voxcity is an easy and one-stop tool to output 3d city models for microclimate simulation by integrating multiple geospatial open-data"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -2386,6 +2386,188 @@ def _rgb_tuple_to_plotly_color(rgb_tuple):
|
|
|
2386
2386
|
return "rgb(128,128,128)"
|
|
2387
2387
|
|
|
2388
2388
|
|
|
2389
|
+
def visualize_building_sim_results_plotly(
|
|
2390
|
+
voxel_array,
|
|
2391
|
+
meshsize,
|
|
2392
|
+
building_sim_mesh,
|
|
2393
|
+
**kwargs
|
|
2394
|
+
):
|
|
2395
|
+
"""
|
|
2396
|
+
Interactive Plotly visualization of voxels with an overlaid building simulation mesh.
|
|
2397
|
+
|
|
2398
|
+
This function reuses visualize_voxcity_plotly to render voxel cubes and adds a
|
|
2399
|
+
Plotly Mesh3d trace for a provided building simulation mesh (e.g., SVF, temperature).
|
|
2400
|
+
|
|
2401
|
+
Parameters (kwargs)
|
|
2402
|
+
-------------------
|
|
2403
|
+
classes : list[int] or None
|
|
2404
|
+
Classes to render for voxel cubes. Default: all non-zero classes present.
|
|
2405
|
+
voxel_color_map : str or dict
|
|
2406
|
+
Scheme name understood by get_voxel_color_map or explicit mapping {class_id: [R,G,B]}.
|
|
2407
|
+
downsample : int or None
|
|
2408
|
+
Stride for voxel cubes. 1 means no downsampling.
|
|
2409
|
+
cubes_opacity : float
|
|
2410
|
+
Opacity for voxel cubes (default 0.95 for buildings, 0.6 for others via function's per-class logic; here default 0.9).
|
|
2411
|
+
title : str
|
|
2412
|
+
Figure title.
|
|
2413
|
+
width, height : int
|
|
2414
|
+
Figure size.
|
|
2415
|
+
value_name : str
|
|
2416
|
+
Metadata field name on building_sim_mesh storing per-face values (default 'svf_values').
|
|
2417
|
+
colormap : str
|
|
2418
|
+
Matplotlib colormap name for the simulation values (default 'viridis').
|
|
2419
|
+
vmin, vmax : float or None
|
|
2420
|
+
Value range for color mapping. If None, computed from finite values.
|
|
2421
|
+
nan_color : str
|
|
2422
|
+
Color name for NaN values (default 'gray').
|
|
2423
|
+
building_opacity : float
|
|
2424
|
+
Opacity for the simulation mesh (default 1.0).
|
|
2425
|
+
shaded : bool
|
|
2426
|
+
If True, apply lighting-based shading to the simulation mesh. Default False (unlit colors).
|
|
2427
|
+
render_voxel_buildings : bool
|
|
2428
|
+
If True, also render voxel buildings (-3) from voxcity_grid. Default False (hide voxel buildings
|
|
2429
|
+
so only simulation mesh buildings are visible).
|
|
2430
|
+
show : bool, return_fig : bool
|
|
2431
|
+
Standard display controls.
|
|
2432
|
+
"""
|
|
2433
|
+
classes = kwargs.get('classes')
|
|
2434
|
+
voxel_color_map = kwargs.get('voxel_color_map', 'default')
|
|
2435
|
+
downsample = kwargs.get('downsample')
|
|
2436
|
+
title = kwargs.get('title', None)
|
|
2437
|
+
width = kwargs.get('width', 1000)
|
|
2438
|
+
height = kwargs.get('height', 800)
|
|
2439
|
+
cubes_opacity = kwargs.get('cubes_opacity', 0.9)
|
|
2440
|
+
show = kwargs.get('show', True)
|
|
2441
|
+
return_fig = kwargs.get('return_fig', False)
|
|
2442
|
+
render_voxel_buildings = kwargs.get('render_voxel_buildings', False)
|
|
2443
|
+
|
|
2444
|
+
# Determine classes for voxel cubes and exclude buildings (-3) by default
|
|
2445
|
+
if classes is None:
|
|
2446
|
+
classes_all = np.unique(voxel_array[voxel_array != 0]).tolist()
|
|
2447
|
+
else:
|
|
2448
|
+
classes_all = list(classes)
|
|
2449
|
+
classes_cubes = classes_all if render_voxel_buildings else [c for c in classes_all if int(c) != -3]
|
|
2450
|
+
|
|
2451
|
+
# Render voxel cubes background (or blank scene if nothing to render)
|
|
2452
|
+
if len(classes_cubes) > 0:
|
|
2453
|
+
fig = visualize_voxcity_plotly(
|
|
2454
|
+
voxel_array,
|
|
2455
|
+
meshsize,
|
|
2456
|
+
classes=classes_cubes,
|
|
2457
|
+
voxel_color_map=voxel_color_map,
|
|
2458
|
+
opacity=cubes_opacity,
|
|
2459
|
+
downsample=downsample,
|
|
2460
|
+
title=title or "Building Simulation (Plotly)",
|
|
2461
|
+
width=width,
|
|
2462
|
+
height=height,
|
|
2463
|
+
show=False,
|
|
2464
|
+
return_fig=True,
|
|
2465
|
+
)
|
|
2466
|
+
else:
|
|
2467
|
+
fig = go.Figure()
|
|
2468
|
+
fig.update_layout(
|
|
2469
|
+
title=title or "Building Simulation (Plotly)",
|
|
2470
|
+
width=width,
|
|
2471
|
+
height=height,
|
|
2472
|
+
scene=dict(
|
|
2473
|
+
xaxis_title="X (m)",
|
|
2474
|
+
yaxis_title="Y (m)",
|
|
2475
|
+
zaxis_title="Z (m)",
|
|
2476
|
+
aspectmode="data",
|
|
2477
|
+
camera=dict(eye=dict(x=1.6, y=1.6, z=1.0)),
|
|
2478
|
+
)
|
|
2479
|
+
)
|
|
2480
|
+
|
|
2481
|
+
# Nothing to overlay
|
|
2482
|
+
if building_sim_mesh is None or getattr(building_sim_mesh, 'vertices', None) is None:
|
|
2483
|
+
if show:
|
|
2484
|
+
fig.show()
|
|
2485
|
+
if return_fig:
|
|
2486
|
+
return fig
|
|
2487
|
+
return None
|
|
2488
|
+
|
|
2489
|
+
# Extract geometry
|
|
2490
|
+
V = np.asarray(building_sim_mesh.vertices)
|
|
2491
|
+
F = np.asarray(building_sim_mesh.faces)
|
|
2492
|
+
values = None
|
|
2493
|
+
value_name = kwargs.get('value_name', 'svf_values')
|
|
2494
|
+
if hasattr(building_sim_mesh, 'metadata') and isinstance(building_sim_mesh.metadata, dict):
|
|
2495
|
+
values = building_sim_mesh.metadata.get(value_name)
|
|
2496
|
+
if values is not None:
|
|
2497
|
+
values = np.asarray(values)
|
|
2498
|
+
|
|
2499
|
+
# Compute per-face scalar to avoid color interpolation across edges
|
|
2500
|
+
face_vals = None
|
|
2501
|
+
if values is not None and values.size == len(F):
|
|
2502
|
+
# Already per-face
|
|
2503
|
+
face_vals = values.astype(float)
|
|
2504
|
+
elif values is not None and values.size == len(V):
|
|
2505
|
+
# Average the three vertex values per face
|
|
2506
|
+
vals_v = values.astype(float)
|
|
2507
|
+
face_vals = np.nanmean(vals_v[F], axis=1)
|
|
2508
|
+
|
|
2509
|
+
# Map to colors
|
|
2510
|
+
cmap_name = kwargs.get('colormap', 'viridis')
|
|
2511
|
+
vmin = kwargs.get('vmin')
|
|
2512
|
+
vmax = kwargs.get('vmax')
|
|
2513
|
+
nan_color = kwargs.get('nan_color', 'gray')
|
|
2514
|
+
building_opacity = kwargs.get('building_opacity', 1.0)
|
|
2515
|
+
|
|
2516
|
+
facecolor = None
|
|
2517
|
+
if face_vals is not None:
|
|
2518
|
+
# Compute range
|
|
2519
|
+
finite = np.isfinite(face_vals)
|
|
2520
|
+
if vmin is None:
|
|
2521
|
+
vmin = float(np.nanmin(face_vals[finite])) if np.any(finite) else 0.0
|
|
2522
|
+
if vmax is None:
|
|
2523
|
+
vmax = float(np.nanmax(face_vals[finite])) if np.any(finite) else 1.0
|
|
2524
|
+
norm = mcolors.Normalize(vmin=vmin, vmax=vmax)
|
|
2525
|
+
cmap = cm.get_cmap(cmap_name)
|
|
2526
|
+
# Colors per face (constant on each triangle)
|
|
2527
|
+
colors_rgba = np.zeros((len(F), 4), dtype=float)
|
|
2528
|
+
colors_rgba[finite] = cmap(norm(face_vals[finite]))
|
|
2529
|
+
nan_rgba = np.array(mcolors.to_rgba(nan_color))
|
|
2530
|
+
colors_rgba[~finite] = nan_rgba
|
|
2531
|
+
facecolor = [
|
|
2532
|
+
f"rgb({int(255*c[0])},{int(255*c[1])},{int(255*c[2])})" for c in colors_rgba
|
|
2533
|
+
]
|
|
2534
|
+
|
|
2535
|
+
# Lighting (disable shading by default for true color rendering)
|
|
2536
|
+
shaded = kwargs.get('shaded', False)
|
|
2537
|
+
if shaded:
|
|
2538
|
+
lighting = dict(ambient=0.35, diffuse=1.0, specular=0.4, roughness=0.5, fresnel=0.1)
|
|
2539
|
+
flat = False
|
|
2540
|
+
else:
|
|
2541
|
+
# Unlit: make colors independent of lighting
|
|
2542
|
+
lighting = dict(ambient=1.0, diffuse=0.0, specular=0.0, roughness=0.0, fresnel=0.0)
|
|
2543
|
+
flat = False
|
|
2544
|
+
cx = float((V[:,0].min() + V[:,0].max()) * 0.5)
|
|
2545
|
+
cy = float((V[:,1].min() + V[:,1].max()) * 0.5)
|
|
2546
|
+
cz = float((V[:,2].min() + V[:,2].max()) * 0.5)
|
|
2547
|
+
lx = cx + (V[:,0].max() - V[:,0].min() + meshsize) * 0.9
|
|
2548
|
+
ly = cy + (V[:,1].max() - V[:,1].min() + meshsize) * 0.6
|
|
2549
|
+
lz = cz + (V[:,2].max() - V[:,2].min() + meshsize) * 1.4
|
|
2550
|
+
|
|
2551
|
+
fig.add_trace(
|
|
2552
|
+
go.Mesh3d(
|
|
2553
|
+
x=V[:,0], y=V[:,1], z=V[:,2],
|
|
2554
|
+
i=F[:,0], j=F[:,1], k=F[:,2],
|
|
2555
|
+
facecolor=facecolor if facecolor is not None else None,
|
|
2556
|
+
color=None if facecolor is not None else 'rgb(200,200,200)',
|
|
2557
|
+
opacity=float(building_opacity),
|
|
2558
|
+
flatshading=flat,
|
|
2559
|
+
lighting=lighting,
|
|
2560
|
+
lightposition=dict(x=lx, y=ly, z=lz),
|
|
2561
|
+
name=value_name if facecolor is not None else 'building_mesh'
|
|
2562
|
+
)
|
|
2563
|
+
)
|
|
2564
|
+
|
|
2565
|
+
if show:
|
|
2566
|
+
fig.show()
|
|
2567
|
+
if return_fig:
|
|
2568
|
+
return fig
|
|
2569
|
+
return None
|
|
2570
|
+
|
|
2389
2571
|
def visualize_voxcity_plotly(
|
|
2390
2572
|
voxel_array,
|
|
2391
2573
|
meshsize,
|
|
@@ -2405,6 +2587,8 @@ def visualize_voxcity_plotly(
|
|
|
2405
2587
|
using Plotly Mesh3d. One Mesh3d trace per class.
|
|
2406
2588
|
|
|
2407
2589
|
Parameters are similar to visualize_voxcity_plotly, but rendering is via exact cubes.
|
|
2590
|
+
voxel_color_map may be either a scheme name (str) understood by get_voxel_color_map,
|
|
2591
|
+
or a dict mapping class_id -> [R, G, B] (0-255).
|
|
2408
2592
|
"""
|
|
2409
2593
|
if voxel_array is None or getattr(voxel_array, 'ndim', 0) != 3:
|
|
2410
2594
|
raise ValueError("voxel_array must be a 3D numpy array (nx, ny, nz)")
|
|
@@ -2441,7 +2625,11 @@ def visualize_voxcity_plotly(
|
|
|
2441
2625
|
if not classes:
|
|
2442
2626
|
raise ValueError("No classes to visualize (voxel grid may be empty)")
|
|
2443
2627
|
|
|
2444
|
-
|
|
2628
|
+
# Resolve color map: accept scheme name or explicit dict
|
|
2629
|
+
if isinstance(voxel_color_map, dict):
|
|
2630
|
+
vox_dict = voxel_color_map
|
|
2631
|
+
else:
|
|
2632
|
+
vox_dict = get_voxel_color_map(voxel_color_map)
|
|
2445
2633
|
|
|
2446
2634
|
def exposed_face_masks(occ):
|
|
2447
2635
|
# occ shape (nx, ny, nz)
|
|
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
|
|
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
|
|
File without changes
|