voxcity 0.5.30__py3-none-any.whl → 0.5.31__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/simulator/view.py +388 -1
- voxcity/utils/visualization.py +1 -1
- {voxcity-0.5.30.dist-info → voxcity-0.5.31.dist-info}/METADATA +1 -1
- {voxcity-0.5.30.dist-info → voxcity-0.5.31.dist-info}/RECORD +8 -8
- {voxcity-0.5.30.dist-info → voxcity-0.5.31.dist-info}/WHEEL +0 -0
- {voxcity-0.5.30.dist-info → voxcity-0.5.31.dist-info}/licenses/AUTHORS.rst +0 -0
- {voxcity-0.5.30.dist-info → voxcity-0.5.31.dist-info}/licenses/LICENSE +0 -0
- {voxcity-0.5.30.dist-info → voxcity-0.5.31.dist-info}/top_level.txt +0 -0
voxcity/simulator/view.py
CHANGED
|
@@ -1333,4 +1333,391 @@ def get_surface_view_factor(voxel_data, meshsize, **kwargs):
|
|
|
1333
1333
|
except Exception as e:
|
|
1334
1334
|
print(f"Error exporting mesh: {e}")
|
|
1335
1335
|
|
|
1336
|
-
return building_mesh
|
|
1336
|
+
return building_mesh
|
|
1337
|
+
|
|
1338
|
+
@njit
|
|
1339
|
+
def trace_ray_to_landmark(voxel_data, origin, target, opaque_values, tree_k, tree_lad, meshsize):
|
|
1340
|
+
"""Trace a ray from origin to target through voxel data with tree transmittance.
|
|
1341
|
+
|
|
1342
|
+
Uses DDA algorithm to efficiently traverse voxels along ray path.
|
|
1343
|
+
Checks for opaque voxels and handles tree transmittance using Beer-Lambert law.
|
|
1344
|
+
|
|
1345
|
+
Args:
|
|
1346
|
+
voxel_data (ndarray): 3D array of voxel values
|
|
1347
|
+
origin (ndarray): Starting point (x,y,z) in voxel coordinates
|
|
1348
|
+
target (ndarray): End point (x,y,z) in voxel coordinates
|
|
1349
|
+
opaque_values (ndarray): Array of voxel values that block the ray
|
|
1350
|
+
tree_k (float): Tree extinction coefficient for Beer-Lambert law
|
|
1351
|
+
tree_lad (float): Leaf area density in m^-1 for tree transmittance
|
|
1352
|
+
meshsize (float): Size of each voxel in meters
|
|
1353
|
+
|
|
1354
|
+
Returns:
|
|
1355
|
+
bool: True if target is visible from origin, False otherwise
|
|
1356
|
+
"""
|
|
1357
|
+
nx, ny, nz = voxel_data.shape
|
|
1358
|
+
x0, y0, z0 = origin[0], origin[1], origin[2]
|
|
1359
|
+
x1, y1, z1 = target[0], target[1], target[2]
|
|
1360
|
+
|
|
1361
|
+
dx = x1 - x0
|
|
1362
|
+
dy = y1 - y0
|
|
1363
|
+
dz = z1 - z0
|
|
1364
|
+
|
|
1365
|
+
# Normalize direction vector
|
|
1366
|
+
length = np.sqrt(dx*dx + dy*dy + dz*dz)
|
|
1367
|
+
if length == 0.0:
|
|
1368
|
+
return True # Origin and target are at the same location
|
|
1369
|
+
|
|
1370
|
+
dx /= length
|
|
1371
|
+
dy /= length
|
|
1372
|
+
dz /= length
|
|
1373
|
+
|
|
1374
|
+
# Initialize ray position
|
|
1375
|
+
x, y, z = x0 + 0.5, y0 + 0.5, z0 + 0.5
|
|
1376
|
+
i, j, k = int(x0), int(y0), int(z0)
|
|
1377
|
+
|
|
1378
|
+
# Determine step direction
|
|
1379
|
+
step_x = 1 if dx >= 0 else -1
|
|
1380
|
+
step_y = 1 if dy >= 0 else -1
|
|
1381
|
+
step_z = 1 if dz >= 0 else -1
|
|
1382
|
+
|
|
1383
|
+
# Calculate distances to next voxel boundaries
|
|
1384
|
+
if dx != 0:
|
|
1385
|
+
t_max_x = ((i + (step_x > 0)) - x) / dx
|
|
1386
|
+
t_delta_x = abs(1 / dx)
|
|
1387
|
+
else:
|
|
1388
|
+
t_max_x = np.inf
|
|
1389
|
+
t_delta_x = np.inf
|
|
1390
|
+
|
|
1391
|
+
if dy != 0:
|
|
1392
|
+
t_max_y = ((j + (step_y > 0)) - y) / dy
|
|
1393
|
+
t_delta_y = abs(1 / dy)
|
|
1394
|
+
else:
|
|
1395
|
+
t_max_y = np.inf
|
|
1396
|
+
t_delta_y = np.inf
|
|
1397
|
+
|
|
1398
|
+
if dz != 0:
|
|
1399
|
+
t_max_z = ((k + (step_z > 0)) - z) / dz
|
|
1400
|
+
t_delta_z = abs(1 / dz)
|
|
1401
|
+
else:
|
|
1402
|
+
t_max_z = np.inf
|
|
1403
|
+
t_delta_z = np.inf
|
|
1404
|
+
|
|
1405
|
+
# Track cumulative tree transmittance
|
|
1406
|
+
tree_transmittance = 1.0
|
|
1407
|
+
|
|
1408
|
+
# Main ray traversal loop
|
|
1409
|
+
while True:
|
|
1410
|
+
# Check if current voxel is within bounds
|
|
1411
|
+
if (0 <= i < nx) and (0 <= j < ny) and (0 <= k < nz):
|
|
1412
|
+
voxel_value = voxel_data[i, j, k]
|
|
1413
|
+
|
|
1414
|
+
# Check for tree voxels (-2)
|
|
1415
|
+
if voxel_value == -2:
|
|
1416
|
+
# Apply Beer-Lambert law for tree transmittance
|
|
1417
|
+
tree_transmittance *= np.exp(-tree_k * tree_lad * meshsize)
|
|
1418
|
+
if tree_transmittance < 0.01: # Ray effectively blocked
|
|
1419
|
+
return False
|
|
1420
|
+
# Check for other opaque voxels
|
|
1421
|
+
elif voxel_value in opaque_values:
|
|
1422
|
+
return False # Ray is blocked
|
|
1423
|
+
else:
|
|
1424
|
+
return False # Ray went out of bounds
|
|
1425
|
+
|
|
1426
|
+
# Check if we've reached the target voxel
|
|
1427
|
+
if i == int(x1) and j == int(y1) and k == int(z1):
|
|
1428
|
+
return True # Ray successfully reached the target
|
|
1429
|
+
|
|
1430
|
+
# Move to next voxel using DDA algorithm
|
|
1431
|
+
if t_max_x < t_max_y:
|
|
1432
|
+
if t_max_x < t_max_z:
|
|
1433
|
+
t_max_x += t_delta_x
|
|
1434
|
+
i += step_x
|
|
1435
|
+
else:
|
|
1436
|
+
t_max_z += t_delta_z
|
|
1437
|
+
k += step_z
|
|
1438
|
+
else:
|
|
1439
|
+
if t_max_y < t_max_z:
|
|
1440
|
+
t_max_y += t_delta_y
|
|
1441
|
+
j += step_y
|
|
1442
|
+
else:
|
|
1443
|
+
t_max_z += t_delta_z
|
|
1444
|
+
k += step_z
|
|
1445
|
+
|
|
1446
|
+
|
|
1447
|
+
@njit
|
|
1448
|
+
def compute_face_landmark_visibility(face_center, face_normal, landmark_positions,
|
|
1449
|
+
voxel_data, meshsize, tree_k, tree_lad,
|
|
1450
|
+
grid_bounds_real, boundary_epsilon):
|
|
1451
|
+
"""Compute binary landmark visibility for a single face.
|
|
1452
|
+
|
|
1453
|
+
Args:
|
|
1454
|
+
face_center (ndarray): Face centroid position in real coordinates
|
|
1455
|
+
face_normal (ndarray): Face normal vector (outward pointing)
|
|
1456
|
+
landmark_positions (ndarray): Array of landmark positions in voxel coordinates
|
|
1457
|
+
voxel_data (ndarray): 3D array of voxel values
|
|
1458
|
+
meshsize (float): Size of each voxel in meters
|
|
1459
|
+
tree_k (float): Tree extinction coefficient
|
|
1460
|
+
tree_lad (float): Leaf area density
|
|
1461
|
+
grid_bounds_real (ndarray): Domain bounds in real coordinates
|
|
1462
|
+
boundary_epsilon (float): Tolerance for boundary detection
|
|
1463
|
+
|
|
1464
|
+
Returns:
|
|
1465
|
+
float: 1.0 if any landmark is visible, 0.0 if none visible, np.nan for boundary faces
|
|
1466
|
+
"""
|
|
1467
|
+
# Check for boundary vertical faces
|
|
1468
|
+
is_vertical = (abs(face_normal[2]) < 0.01)
|
|
1469
|
+
|
|
1470
|
+
on_x_min = (abs(face_center[0] - grid_bounds_real[0,0]) < boundary_epsilon)
|
|
1471
|
+
on_y_min = (abs(face_center[1] - grid_bounds_real[0,1]) < boundary_epsilon)
|
|
1472
|
+
on_x_max = (abs(face_center[0] - grid_bounds_real[1,0]) < boundary_epsilon)
|
|
1473
|
+
on_y_max = (abs(face_center[1] - grid_bounds_real[1,1]) < boundary_epsilon)
|
|
1474
|
+
|
|
1475
|
+
is_boundary_vertical = is_vertical and (on_x_min or on_y_min or on_x_max or on_y_max)
|
|
1476
|
+
if is_boundary_vertical:
|
|
1477
|
+
return np.nan
|
|
1478
|
+
|
|
1479
|
+
# Convert face center to voxel coordinates with small offset
|
|
1480
|
+
norm_n = np.sqrt(face_normal[0]**2 + face_normal[1]**2 + face_normal[2]**2)
|
|
1481
|
+
if norm_n < 1e-12:
|
|
1482
|
+
return 0.0
|
|
1483
|
+
|
|
1484
|
+
offset_vox = 0.1 # Offset in voxel units to avoid self-intersection
|
|
1485
|
+
ray_origin = (face_center / meshsize) + (face_normal / norm_n) * offset_vox
|
|
1486
|
+
|
|
1487
|
+
# Define opaque values (everything except empty space and landmarks)
|
|
1488
|
+
unique_values = np.unique(voxel_data)
|
|
1489
|
+
landmark_value = -30 # Standard landmark value
|
|
1490
|
+
opaque_values = np.array([v for v in unique_values if v != 0 and v != landmark_value], dtype=np.int32)
|
|
1491
|
+
|
|
1492
|
+
# Check visibility to each landmark
|
|
1493
|
+
for idx in range(landmark_positions.shape[0]):
|
|
1494
|
+
target = landmark_positions[idx]
|
|
1495
|
+
|
|
1496
|
+
# Check if ray direction points towards the face (not away from it)
|
|
1497
|
+
ray_dir = target - ray_origin
|
|
1498
|
+
ray_length = np.sqrt(ray_dir[0]**2 + ray_dir[1]**2 + ray_dir[2]**2)
|
|
1499
|
+
if ray_length > 0:
|
|
1500
|
+
ray_dir = ray_dir / ray_length
|
|
1501
|
+
dot_product = (ray_dir[0]*face_normal[0] +
|
|
1502
|
+
ray_dir[1]*face_normal[1] +
|
|
1503
|
+
ray_dir[2]*face_normal[2])
|
|
1504
|
+
|
|
1505
|
+
# Only trace ray if it points outward from the face
|
|
1506
|
+
if dot_product > 0:
|
|
1507
|
+
is_visible = trace_ray_to_landmark(voxel_data, ray_origin, target,
|
|
1508
|
+
opaque_values, tree_k, tree_lad, meshsize)
|
|
1509
|
+
if is_visible:
|
|
1510
|
+
return 1.0 # Return immediately when first visible landmark is found
|
|
1511
|
+
|
|
1512
|
+
return 0.0 # No landmarks were visible
|
|
1513
|
+
|
|
1514
|
+
|
|
1515
|
+
@njit(parallel=True)
|
|
1516
|
+
def compute_landmark_visibility_for_all_faces(face_centers, face_normals, landmark_positions,
|
|
1517
|
+
voxel_data, meshsize, tree_k, tree_lad,
|
|
1518
|
+
grid_bounds_real, boundary_epsilon):
|
|
1519
|
+
"""Compute binary landmark visibility for all building surface faces.
|
|
1520
|
+
|
|
1521
|
+
Args:
|
|
1522
|
+
face_centers (ndarray): (n_faces, 3) face centroid positions in real coordinates
|
|
1523
|
+
face_normals (ndarray): (n_faces, 3) face normal vectors
|
|
1524
|
+
landmark_positions (ndarray): (n_landmarks, 3) landmark positions in voxel coordinates
|
|
1525
|
+
voxel_data (ndarray): 3D array of voxel values
|
|
1526
|
+
meshsize (float): Size of each voxel in meters
|
|
1527
|
+
tree_k (float): Tree extinction coefficient
|
|
1528
|
+
tree_lad (float): Leaf area density
|
|
1529
|
+
grid_bounds_real (ndarray): Domain bounds in real coordinates
|
|
1530
|
+
boundary_epsilon (float): Tolerance for boundary detection
|
|
1531
|
+
|
|
1532
|
+
Returns:
|
|
1533
|
+
ndarray: Binary visibility values for each face (1=visible, 0=not visible, nan=boundary)
|
|
1534
|
+
"""
|
|
1535
|
+
n_faces = face_centers.shape[0]
|
|
1536
|
+
visibility_values = np.zeros(n_faces, dtype=np.float64)
|
|
1537
|
+
|
|
1538
|
+
# Process each face in parallel
|
|
1539
|
+
for fidx in prange(n_faces):
|
|
1540
|
+
visibility_values[fidx] = compute_face_landmark_visibility(
|
|
1541
|
+
face_centers[fidx],
|
|
1542
|
+
face_normals[fidx],
|
|
1543
|
+
landmark_positions,
|
|
1544
|
+
voxel_data,
|
|
1545
|
+
meshsize,
|
|
1546
|
+
tree_k,
|
|
1547
|
+
tree_lad,
|
|
1548
|
+
grid_bounds_real,
|
|
1549
|
+
boundary_epsilon
|
|
1550
|
+
)
|
|
1551
|
+
|
|
1552
|
+
return visibility_values
|
|
1553
|
+
|
|
1554
|
+
|
|
1555
|
+
def get_surface_landmark_visibility(voxel_data, building_id_grid, building_gdf, meshsize, **kwargs):
|
|
1556
|
+
"""
|
|
1557
|
+
Compute binary landmark visibility for building surface meshes.
|
|
1558
|
+
|
|
1559
|
+
This function extracts building surface meshes and computes whether each face
|
|
1560
|
+
can see any landmark voxel. It uses direct ray tracing from face centers to
|
|
1561
|
+
all landmark positions, accounting for tree transmittance.
|
|
1562
|
+
|
|
1563
|
+
Args:
|
|
1564
|
+
voxel_data (ndarray): 3D array of voxel values
|
|
1565
|
+
building_id_grid (ndarray): 3D array mapping voxels to building IDs
|
|
1566
|
+
building_gdf (GeoDataFrame): GeoDataFrame containing building features
|
|
1567
|
+
meshsize (float): Size of each voxel in meters
|
|
1568
|
+
**kwargs: Configuration options including:
|
|
1569
|
+
landmark_building_ids (list): List of building IDs to mark as landmarks
|
|
1570
|
+
landmark_polygon (list): Polygon vertices to identify landmarks
|
|
1571
|
+
rectangle_vertices (list): Rectangle vertices to identify landmarks
|
|
1572
|
+
building_class_id (int): Voxel class for buildings (default: -3)
|
|
1573
|
+
tree_k (float): Tree extinction coefficient (default: 0.6)
|
|
1574
|
+
tree_lad (float): Leaf area density (default: 1.0)
|
|
1575
|
+
colormap (str): Matplotlib colormap (default: 'RdYlGn')
|
|
1576
|
+
obj_export (bool): Whether to export mesh as OBJ file
|
|
1577
|
+
output_directory (str): Directory for OBJ export
|
|
1578
|
+
output_file_name (str): Base filename for OBJ export
|
|
1579
|
+
progress_report (bool): Whether to print progress
|
|
1580
|
+
|
|
1581
|
+
Returns:
|
|
1582
|
+
tuple: (surface_mesh, modified_voxel_data)
|
|
1583
|
+
- surface_mesh: trimesh.Trimesh with binary visibility values in metadata
|
|
1584
|
+
- modified_voxel_data: voxel data with landmarks marked
|
|
1585
|
+
Returns (None, None) if no surfaces or landmarks found
|
|
1586
|
+
"""
|
|
1587
|
+
import matplotlib.pyplot as plt
|
|
1588
|
+
import os
|
|
1589
|
+
|
|
1590
|
+
# Get landmark building IDs
|
|
1591
|
+
landmark_ids = kwargs.get('landmark_building_ids', None)
|
|
1592
|
+
landmark_polygon = kwargs.get('landmark_polygon', None)
|
|
1593
|
+
if landmark_ids is None:
|
|
1594
|
+
if landmark_polygon is not None:
|
|
1595
|
+
landmark_ids = get_buildings_in_drawn_polygon(building_gdf, landmark_polygon, operation='within')
|
|
1596
|
+
else:
|
|
1597
|
+
rectangle_vertices = kwargs.get("rectangle_vertices", None)
|
|
1598
|
+
if rectangle_vertices is None:
|
|
1599
|
+
print("Cannot set landmark buildings. You need to input either of rectangle_vertices or landmark_ids.")
|
|
1600
|
+
return None, None
|
|
1601
|
+
|
|
1602
|
+
# Calculate center point of rectangle
|
|
1603
|
+
lons = [coord[0] for coord in rectangle_vertices]
|
|
1604
|
+
lats = [coord[1] for coord in rectangle_vertices]
|
|
1605
|
+
center_lon = (min(lons) + max(lons)) / 2
|
|
1606
|
+
center_lat = (min(lats) + max(lats)) / 2
|
|
1607
|
+
target_point = (center_lon, center_lat)
|
|
1608
|
+
|
|
1609
|
+
# Find buildings at center point
|
|
1610
|
+
landmark_ids = find_building_containing_point(building_gdf, target_point)
|
|
1611
|
+
|
|
1612
|
+
# Extract configuration parameters
|
|
1613
|
+
building_class_id = kwargs.get("building_class_id", -3)
|
|
1614
|
+
landmark_value = -30
|
|
1615
|
+
tree_k = kwargs.get("tree_k", 0.6)
|
|
1616
|
+
tree_lad = kwargs.get("tree_lad", 1.0)
|
|
1617
|
+
colormap = kwargs.get("colormap", 'RdYlGn')
|
|
1618
|
+
progress_report = kwargs.get("progress_report", False)
|
|
1619
|
+
|
|
1620
|
+
# Create a copy of voxel data for modifications
|
|
1621
|
+
voxel_data_for_mesh = voxel_data.copy()
|
|
1622
|
+
voxel_data_modified = voxel_data.copy()
|
|
1623
|
+
|
|
1624
|
+
# Mark landmark buildings with special value in modified data
|
|
1625
|
+
voxel_data_modified = mark_building_by_id(voxel_data_modified, building_id_grid, landmark_ids, landmark_value)
|
|
1626
|
+
|
|
1627
|
+
# In the mesh extraction data, change landmark buildings to a different value
|
|
1628
|
+
# so they won't be included in the surface mesh extraction
|
|
1629
|
+
voxel_data_for_mesh = mark_building_by_id(voxel_data_for_mesh, building_id_grid, landmark_ids, 0)
|
|
1630
|
+
|
|
1631
|
+
# Find positions of all landmark voxels
|
|
1632
|
+
landmark_positions = np.argwhere(voxel_data_modified == landmark_value).astype(np.float64)
|
|
1633
|
+
if landmark_positions.shape[0] == 0:
|
|
1634
|
+
print(f"No landmarks found after marking buildings with IDs: {landmark_ids}")
|
|
1635
|
+
return None, None
|
|
1636
|
+
|
|
1637
|
+
if progress_report:
|
|
1638
|
+
print(f"Found {landmark_positions.shape[0]} landmark voxels")
|
|
1639
|
+
print(f"Landmark building IDs: {landmark_ids}")
|
|
1640
|
+
|
|
1641
|
+
# Extract building surface mesh excluding landmark buildings
|
|
1642
|
+
try:
|
|
1643
|
+
building_mesh = create_voxel_mesh(
|
|
1644
|
+
voxel_data_for_mesh, # Use data where landmarks are excluded
|
|
1645
|
+
building_class_id,
|
|
1646
|
+
meshsize,
|
|
1647
|
+
building_id_grid=building_id_grid,
|
|
1648
|
+
mesh_type='open_air'
|
|
1649
|
+
)
|
|
1650
|
+
if building_mesh is None or len(building_mesh.faces) == 0:
|
|
1651
|
+
print("No non-landmark building surfaces found in voxel data.")
|
|
1652
|
+
return None, None
|
|
1653
|
+
except Exception as e:
|
|
1654
|
+
print(f"Error during mesh extraction: {e}")
|
|
1655
|
+
return None, None
|
|
1656
|
+
|
|
1657
|
+
if progress_report:
|
|
1658
|
+
print(f"Processing landmark visibility for {len(building_mesh.faces)} faces...")
|
|
1659
|
+
|
|
1660
|
+
# Get mesh properties
|
|
1661
|
+
face_centers = building_mesh.triangles_center
|
|
1662
|
+
face_normals = building_mesh.face_normals
|
|
1663
|
+
|
|
1664
|
+
# Calculate domain bounds
|
|
1665
|
+
nx, ny, nz = voxel_data_modified.shape
|
|
1666
|
+
grid_bounds_voxel = np.array([[0,0,0],[nx, ny, nz]], dtype=np.float64)
|
|
1667
|
+
grid_bounds_real = grid_bounds_voxel * meshsize
|
|
1668
|
+
boundary_epsilon = meshsize * 0.05
|
|
1669
|
+
|
|
1670
|
+
# Compute binary visibility for all faces using modified voxel data
|
|
1671
|
+
visibility_values = compute_landmark_visibility_for_all_faces(
|
|
1672
|
+
face_centers,
|
|
1673
|
+
face_normals,
|
|
1674
|
+
landmark_positions,
|
|
1675
|
+
voxel_data_modified, # Use modified data with landmarks marked
|
|
1676
|
+
meshsize,
|
|
1677
|
+
tree_k,
|
|
1678
|
+
tree_lad,
|
|
1679
|
+
grid_bounds_real,
|
|
1680
|
+
boundary_epsilon
|
|
1681
|
+
)
|
|
1682
|
+
|
|
1683
|
+
# Store visibility values in mesh metadata
|
|
1684
|
+
if not hasattr(building_mesh, 'metadata'):
|
|
1685
|
+
building_mesh.metadata = {}
|
|
1686
|
+
building_mesh.metadata['landmark_visibility'] = visibility_values
|
|
1687
|
+
|
|
1688
|
+
# Count visible faces (excluding NaN boundary faces)
|
|
1689
|
+
valid_mask = ~np.isnan(visibility_values)
|
|
1690
|
+
n_valid = np.sum(valid_mask)
|
|
1691
|
+
n_visible = np.sum(visibility_values[valid_mask] > 0.5)
|
|
1692
|
+
|
|
1693
|
+
if progress_report:
|
|
1694
|
+
print(f"Landmark visibility statistics:")
|
|
1695
|
+
print(f" Total faces: {len(visibility_values)}")
|
|
1696
|
+
print(f" Valid faces: {n_valid}")
|
|
1697
|
+
print(f" Faces with landmark visibility: {n_visible} ({n_visible/n_valid*100:.1f}%)")
|
|
1698
|
+
|
|
1699
|
+
# Optional OBJ export
|
|
1700
|
+
obj_export = kwargs.get("obj_export", False)
|
|
1701
|
+
if obj_export:
|
|
1702
|
+
output_dir = kwargs.get("output_directory", "output")
|
|
1703
|
+
output_file_name = kwargs.get("output_file_name", "surface_landmark_visibility")
|
|
1704
|
+
os.makedirs(output_dir, exist_ok=True)
|
|
1705
|
+
|
|
1706
|
+
try:
|
|
1707
|
+
# Apply colormap to visibility values for visualization
|
|
1708
|
+
cmap = plt.cm.get_cmap(colormap)
|
|
1709
|
+
face_colors = np.zeros((len(visibility_values), 4))
|
|
1710
|
+
|
|
1711
|
+
for i, val in enumerate(visibility_values):
|
|
1712
|
+
if np.isnan(val):
|
|
1713
|
+
face_colors[i] = [0.7, 0.7, 0.7, 1.0] # Gray for boundary faces
|
|
1714
|
+
else:
|
|
1715
|
+
face_colors[i] = cmap(val)
|
|
1716
|
+
|
|
1717
|
+
building_mesh.visual.face_colors = face_colors
|
|
1718
|
+
building_mesh.export(f"{output_dir}/{output_file_name}.obj")
|
|
1719
|
+
print(f"Exported surface mesh to {output_dir}/{output_file_name}.obj")
|
|
1720
|
+
except Exception as e:
|
|
1721
|
+
print(f"Error exporting mesh: {e}")
|
|
1722
|
+
|
|
1723
|
+
return building_mesh, voxel_data_modified
|
voxcity/utils/visualization.py
CHANGED
|
@@ -301,7 +301,7 @@ def get_voxel_color_map(color_scheme='default'):
|
|
|
301
301
|
elif color_scheme == 'grayscale':
|
|
302
302
|
return {
|
|
303
303
|
-99: [0, 0, 0], # void (black)
|
|
304
|
-
-30: [
|
|
304
|
+
-30: [253, 231, 37], # (Pink) 'Landmark',
|
|
305
305
|
-17: [240, 240, 240], # 'plaster'
|
|
306
306
|
-16: [60, 60, 60], # 'glass'
|
|
307
307
|
-15: [130, 130, 130], # 'stone'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: voxcity
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.31
|
|
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>
|
|
@@ -24,15 +24,15 @@ voxcity/geoprocessor/utils.py,sha256=DVg3EMRytLQLEQeXLvNgjt1Ynoa689EsD-To-14xgSQ
|
|
|
24
24
|
voxcity/simulator/__init__.py,sha256=APdkcdaovj0v_RPOaA4SBvFUKT2RM7Hxuuz3Sux4gCo,65
|
|
25
25
|
voxcity/simulator/solar.py,sha256=t7zOSxBObQMuWvQSiHN9AViJstzfkXqAg3BpIppn0KA,82694
|
|
26
26
|
voxcity/simulator/utils.py,sha256=sEYBB2-hLJxTiXQps1_-Fi7t1HN3-1OPOvBCWtgIisA,130
|
|
27
|
-
voxcity/simulator/view.py,sha256=
|
|
27
|
+
voxcity/simulator/view.py,sha256=Xnr-NoKGyO9D02bF104ZKzNhsnjNCkzc-B1yPtDEqpQ,75898
|
|
28
28
|
voxcity/utils/__init__.py,sha256=Q-NYCqYnAAaF80KuNwpqIjbE7Ec3Gr4y_khMLIMhJrg,68
|
|
29
29
|
voxcity/utils/lc.py,sha256=h2yOWLUIrrummkyMyhRK5VbyrsPtslS0MJov_y0WGIQ,18925
|
|
30
30
|
voxcity/utils/material.py,sha256=H8K8Lq4wBL6dQtgj7esUW2U6wLCOTeOtelkTDJoRgMo,10007
|
|
31
|
-
voxcity/utils/visualization.py,sha256=
|
|
31
|
+
voxcity/utils/visualization.py,sha256=OvfZ67dg3rHr1LD0xTPYK_LBtoZwjJe-H0Nhcwkd_HA,112919
|
|
32
32
|
voxcity/utils/weather.py,sha256=2Jtg-rIVJcsTtiKE-KuDnhIqS1-MSS16_zFRzj6zmu4,36435
|
|
33
|
-
voxcity-0.5.
|
|
34
|
-
voxcity-0.5.
|
|
35
|
-
voxcity-0.5.
|
|
36
|
-
voxcity-0.5.
|
|
37
|
-
voxcity-0.5.
|
|
38
|
-
voxcity-0.5.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|