voxcity 0.6.5__py3-none-any.whl → 0.6.7__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/exporter/cityles.py +27 -6
- voxcity/geoprocessor/grid.py +45 -33
- {voxcity-0.6.5.dist-info → voxcity-0.6.7.dist-info}/METADATA +1 -1
- {voxcity-0.6.5.dist-info → voxcity-0.6.7.dist-info}/RECORD +7 -7
- {voxcity-0.6.5.dist-info → voxcity-0.6.7.dist-info}/AUTHORS.rst +0 -0
- {voxcity-0.6.5.dist-info → voxcity-0.6.7.dist-info}/LICENSE +0 -0
- {voxcity-0.6.5.dist-info → voxcity-0.6.7.dist-info}/WHEEL +0 -0
voxcity/exporter/cityles.py
CHANGED
|
@@ -190,7 +190,7 @@ def _build_index_to_cityles_map(land_cover_source):
|
|
|
190
190
|
|
|
191
191
|
|
|
192
192
|
def export_topog(building_height_grid, building_id_grid, output_path,
|
|
193
|
-
building_material='default'):
|
|
193
|
+
building_material='default', cityles_landuse_grid=None):
|
|
194
194
|
"""
|
|
195
195
|
Export topog.txt file for CityLES
|
|
196
196
|
|
|
@@ -226,8 +226,17 @@ def export_topog(building_height_grid, building_id_grid, output_path,
|
|
|
226
226
|
i_1based = i + 1
|
|
227
227
|
j_1based = j + 1
|
|
228
228
|
height = float(building_height_grid[j, i])
|
|
229
|
+
# Decide material code per cell
|
|
230
|
+
if cityles_landuse_grid is not None:
|
|
231
|
+
cell_lu = int(cityles_landuse_grid[j, i])
|
|
232
|
+
material_code_cell = cell_lu + 100
|
|
233
|
+
else:
|
|
234
|
+
if height > 0:
|
|
235
|
+
material_code_cell = material_code
|
|
236
|
+
else:
|
|
237
|
+
material_code_cell = 102
|
|
229
238
|
# Format: i j height material_code depth1 depth2 changed_material
|
|
230
|
-
f.write(f"{i_1based} {j_1based} {height:.1f} {
|
|
239
|
+
f.write(f"{i_1based} {j_1based} {height:.1f} {material_code_cell} 0.0 0.0 102\n")
|
|
231
240
|
|
|
232
241
|
|
|
233
242
|
def export_landuse(land_cover_grid, output_path, land_cover_source=None):
|
|
@@ -254,6 +263,8 @@ def export_landuse(land_cover_grid, output_path, land_cover_source=None):
|
|
|
254
263
|
|
|
255
264
|
# Create mapping statistics
|
|
256
265
|
mapping_stats = {}
|
|
266
|
+
# Prepare grid to return
|
|
267
|
+
cityles_landuse_grid = np.zeros((ny, nx), dtype=int)
|
|
257
268
|
|
|
258
269
|
with open(filename, 'w') as f:
|
|
259
270
|
# Write in row-major order (j varies first, then i)
|
|
@@ -263,6 +274,8 @@ def export_landuse(land_cover_grid, output_path, land_cover_source=None):
|
|
|
263
274
|
cityles_code = index_to_code.get(idx, 4)
|
|
264
275
|
f.write(f"{cityles_code}\n")
|
|
265
276
|
|
|
277
|
+
cityles_landuse_grid[j, i] = cityles_code
|
|
278
|
+
|
|
266
279
|
# Track mapping statistics
|
|
267
280
|
if idx not in mapping_stats:
|
|
268
281
|
mapping_stats[idx] = {'cityles_code': cityles_code, 'count': 0}
|
|
@@ -277,6 +290,8 @@ def export_landuse(land_cover_grid, output_path, land_cover_source=None):
|
|
|
277
290
|
class_name = class_names[idx] if 0 <= idx < len(class_names) else 'Unknown'
|
|
278
291
|
print(f" {idx}: {class_name} -> CityLES {stats['cityles_code']}: "
|
|
279
292
|
f"{stats['count']} cells ({percentage:.1f}%)")
|
|
293
|
+
|
|
294
|
+
return cityles_landuse_grid
|
|
280
295
|
|
|
281
296
|
|
|
282
297
|
def export_dem(dem_grid, output_path):
|
|
@@ -434,11 +449,17 @@ def export_cityles(building_height_grid, building_id_grid, canopy_height_grid,
|
|
|
434
449
|
print(f"Land cover source: {land_cover_source}")
|
|
435
450
|
|
|
436
451
|
# Export individual files
|
|
437
|
-
print("\nExporting topog.txt...")
|
|
438
|
-
export_topog(building_height_grid, building_id_grid, output_path, building_material)
|
|
439
|
-
|
|
440
452
|
print("\nExporting landuse.txt...")
|
|
441
|
-
export_landuse(land_cover_grid, output_path, land_cover_source)
|
|
453
|
+
cityles_landuse_grid = export_landuse(land_cover_grid, output_path, land_cover_source)
|
|
454
|
+
|
|
455
|
+
print("\nExporting topog.txt...")
|
|
456
|
+
export_topog(
|
|
457
|
+
building_height_grid,
|
|
458
|
+
building_id_grid,
|
|
459
|
+
output_path,
|
|
460
|
+
building_material,
|
|
461
|
+
cityles_landuse_grid=cityles_landuse_grid,
|
|
462
|
+
)
|
|
442
463
|
|
|
443
464
|
print("\nExporting dem.txt...")
|
|
444
465
|
export_dem(dem_grid, output_path)
|
voxcity/geoprocessor/grid.py
CHANGED
|
@@ -263,14 +263,19 @@ def calculate_grid_size(side_1, side_2, u_vec, v_vec, meshsize):
|
|
|
263
263
|
>>> mesh = 10 # Desired 10-unit mesh
|
|
264
264
|
>>> grid_size, adj_mesh = calculate_grid_size(side1, side2, u, v, mesh)
|
|
265
265
|
"""
|
|
266
|
-
# Calculate
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
# Calculate total side lengths in meters using the relationship between side vectors and unit vectors
|
|
267
|
+
# u_vec and v_vec represent degrees per meter along each side direction
|
|
268
|
+
dist_side_1_m = np.linalg.norm(side_1) / (np.linalg.norm(u_vec) + 1e-12)
|
|
269
|
+
dist_side_2_m = np.linalg.norm(side_2) / (np.linalg.norm(v_vec) + 1e-12)
|
|
270
|
+
|
|
271
|
+
# Calculate number of cells (nx along u, ny along v), rounding to nearest integer and ensuring at least 1
|
|
272
|
+
grid_size_0 = max(1, int(dist_side_1_m / meshsize + 0.5))
|
|
273
|
+
grid_size_1 = max(1, int(dist_side_2_m / meshsize + 0.5))
|
|
274
|
+
|
|
275
|
+
# Adjust mesh sizes (in meters) to exactly fit the sides with the calculated number of cells
|
|
276
|
+
adjusted_mesh_size_0 = dist_side_1_m / grid_size_0
|
|
277
|
+
adjusted_mesh_size_1 = dist_side_2_m / grid_size_1
|
|
278
|
+
|
|
274
279
|
return (grid_size_0, grid_size_1), (adjusted_mesh_size_0, adjusted_mesh_size_1)
|
|
275
280
|
|
|
276
281
|
def create_coordinate_mesh(origin, grid_size, adjusted_meshsize, u_vec, v_vec):
|
|
@@ -908,12 +913,19 @@ def _process_with_rasterio(filtered_gdf, grid_size, adjusted_meshsize, origin, u
|
|
|
908
913
|
Process buildings using fast rasterio-based approach.
|
|
909
914
|
Faster but less precise for overlapping footprints.
|
|
910
915
|
"""
|
|
911
|
-
# Set up transform for rasterio
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
916
|
+
# Set up transform for rasterio using rotated basis defined by u_vec and v_vec
|
|
917
|
+
# Step vectors in coordinate units (degrees) per cell
|
|
918
|
+
u_step = adjusted_meshsize[0] * u_vec
|
|
919
|
+
v_step = adjusted_meshsize[1] * v_vec
|
|
920
|
+
|
|
921
|
+
# Define the top-left corner so that row=0 is the northern edge
|
|
922
|
+
top_left = origin + grid_size[1] * v_step
|
|
923
|
+
|
|
924
|
+
# Affine transform mapping (col, row) -> (x, y)
|
|
925
|
+
# x = a*col + b*row + c ; y = d*col + e*row + f
|
|
926
|
+
# col increases along u_step; row increases southward, hence -v_step
|
|
927
|
+
transform = Affine(u_step[0], -v_step[0], top_left[0],
|
|
928
|
+
u_step[1], -v_step[1], top_left[1])
|
|
917
929
|
|
|
918
930
|
# Process buildings data
|
|
919
931
|
filtered_gdf = filtered_gdf.copy()
|
|
@@ -934,9 +946,9 @@ def _process_with_rasterio(filtered_gdf, grid_size, adjusted_meshsize, origin, u
|
|
|
934
946
|
regular_buildings = filtered_gdf[~filtered_gdf['is_inner']].copy()
|
|
935
947
|
regular_buildings = regular_buildings.sort_values('height', ascending=True, na_position='first')
|
|
936
948
|
|
|
937
|
-
#
|
|
938
|
-
|
|
939
|
-
|
|
949
|
+
# Temporary raster grids in rasterio's (rows=ny, cols=nx) order
|
|
950
|
+
height_raster = np.zeros((grid_size[1], grid_size[0]), dtype=np.float64)
|
|
951
|
+
id_raster = np.zeros((grid_size[1], grid_size[0]), dtype=np.float64)
|
|
940
952
|
|
|
941
953
|
# Vectorized rasterization
|
|
942
954
|
if len(regular_buildings) > 0:
|
|
@@ -949,9 +961,9 @@ def _process_with_rasterio(filtered_gdf, grid_size, adjusted_meshsize, origin, u
|
|
|
949
961
|
if pd.notna(height) and height > 0]
|
|
950
962
|
|
|
951
963
|
if height_shapes:
|
|
952
|
-
|
|
964
|
+
height_raster = features.rasterize(
|
|
953
965
|
height_shapes,
|
|
954
|
-
out_shape=grid_size,
|
|
966
|
+
out_shape=(grid_size[1], grid_size[0]),
|
|
955
967
|
transform=transform,
|
|
956
968
|
fill=0,
|
|
957
969
|
dtype=np.float64
|
|
@@ -962,9 +974,9 @@ def _process_with_rasterio(filtered_gdf, grid_size, adjusted_meshsize, origin, u
|
|
|
962
974
|
zip(valid_buildings.geometry, valid_buildings['id'])]
|
|
963
975
|
|
|
964
976
|
if id_shapes:
|
|
965
|
-
|
|
977
|
+
id_raster = features.rasterize(
|
|
966
978
|
id_shapes,
|
|
967
|
-
out_shape=grid_size,
|
|
979
|
+
out_shape=(grid_size[1], grid_size[0]),
|
|
968
980
|
transform=transform,
|
|
969
981
|
fill=0,
|
|
970
982
|
dtype=np.float64
|
|
@@ -977,17 +989,17 @@ def _process_with_rasterio(filtered_gdf, grid_size, adjusted_meshsize, origin, u
|
|
|
977
989
|
if inner_shapes:
|
|
978
990
|
inner_mask = features.rasterize(
|
|
979
991
|
inner_shapes,
|
|
980
|
-
out_shape=grid_size,
|
|
992
|
+
out_shape=(grid_size[1], grid_size[0]),
|
|
981
993
|
transform=transform,
|
|
982
994
|
fill=0,
|
|
983
995
|
dtype=np.uint8
|
|
984
996
|
)
|
|
985
|
-
|
|
986
|
-
|
|
997
|
+
height_raster[inner_mask > 0] = 0
|
|
998
|
+
id_raster[inner_mask > 0] = 0
|
|
987
999
|
|
|
988
1000
|
# Simplified min_height grid
|
|
989
1001
|
building_min_height_grid = np.empty(grid_size, dtype=object)
|
|
990
|
-
|
|
1002
|
+
min_heights_raster = np.zeros((grid_size[1], grid_size[0]), dtype=np.float64)
|
|
991
1003
|
|
|
992
1004
|
if len(regular_buildings) > 0:
|
|
993
1005
|
valid_buildings = regular_buildings[regular_buildings.geometry.is_valid].copy()
|
|
@@ -997,27 +1009,27 @@ def _process_with_rasterio(filtered_gdf, grid_size, adjusted_meshsize, origin, u
|
|
|
997
1009
|
if pd.notna(min_h)]
|
|
998
1010
|
|
|
999
1011
|
if min_height_shapes:
|
|
1000
|
-
|
|
1012
|
+
min_heights_raster = features.rasterize(
|
|
1001
1013
|
min_height_shapes,
|
|
1002
|
-
out_shape=grid_size,
|
|
1014
|
+
out_shape=(grid_size[1], grid_size[0]),
|
|
1003
1015
|
transform=transform,
|
|
1004
1016
|
fill=0,
|
|
1005
1017
|
dtype=np.float64
|
|
1006
1018
|
)
|
|
1007
1019
|
|
|
1008
1020
|
# Convert to list format (simplified)
|
|
1021
|
+
# Convert raster (ny, nx) to internal orientation (nx, ny) with north-up
|
|
1022
|
+
building_height_grid = np.flipud(height_raster).T
|
|
1023
|
+
building_id_grid = np.flipud(id_raster).T
|
|
1024
|
+
min_heights = np.flipud(min_heights_raster).T
|
|
1025
|
+
|
|
1009
1026
|
for i in range(grid_size[0]):
|
|
1010
1027
|
for j in range(grid_size[1]):
|
|
1011
1028
|
if building_height_grid[i, j] > 0:
|
|
1012
1029
|
building_min_height_grid[i, j] = [[min_heights[i, j], building_height_grid[i, j]]]
|
|
1013
1030
|
else:
|
|
1014
1031
|
building_min_height_grid[i, j] = []
|
|
1015
|
-
|
|
1016
|
-
# Fix north-south orientation by flipping grids
|
|
1017
|
-
building_height_grid = np.flipud(building_height_grid)
|
|
1018
|
-
building_id_grid = np.flipud(building_id_grid)
|
|
1019
|
-
building_min_height_grid = np.flipud(building_min_height_grid)
|
|
1020
|
-
|
|
1032
|
+
|
|
1021
1033
|
return building_height_grid, building_min_height_grid, building_id_grid, filtered_gdf
|
|
1022
1034
|
|
|
1023
1035
|
def create_building_height_grid_from_open_building_temporal_polygon(meshsize, rectangle_vertices, output_dir):
|
|
@@ -9,14 +9,14 @@ voxcity/downloader/osm.py,sha256=9nOVcVE50N76F5uquJbNIFr8Xajff4ac2Uj2oSGcFrc,425
|
|
|
9
9
|
voxcity/downloader/overture.py,sha256=4YG2DMwUSSyZKUw_o8cGhMmAkPJon82aPqOFBvrre-Y,11987
|
|
10
10
|
voxcity/downloader/utils.py,sha256=tz6wt4B9BhEOyvoF5OYXlr8rUd5cBEDedWL3j__oT70,3099
|
|
11
11
|
voxcity/exporter/__init__.py,sha256=dvyWJ184Eik9tFc0VviGbzTQzZi7O0JNyrqi_n39pVI,94
|
|
12
|
-
voxcity/exporter/cityles.py,sha256=
|
|
12
|
+
voxcity/exporter/cityles.py,sha256=OhsGODC8bFq0MTGc0nBnqRyqnqg1JXL7UaL66a-CKsQ,17418
|
|
13
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/generator.py,sha256=J61i6-bvgOlNQWgxlkSvOZ7CLAjRgh_XRYwslWkKxVM,55756
|
|
17
17
|
voxcity/geoprocessor/__init__.py,sha256=WYxcAQrjGucIvGHF0JTC6rONZzL3kCms1S2vpzS6KaU,127
|
|
18
18
|
voxcity/geoprocessor/draw.py,sha256=avXQwbGQWG3ZPPI8mwy0YN0K_aG4NMBdXI0vDg7yad0,35837
|
|
19
|
-
voxcity/geoprocessor/grid.py,sha256=
|
|
19
|
+
voxcity/geoprocessor/grid.py,sha256=D4sqoIGK2P1U8uuVQZ-447SD0Yrv6qS_zlmDtKoLDe8,71257
|
|
20
20
|
voxcity/geoprocessor/mesh.py,sha256=A7uaCMWfm82KEoYPfQYpxv6xMtQKaU2PBVDfKTpngqg,32027
|
|
21
21
|
voxcity/geoprocessor/network.py,sha256=YynqR0nq_NUra_cQ3Z_56KxfRia1b6-hIzGCj3QT-wE,25137
|
|
22
22
|
voxcity/geoprocessor/polygon.py,sha256=DfzXf6R-qoWXEZv1z1aHCVfr-DCuCFw6lieQT5cNHPA,61188
|
|
@@ -30,8 +30,8 @@ voxcity/utils/lc.py,sha256=722Gz3lPbgAp0mmTZ-g-QKBbAnbxrcgaYwb1sa7q8Sk,16189
|
|
|
30
30
|
voxcity/utils/material.py,sha256=H8K8Lq4wBL6dQtgj7esUW2U6wLCOTeOtelkTDJoRgMo,10007
|
|
31
31
|
voxcity/utils/visualization.py,sha256=ZR9N-XKfydeSStO53IM2hGXyZJoeBiAyIMWw9Cb2MPM,116449
|
|
32
32
|
voxcity/utils/weather.py,sha256=2Jtg-rIVJcsTtiKE-KuDnhIqS1-MSS16_zFRzj6zmu4,36435
|
|
33
|
-
voxcity-0.6.
|
|
34
|
-
voxcity-0.6.
|
|
35
|
-
voxcity-0.6.
|
|
36
|
-
voxcity-0.6.
|
|
37
|
-
voxcity-0.6.
|
|
33
|
+
voxcity-0.6.7.dist-info/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
|
|
34
|
+
voxcity-0.6.7.dist-info/LICENSE,sha256=s_jE1Df1nTPL4A_5GCGic5Zwex0CVaPKcAmSilxJPPE,1089
|
|
35
|
+
voxcity-0.6.7.dist-info/METADATA,sha256=lICJOYiGbMdF2Lee90GLEP-KBz3S71jHDaHucZrYs7g,26091
|
|
36
|
+
voxcity-0.6.7.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
37
|
+
voxcity-0.6.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|