voxcity 0.3.12__py3-none-any.whl → 0.3.14__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/__init__.py +1 -1
- voxcity/{download → downloader}/eubucco.py +21 -35
- voxcity/{download → downloader}/mbfp.py +5 -6
- voxcity/{download → downloader}/omt.py +11 -4
- voxcity/{download → downloader}/osm.py +31 -35
- voxcity/{download → downloader}/overture.py +7 -4
- voxcity/{file → exporter}/envimet.py +2 -2
- voxcity/{voxcity.py → generator.py} +34 -33
- voxcity/{geo → geoprocessor}/__init_.py +1 -0
- voxcity/{geo → geoprocessor}/draw.py +15 -22
- voxcity/{geo → geoprocessor}/grid.py +199 -90
- voxcity/{geo → geoprocessor}/network.py +7 -7
- voxcity/{file/geojson.py → geoprocessor/polygon.py} +146 -168
- voxcity/{sim → simulator}/solar.py +1 -1
- voxcity/{sim → simulator}/view.py +6 -7
- voxcity/utils/visualization.py +2 -2
- {voxcity-0.3.12.dist-info → voxcity-0.3.14.dist-info}/METADATA +2 -2
- voxcity-0.3.14.dist-info/RECORD +36 -0
- voxcity-0.3.12.dist-info/RECORD +0 -36
- /voxcity/{download → downloader}/__init__.py +0 -0
- /voxcity/{download → downloader}/gee.py +0 -0
- /voxcity/{download → downloader}/oemj.py +0 -0
- /voxcity/{download → downloader}/utils.py +0 -0
- /voxcity/{file → exporter}/__init_.py +0 -0
- /voxcity/{file → exporter}/magicavoxel.py +0 -0
- /voxcity/{file → exporter}/obj.py +0 -0
- /voxcity/{geo → geoprocessor}/utils.py +0 -0
- /voxcity/{sim → simulator}/__init_.py +0 -0
- /voxcity/{sim → simulator}/utils.py +0 -0
- {voxcity-0.3.12.dist-info → voxcity-0.3.14.dist-info}/AUTHORS.rst +0 -0
- {voxcity-0.3.12.dist-info → voxcity-0.3.14.dist-info}/LICENSE +0 -0
- {voxcity-0.3.12.dist-info → voxcity-0.3.14.dist-info}/WHEEL +0 -0
- {voxcity-0.3.12.dist-info → voxcity-0.3.14.dist-info}/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ from shapely.geometry import box
|
|
|
13
13
|
from scipy.interpolate import griddata
|
|
14
14
|
from shapely.errors import GEOSException
|
|
15
15
|
import geopandas as gpd
|
|
16
|
+
from rtree import index
|
|
16
17
|
|
|
17
18
|
from .utils import (
|
|
18
19
|
initialize_geod,
|
|
@@ -21,18 +22,18 @@ from .utils import (
|
|
|
21
22
|
create_building_polygons,
|
|
22
23
|
convert_format_lat_lon
|
|
23
24
|
)
|
|
24
|
-
from ..
|
|
25
|
+
from ..geoprocessor.polygon import (
|
|
25
26
|
filter_buildings,
|
|
26
27
|
extract_building_heights_from_geotiff,
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
extract_building_heights_from_gdf,
|
|
29
|
+
complement_building_heights_from_gdf
|
|
29
30
|
)
|
|
30
31
|
from ..utils.lc import (
|
|
31
32
|
get_class_priority,
|
|
32
33
|
create_land_cover_polygons,
|
|
33
34
|
get_dominant_class,
|
|
34
35
|
)
|
|
35
|
-
from ..
|
|
36
|
+
from ..downloader.gee import (
|
|
36
37
|
get_roi,
|
|
37
38
|
save_geotiff_open_buildings_temporal
|
|
38
39
|
)
|
|
@@ -304,11 +305,11 @@ def create_land_cover_grid_from_geotiff_polygon(tiff_path, mesh_size, land_cover
|
|
|
304
305
|
# Flip grid vertically to match geographic orientation
|
|
305
306
|
return np.flipud(grid)
|
|
306
307
|
|
|
307
|
-
def
|
|
308
|
-
"""Create a grid of land cover classes from
|
|
308
|
+
def create_land_cover_grid_from_gdf_polygon(gdf, meshsize, source, rectangle_vertices):
|
|
309
|
+
"""Create a grid of land cover classes from GeoDataFrame polygon data.
|
|
309
310
|
|
|
310
311
|
Args:
|
|
311
|
-
|
|
312
|
+
gdf (GeoDataFrame): GeoDataFrame containing land cover polygons
|
|
312
313
|
meshsize (float): Size of each grid cell in meters
|
|
313
314
|
source (str): Source of the land cover data to determine class priorities
|
|
314
315
|
rectangle_vertices (list): List of 4 (lon,lat) coordinate pairs defining the rectangle bounds
|
|
@@ -366,7 +367,13 @@ def create_land_cover_grid_from_geojson_polygon(geojson_data, meshsize, source,
|
|
|
366
367
|
plotting_box = box(extent[2], extent[0], extent[3], extent[1])
|
|
367
368
|
|
|
368
369
|
# Create spatial index for efficient polygon lookup
|
|
369
|
-
land_cover_polygons
|
|
370
|
+
land_cover_polygons = []
|
|
371
|
+
idx = index.Index()
|
|
372
|
+
for i, row in gdf.iterrows():
|
|
373
|
+
polygon = row.geometry
|
|
374
|
+
land_cover_class = row['class']
|
|
375
|
+
land_cover_polygons.append((polygon, land_cover_class))
|
|
376
|
+
idx.insert(i, polygon.bounds)
|
|
370
377
|
|
|
371
378
|
# Iterate through each grid cell
|
|
372
379
|
for i in range(grid_size[0]):
|
|
@@ -467,160 +474,261 @@ def create_height_grid_from_geotiff_polygon(tiff_path, mesh_size, polygon):
|
|
|
467
474
|
|
|
468
475
|
return np.flipud(grid)
|
|
469
476
|
|
|
470
|
-
def
|
|
477
|
+
def create_building_height_grid_from_gdf_polygon(
|
|
478
|
+
gdf,
|
|
479
|
+
meshsize,
|
|
480
|
+
rectangle_vertices,
|
|
481
|
+
gdf_comp=None,
|
|
482
|
+
geotiff_path_comp=None,
|
|
483
|
+
complement_building_footprints=None
|
|
484
|
+
):
|
|
471
485
|
"""
|
|
472
|
-
Create a building height grid from
|
|
486
|
+
Create a building height grid from GeoDataFrame data within a polygon boundary.
|
|
473
487
|
|
|
474
488
|
Args:
|
|
475
|
-
|
|
489
|
+
gdf (geopandas.GeoDataFrame): GeoDataFrame containing building information
|
|
476
490
|
meshsize (float): Size of mesh cells
|
|
477
491
|
rectangle_vertices (list): List of rectangle vertices defining the boundary
|
|
478
|
-
|
|
492
|
+
gdf_comp (geopandas.GeoDataFrame, optional): Complementary GeoDataFrame
|
|
479
493
|
geotiff_path_comp (str, optional): Path to complementary GeoTIFF file
|
|
480
|
-
complement_building_footprints (bool, optional): Whether to complement
|
|
481
|
-
|
|
494
|
+
complement_building_footprints (bool, optional): Whether to complement footprints
|
|
495
|
+
|
|
482
496
|
Returns:
|
|
483
497
|
tuple: (building_height_grid, building_min_height_grid, building_id_grid, filtered_buildings)
|
|
484
498
|
- building_height_grid (numpy.ndarray): Grid of building heights
|
|
485
|
-
- building_min_height_grid (numpy.ndarray): Grid of
|
|
499
|
+
- building_min_height_grid (numpy.ndarray): Grid of min building heights (list per cell)
|
|
486
500
|
- building_id_grid (numpy.ndarray): Grid of building IDs
|
|
487
|
-
- filtered_buildings (
|
|
501
|
+
- filtered_buildings (geopandas.GeoDataFrame): The buildings used (filtered_gdf)
|
|
488
502
|
"""
|
|
489
|
-
#
|
|
503
|
+
# --------------------------------------------------------------------------
|
|
504
|
+
# 1) INITIAL SETUP AND DATA FILTERING
|
|
505
|
+
# --------------------------------------------------------------------------
|
|
490
506
|
geod = initialize_geod()
|
|
491
507
|
vertex_0, vertex_1, vertex_3 = rectangle_vertices[0], rectangle_vertices[1], rectangle_vertices[3]
|
|
492
508
|
|
|
493
|
-
#
|
|
509
|
+
# Distances for each side
|
|
494
510
|
dist_side_1 = calculate_distance(geod, vertex_0[0], vertex_0[1], vertex_1[0], vertex_1[1])
|
|
495
511
|
dist_side_2 = calculate_distance(geod, vertex_0[0], vertex_0[1], vertex_3[0], vertex_3[1])
|
|
496
512
|
|
|
497
|
-
#
|
|
513
|
+
# Normalized vectors
|
|
498
514
|
side_1 = np.array(vertex_1) - np.array(vertex_0)
|
|
499
515
|
side_2 = np.array(vertex_3) - np.array(vertex_0)
|
|
500
516
|
u_vec = normalize_to_one_meter(side_1, dist_side_1)
|
|
501
517
|
v_vec = normalize_to_one_meter(side_2, dist_side_2)
|
|
502
518
|
|
|
503
|
-
#
|
|
519
|
+
# Grid parameters
|
|
504
520
|
origin = np.array(rectangle_vertices[0])
|
|
505
521
|
grid_size, adjusted_meshsize = calculate_grid_size(side_1, side_2, u_vec, v_vec, meshsize)
|
|
506
522
|
|
|
507
523
|
# Initialize output grids
|
|
508
524
|
building_height_grid = np.zeros(grid_size)
|
|
509
525
|
building_id_grid = np.zeros(grid_size)
|
|
526
|
+
# Use a Python list-of-lists or object array for min_height tracking
|
|
510
527
|
building_min_height_grid = np.empty(grid_size, dtype=object)
|
|
511
|
-
|
|
512
|
-
# Initialize min height grid with empty lists
|
|
513
528
|
for i in range(grid_size[0]):
|
|
514
529
|
for j in range(grid_size[1]):
|
|
515
530
|
building_min_height_grid[i, j] = []
|
|
516
531
|
|
|
517
|
-
#
|
|
518
|
-
extent = [
|
|
519
|
-
|
|
532
|
+
# Filter the input GeoDataFrame by bounding box
|
|
533
|
+
extent = [
|
|
534
|
+
min(coord[1] for coord in rectangle_vertices),
|
|
535
|
+
max(coord[1] for coord in rectangle_vertices),
|
|
536
|
+
min(coord[0] for coord in rectangle_vertices),
|
|
537
|
+
max(coord[0] for coord in rectangle_vertices)
|
|
538
|
+
]
|
|
520
539
|
plotting_box = box(extent[2], extent[0], extent[3], extent[1])
|
|
540
|
+
filtered_gdf = gdf[gdf.geometry.intersects(plotting_box)].copy()
|
|
521
541
|
|
|
522
|
-
#
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
# Handle complementary data sources
|
|
526
|
-
if geojson_data_comp:
|
|
527
|
-
filtered_geojson_data_comp = filter_buildings(geojson_data_comp, plotting_box)
|
|
542
|
+
# Optionally merge heights from complementary sources
|
|
543
|
+
if gdf_comp is not None:
|
|
544
|
+
filtered_gdf_comp = gdf_comp[gdf_comp.geometry.intersects(plotting_box)].copy()
|
|
528
545
|
if complement_building_footprints:
|
|
529
|
-
|
|
546
|
+
filtered_gdf = complement_building_heights_gdf(filtered_gdf, filtered_gdf_comp)
|
|
530
547
|
else:
|
|
531
|
-
|
|
548
|
+
filtered_gdf = extract_building_heights_from_gdf(filtered_gdf, filtered_gdf_comp)
|
|
532
549
|
elif geotiff_path_comp:
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
#
|
|
538
|
-
|
|
550
|
+
filtered_gdf = extract_building_heights_from_geotiff(geotiff_path_comp, filtered_gdf)
|
|
551
|
+
|
|
552
|
+
# --------------------------------------------------------------------------
|
|
553
|
+
# 2) PREPARE BUILDING POLYGONS & SPATIAL INDEX
|
|
554
|
+
# - fix geometries if needed
|
|
555
|
+
# - store bounding boxes
|
|
556
|
+
# --------------------------------------------------------------------------
|
|
557
|
+
building_polygons = []
|
|
558
|
+
for idx_b, row in filtered_gdf.iterrows():
|
|
559
|
+
polygon = row.geometry
|
|
560
|
+
height = row.get('height', None)
|
|
561
|
+
min_height = row.get('min_height', 0)
|
|
562
|
+
is_inner = row.get('is_inner', False)
|
|
563
|
+
feature_id = row.get('id', idx_b)
|
|
564
|
+
|
|
565
|
+
# Fix invalid geometry (buffer(0) or simplify if needed)
|
|
566
|
+
# Doing this once per building avoids repeated overhead per cell.
|
|
567
|
+
if not polygon.is_valid:
|
|
568
|
+
try:
|
|
569
|
+
# Attempt simple fix
|
|
570
|
+
polygon = polygon.buffer(0)
|
|
571
|
+
if not polygon.is_valid:
|
|
572
|
+
# Attempt a small simplify if buffer(0) didn't fix it
|
|
573
|
+
polygon = polygon.simplify(1e-8)
|
|
574
|
+
except Exception as e:
|
|
575
|
+
# If still invalid, we keep it as-is; intersection attempts
|
|
576
|
+
# will be caught in the main loop's try/except
|
|
577
|
+
pass
|
|
578
|
+
|
|
579
|
+
bounding_box = polygon.bounds # (minx, miny, maxx, maxy)
|
|
580
|
+
building_polygons.append(
|
|
581
|
+
(
|
|
582
|
+
polygon,
|
|
583
|
+
bounding_box,
|
|
584
|
+
height,
|
|
585
|
+
min_height,
|
|
586
|
+
is_inner,
|
|
587
|
+
feature_id
|
|
588
|
+
)
|
|
589
|
+
)
|
|
590
|
+
|
|
591
|
+
# Build R-tree index using bounding boxes
|
|
592
|
+
idx = index.Index()
|
|
593
|
+
for i_b, (poly, bbox, _, _, _, _) in enumerate(building_polygons):
|
|
594
|
+
idx.insert(i_b, bbox)
|
|
595
|
+
|
|
596
|
+
# --------------------------------------------------------------------------
|
|
597
|
+
# 3) MAIN GRID LOOP
|
|
598
|
+
# --------------------------------------------------------------------------
|
|
599
|
+
INTERSECTION_THRESHOLD = 0.3
|
|
539
600
|
|
|
540
|
-
# Process each grid cell
|
|
541
601
|
for i in range(grid_size[0]):
|
|
542
602
|
for j in range(grid_size[1]):
|
|
603
|
+
# Create the cell polygon once
|
|
543
604
|
cell = create_cell_polygon(origin, i, j, adjusted_meshsize, u_vec, v_vec)
|
|
544
|
-
# Ensure cell geometry is valid
|
|
545
605
|
if not cell.is_valid:
|
|
546
606
|
cell = cell.buffer(0)
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
607
|
+
|
|
608
|
+
cell_area = cell.area # reused for intersection ratio checks
|
|
609
|
+
|
|
610
|
+
# Find possible intersections from the index
|
|
611
|
+
potential = list(idx.intersection(cell.bounds))
|
|
612
|
+
if not potential:
|
|
552
613
|
continue
|
|
553
|
-
|
|
554
|
-
# Sort buildings by height
|
|
555
|
-
|
|
556
|
-
cell_buildings
|
|
557
|
-
|
|
558
|
-
|
|
614
|
+
|
|
615
|
+
# Sort buildings by height descending
|
|
616
|
+
# (height=None => treat as -inf so it sorts last)
|
|
617
|
+
cell_buildings = []
|
|
618
|
+
for k in potential:
|
|
619
|
+
bpoly, bbox, height, minh, inr, fid = building_polygons[k]
|
|
620
|
+
# We only sort by height for a stable layering
|
|
621
|
+
sort_val = height if (height is not None) else -float('inf')
|
|
622
|
+
cell_buildings.append((k, bpoly, bbox, height, minh, inr, fid, sort_val))
|
|
623
|
+
|
|
624
|
+
cell_buildings.sort(key=lambda x: x[-1], reverse=True)
|
|
625
|
+
|
|
559
626
|
found_intersection = False
|
|
560
627
|
all_zero_or_nan = True
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
628
|
+
|
|
629
|
+
for (
|
|
630
|
+
k,
|
|
631
|
+
polygon,
|
|
632
|
+
bbox,
|
|
633
|
+
height,
|
|
634
|
+
min_height,
|
|
635
|
+
is_inner,
|
|
636
|
+
feature_id,
|
|
637
|
+
_
|
|
638
|
+
) in cell_buildings:
|
|
564
639
|
try:
|
|
640
|
+
# Quick bounding-box check to skip expensive geometry intersection
|
|
641
|
+
# if bounding boxes cannot possibly meet the threshold
|
|
642
|
+
# --------------------------------------------------------
|
|
643
|
+
minx_p, miny_p, maxx_p, maxy_p = bbox
|
|
644
|
+
minx_c, miny_c, maxx_c, maxy_c = cell.bounds
|
|
645
|
+
|
|
646
|
+
# Overlap bounding box
|
|
647
|
+
overlap_minx = max(minx_p, minx_c)
|
|
648
|
+
overlap_miny = max(miny_p, miny_c)
|
|
649
|
+
overlap_maxx = min(maxx_p, maxx_c)
|
|
650
|
+
overlap_maxy = min(maxy_p, maxy_c)
|
|
651
|
+
|
|
652
|
+
if (overlap_maxx <= overlap_minx) or (overlap_maxy <= overlap_miny):
|
|
653
|
+
# No bounding-box overlap => skip
|
|
654
|
+
continue
|
|
655
|
+
|
|
656
|
+
# Area of bounding-box intersection
|
|
657
|
+
bbox_intersect_area = (overlap_maxx - overlap_minx) * (overlap_maxy - overlap_miny)
|
|
658
|
+
if bbox_intersect_area < INTERSECTION_THRESHOLD * cell_area:
|
|
659
|
+
# Even if the polygons overlap, they can't exceed threshold
|
|
660
|
+
continue
|
|
661
|
+
# --------------------------------------------------------
|
|
662
|
+
|
|
565
663
|
# Ensure valid geometry
|
|
566
664
|
if not polygon.is_valid:
|
|
567
665
|
polygon = polygon.buffer(0)
|
|
568
|
-
|
|
569
|
-
|
|
666
|
+
# If still invalid, let intersection attempt fail
|
|
667
|
+
|
|
570
668
|
if cell.intersects(polygon):
|
|
571
669
|
intersection = cell.intersection(polygon)
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if intersection_ratio > INTERSECTION_THRESHOLD:
|
|
670
|
+
inter_area = intersection.area
|
|
671
|
+
|
|
672
|
+
# If the fraction of cell covered > threshold
|
|
673
|
+
if (inter_area / cell_area) > INTERSECTION_THRESHOLD:
|
|
577
674
|
found_intersection = True
|
|
578
|
-
|
|
675
|
+
|
|
676
|
+
# If not an inner courtyard
|
|
579
677
|
if not is_inner:
|
|
580
|
-
# Store building information
|
|
581
678
|
building_min_height_grid[i, j].append([min_height, height])
|
|
582
679
|
building_id_grid[i, j] = feature_id
|
|
583
|
-
|
|
584
|
-
# Update height if valid
|
|
585
|
-
|
|
586
|
-
|
|
680
|
+
|
|
681
|
+
# Update building height if valid
|
|
682
|
+
if (
|
|
683
|
+
height is not None
|
|
684
|
+
and not np.isnan(height)
|
|
685
|
+
and height > 0
|
|
686
|
+
):
|
|
587
687
|
all_zero_or_nan = False
|
|
588
|
-
|
|
589
688
|
current_height = building_height_grid[i, j]
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
689
|
+
|
|
690
|
+
# Replace if we had 0, nan, or smaller height
|
|
691
|
+
if (
|
|
692
|
+
current_height == 0
|
|
693
|
+
or np.isnan(current_height)
|
|
694
|
+
or current_height < height
|
|
695
|
+
):
|
|
593
696
|
building_height_grid[i, j] = height
|
|
697
|
+
|
|
594
698
|
else:
|
|
595
|
-
#
|
|
699
|
+
# Inner courtyards => override with 0
|
|
596
700
|
building_min_height_grid[i, j] = [[0, 0]]
|
|
597
701
|
building_height_grid[i, j] = 0
|
|
598
702
|
found_intersection = True
|
|
599
703
|
all_zero_or_nan = False
|
|
704
|
+
# Because it's an inner courtyard, we can break
|
|
600
705
|
break
|
|
601
|
-
|
|
706
|
+
|
|
602
707
|
except (GEOSException, ValueError) as e:
|
|
603
|
-
# Attempt to fix
|
|
708
|
+
# Attempt to fix in a fallback
|
|
604
709
|
try:
|
|
605
710
|
simplified_polygon = polygon.simplify(1e-8)
|
|
606
711
|
if simplified_polygon.is_valid:
|
|
607
712
|
intersection = cell.intersection(simplified_polygon)
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
if
|
|
713
|
+
inter_area = intersection.area
|
|
714
|
+
|
|
715
|
+
if (inter_area / cell_area) > INTERSECTION_THRESHOLD:
|
|
611
716
|
found_intersection = True
|
|
612
|
-
|
|
613
717
|
if not is_inner:
|
|
614
718
|
building_min_height_grid[i, j].append([min_height, height])
|
|
615
719
|
building_id_grid[i, j] = feature_id
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
720
|
+
|
|
721
|
+
if (
|
|
722
|
+
height is not None
|
|
723
|
+
and not np.isnan(height)
|
|
724
|
+
and height > 0
|
|
725
|
+
):
|
|
619
726
|
all_zero_or_nan = False
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
building_height_grid[i, j]
|
|
623
|
-
|
|
727
|
+
if (
|
|
728
|
+
building_height_grid[i, j] == 0
|
|
729
|
+
or np.isnan(building_height_grid[i, j])
|
|
730
|
+
or building_height_grid[i, j] < height
|
|
731
|
+
):
|
|
624
732
|
building_height_grid[i, j] = height
|
|
625
733
|
else:
|
|
626
734
|
building_min_height_grid[i, j] = [[0, 0]]
|
|
@@ -629,14 +737,15 @@ def create_building_height_grid_from_geojson_polygon(geojson_data, meshsize, rec
|
|
|
629
737
|
all_zero_or_nan = False
|
|
630
738
|
break
|
|
631
739
|
except Exception as fix_error:
|
|
740
|
+
# Log and skip
|
|
632
741
|
print(f"Failed to process cell ({i}, {j}) - Building {k}: {str(fix_error)}")
|
|
633
742
|
continue
|
|
634
|
-
|
|
635
|
-
#
|
|
743
|
+
|
|
744
|
+
# If we found intersecting buildings but all were zero/NaN, mark as NaN
|
|
636
745
|
if found_intersection and all_zero_or_nan:
|
|
637
746
|
building_height_grid[i, j] = np.nan
|
|
638
747
|
|
|
639
|
-
return building_height_grid, building_min_height_grid, building_id_grid,
|
|
748
|
+
return building_height_grid, building_min_height_grid, building_id_grid, filtered_gdf
|
|
640
749
|
|
|
641
750
|
def create_building_height_grid_from_open_building_temporal_polygon(meshsize, rectangle_vertices, output_dir):
|
|
642
751
|
"""
|
|
@@ -671,7 +780,7 @@ def create_building_height_grid_from_open_building_temporal_polygon(meshsize, re
|
|
|
671
780
|
building_min_height_grid[i, j] = [[0, building_height_grid[i, j]]]
|
|
672
781
|
|
|
673
782
|
# Create building ID grid with sequential numbering for non-zero heights
|
|
674
|
-
filtered_buildings =
|
|
783
|
+
filtered_buildings = gpd.GeoDataFrame()
|
|
675
784
|
building_id_grid = np.zeros_like(building_height_grid, dtype=int)
|
|
676
785
|
non_zero_positions = np.nonzero(building_height_grid)
|
|
677
786
|
sequence = np.arange(1, len(non_zero_positions[0]) + 1)
|
|
@@ -165,11 +165,6 @@ def get_network_values(
|
|
|
165
165
|
|
|
166
166
|
return G, edge_gdf
|
|
167
167
|
|
|
168
|
-
# -------------------------------------------------------------------
|
|
169
|
-
# Optionally import your DEM helper
|
|
170
|
-
# -------------------------------------------------------------------
|
|
171
|
-
from voxcity.geo.grid import grid_to_geodataframe
|
|
172
|
-
|
|
173
168
|
# -------------------------------------------------------------------
|
|
174
169
|
# 1) Functions for interpolation, parallelization, and slope
|
|
175
170
|
# -------------------------------------------------------------------
|
|
@@ -511,8 +506,13 @@ def analyze_network_slopes(
|
|
|
511
506
|
legend_kwds={'label': f"{value_name} (%)"}
|
|
512
507
|
)
|
|
513
508
|
|
|
514
|
-
# Add basemap
|
|
515
|
-
ctx.add_basemap(
|
|
509
|
+
# Add basemap with the same extent as the rectangle
|
|
510
|
+
ctx.add_basemap(
|
|
511
|
+
ax,
|
|
512
|
+
source=settings['basemap_style'],
|
|
513
|
+
zoom=settings['zoom'],
|
|
514
|
+
bounds=(minx, miny, maxx, maxy) # Explicitly set the bounds of the basemap
|
|
515
|
+
)
|
|
516
516
|
|
|
517
517
|
# Set the plot limits to the bounding box of the rectangle
|
|
518
518
|
ax.set_xlim(minx, maxx)
|