voxcity 0.3.13__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.

@@ -120,55 +120,72 @@ def get_geojson_from_gpkg(gpkg_path, rectangle_vertices):
120
120
  geojson = filter_and_convert_gdf_to_geojson(gdf, rectangle_vertices)
121
121
  return geojson
122
122
 
123
- def extract_building_heights_from_geojson(geojson_data_0: List[Dict], geojson_data_1: List[Dict]) -> List[Dict]:
123
+ def extract_building_heights_from_gdf(gdf_0: gpd.GeoDataFrame, gdf_1: gpd.GeoDataFrame) -> gpd.GeoDataFrame:
124
124
  """
125
- Extract building heights from one GeoJSON dataset and apply them to another based on spatial overlap.
125
+ Extract building heights from one GeoDataFrame and apply them to another based on spatial overlap.
126
126
 
127
127
  Args:
128
- geojson_data_0 (List[Dict]): Primary GeoJSON features to update with heights
129
- geojson_data_1 (List[Dict]): Reference GeoJSON features containing height data
128
+ gdf_0 (gpd.GeoDataFrame): Primary GeoDataFrame to update with heights
129
+ gdf_1 (gpd.GeoDataFrame): Reference GeoDataFrame containing height data
130
130
 
131
131
  Returns:
132
- List[Dict]: Updated primary GeoJSON features with extracted heights
132
+ gpd.GeoDataFrame: Updated primary GeoDataFrame with extracted heights
133
133
  """
134
- # Convert reference dataset to Shapely polygons with height info
135
- reference_buildings = []
136
- for feature in geojson_data_1:
137
- geom = shape(feature['geometry'])
138
- height = feature['properties']['height']
139
- reference_buildings.append((geom, height))
134
+ # Make a copy of input GeoDataFrame to avoid modifying original
135
+ gdf_primary = gdf_0.copy()
136
+ gdf_ref = gdf_1.copy()
137
+
138
+ # Make sure height columns exist
139
+ if 'height' not in gdf_primary.columns:
140
+ gdf_primary['height'] = 0.0
141
+ if 'height' not in gdf_ref.columns:
142
+ gdf_ref['height'] = 0.0
140
143
 
141
144
  # Initialize counters for statistics
142
145
  count_0 = 0 # Buildings without height
143
146
  count_1 = 0 # Buildings updated with height
144
147
  count_2 = 0 # Buildings with no height data found
145
148
 
146
- # Process primary dataset and update heights where needed
147
- updated_geojson_data_0 = []
148
- for feature in geojson_data_0:
149
- geom = shape(feature['geometry'])
150
- height = feature['properties']['height']
151
- if height == 0:
152
- count_0 += 1
149
+ # Create spatial index for reference buildings
150
+ from rtree import index
151
+ spatial_index = index.Index()
152
+ for i, geom in enumerate(gdf_ref.geometry):
153
+ if geom.is_valid:
154
+ spatial_index.insert(i, geom.bounds)
155
+
156
+ # Process each building in primary dataset that needs height data
157
+ for idx_primary, row in gdf_primary.iterrows():
158
+ if row['height'] == 0:
159
+ count_0 += 1
160
+ geom = row.geometry
161
+
153
162
  # Calculate weighted average height based on overlapping areas
154
163
  overlapping_height_area = 0
155
164
  overlapping_area = 0
156
- for ref_geom, ref_height in reference_buildings:
165
+
166
+ # Get potential intersecting buildings using spatial index
167
+ potential_matches = list(spatial_index.intersection(geom.bounds))
168
+
169
+ # Check intersections with reference buildings
170
+ for ref_idx in potential_matches:
171
+ if ref_idx >= len(gdf_ref):
172
+ continue
173
+
174
+ ref_row = gdf_ref.iloc[ref_idx]
157
175
  try:
158
- if geom.intersects(ref_geom):
159
- overlap_area = geom.intersection(ref_geom).area
160
- overlapping_height_area += ref_height * overlap_area
176
+ if geom.intersects(ref_row.geometry):
177
+ overlap_area = geom.intersection(ref_row.geometry).area
178
+ overlapping_height_area += ref_row['height'] * overlap_area
161
179
  overlapping_area += overlap_area
162
- except GEOSException as e:
180
+ except GEOSException:
163
181
  # Try to fix invalid geometries using buffer(0)
164
- print(f"GEOS error at a building polygon {ref_geom}")
165
182
  try:
166
- fixed_ref_geom = ref_geom.buffer(0)
183
+ fixed_ref_geom = ref_row.geometry.buffer(0)
167
184
  if geom.intersects(fixed_ref_geom):
168
- overlap_area = geom.intersection(ref_geom).area
169
- overlapping_height_area += ref_height * overlap_area
185
+ overlap_area = geom.intersection(fixed_ref_geom).area
186
+ overlapping_height_area += ref_row['height'] * overlap_area
170
187
  overlapping_area += overlap_area
171
- except Exception as fix_error:
188
+ except Exception:
172
189
  print(f"Failed to fix polygon")
173
190
  continue
174
191
 
@@ -177,19 +194,17 @@ def extract_building_heights_from_geojson(geojson_data_0: List[Dict], geojson_da
177
194
  count_1 += 1
178
195
  # Calculate weighted average height
179
196
  new_height = overlapping_height_area / overlapping_area
180
- feature['properties']['height'] = new_height
197
+ gdf_primary.at[idx_primary, 'height'] = new_height
181
198
  else:
182
199
  count_2 += 1
183
- feature['properties']['height'] = np.nan
184
-
185
- updated_geojson_data_0.append(feature)
200
+ gdf_primary.at[idx_primary, 'height'] = np.nan
186
201
 
187
202
  # Print statistics about height updates
188
203
  if count_0 > 0:
189
- print(f"{count_0} of the total {len(geojson_data_0)} building footprint from OSM did not have height data.")
204
+ print(f"{count_0} of the total {len(gdf_primary)} building footprint from OSM did not have height data.")
190
205
  print(f"For {count_1} of these building footprints without height, values from Microsoft Building Footprints were assigned.")
191
206
 
192
- return updated_geojson_data_0
207
+ return gdf_primary
193
208
 
194
209
  # from typing import List, Dict
195
210
  # from shapely.geometry import shape
@@ -346,18 +361,17 @@ def geojson_to_gdf(geojson_data, id_col='id'):
346
361
  return gdf
347
362
 
348
363
 
349
- def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
364
+ def complement_building_heights_from_gdf(gdf_0, gdf_1,
350
365
  primary_id='id', ref_id='id'):
351
366
  """
352
367
  Use a vectorized approach with GeoPandas to:
353
- 1) Convert both datasets to GeoDataFrames
354
- 2) Find intersections and compute weighted average heights
355
- 3) Update heights in the primary dataset
356
- 4) Add non-intersecting buildings from the reference dataset
368
+ 1) Find intersections and compute weighted average heights
369
+ 2) Update heights in the primary dataset
370
+ 3) Add non-intersecting buildings from the reference dataset
357
371
 
358
372
  Args:
359
- geojson_data_0 (List[Dict]): Primary GeoJSON-like features
360
- geojson_data_1 (List[Dict]): Reference GeoJSON-like features
373
+ gdf_0 (gpd.GeoDataFrame): Primary GeoDataFrame
374
+ gdf_1 (gpd.GeoDataFrame): Reference GeoDataFrame
361
375
  primary_id (str): Name of the unique identifier in primary dataset's properties
362
376
  ref_id (str): Name of the unique identifier in reference dataset's properties
363
377
 
@@ -365,11 +379,9 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
365
379
  gpd.GeoDataFrame: Updated GeoDataFrame (including new buildings).
366
380
  You can convert it back to a list of dict features if needed.
367
381
  """
368
- # ----------------------------------------------------------------
369
- # 1) Convert primary and reference data to GeoDataFrames
370
- # ----------------------------------------------------------------
371
- gdf_primary = geojson_to_gdf(geojson_data_0, id_col=primary_id)
372
- gdf_ref = geojson_to_gdf(geojson_data_1, id_col=ref_id)
382
+ # Make a copy of input GeoDataFrames to avoid modifying originals
383
+ gdf_primary = gdf_0.copy()
384
+ gdf_ref = gdf_1.copy()
373
385
 
374
386
  # Ensure both are in the same CRS, e.g. EPSG:4326 or some projected CRS
375
387
  # If needed, do something like:
@@ -383,7 +395,7 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
383
395
  gdf_ref['height'] = 0.0
384
396
 
385
397
  # ----------------------------------------------------------------
386
- # 2) Intersection to compute areas for overlapping buildings
398
+ # 1) Intersection to compute areas for overlapping buildings
387
399
  # ----------------------------------------------------------------
388
400
  # We'll rename columns to avoid collision after overlay
389
401
  gdf_primary = gdf_primary.rename(columns={'height': 'height_primary'})
@@ -398,7 +410,7 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
398
410
  intersect_gdf['height_area'] = intersect_gdf['height_ref'] * intersect_gdf['intersect_area']
399
411
 
400
412
  # ----------------------------------------------------------------
401
- # 3) Aggregate to get weighted average height for each primary building
413
+ # 2) Aggregate to get weighted average height for each primary building
402
414
  # ----------------------------------------------------------------
403
415
  # We group by the primary building ID, summing up the area and the 'height_area'
404
416
  group_cols = {
@@ -411,7 +423,7 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
411
423
  grouped['weighted_height'] = grouped['height_area'] / grouped['intersect_area']
412
424
 
413
425
  # ----------------------------------------------------------------
414
- # 4) Merge aggregated results back to the primary GDF
426
+ # 3) Merge aggregated results back to the primary GDF
415
427
  # ----------------------------------------------------------------
416
428
  # After merging, the primary GDF will have a column 'weighted_height'
417
429
  gdf_primary = gdf_primary.merge(grouped['weighted_height'],
@@ -428,7 +440,7 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
428
440
  gdf_primary['height_primary'] = gdf_primary['height_primary'].fillna(np.nan)
429
441
 
430
442
  # ----------------------------------------------------------------
431
- # 5) Identify reference buildings that do not intersect any primary building
443
+ # 4) Identify reference buildings that do not intersect any primary building
432
444
  # ----------------------------------------------------------------
433
445
  # Another overlay or spatial join can do this:
434
446
  # Option A: use 'difference' on reference to get non-overlapping parts, but that can chop polygons.
@@ -450,7 +462,7 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
450
462
  # Also rename any other properties you prefer. For clarity, keep an ID so you know they came from reference.
451
463
 
452
464
  # ----------------------------------------------------------------
453
- # 6) Combine the updated primary GDF with the new reference buildings
465
+ # 5) Combine the updated primary GDF with the new reference buildings
454
466
  # ----------------------------------------------------------------
455
467
  # First, rename columns in updated primary GDF
456
468
  gdf_primary = gdf_primary.rename(columns={'height_primary': 'height'})
@@ -461,10 +473,6 @@ def complement_building_heights_gdf(geojson_data_0, geojson_data_1,
461
473
  # Concatenate
462
474
  final_gdf = pd.concat([gdf_primary, gdf_ref_non_intersect], ignore_index=True)
463
475
 
464
- # ----------------------------------------------------------------
465
- # Return the combined GeoDataFrame
466
- # (You can convert it back to a list of GeoJSON-like dictionaries)
467
- # ----------------------------------------------------------------
468
476
  return final_gdf
469
477
 
470
478
 
@@ -492,34 +500,15 @@ def gdf_to_geojson_dicts(gdf, id_col='id'):
492
500
 
493
501
  return features
494
502
 
495
-
496
- def complement_building_heights_from_geojson(geojson_data_0, geojson_data_1,
497
- primary_id='id', ref_id='id'):
498
- """
499
- High-level function that wraps the GeoPandas approach end-to-end.
500
- Returns a list of GeoJSON-like feature dicts.
503
+ def load_gdf_from_multiple_gz(file_paths):
501
504
  """
502
- # 1) Complement building heights using the GeoDataFrame approach
503
- final_gdf = complement_building_heights_gdf(
504
- geojson_data_0,
505
- geojson_data_1,
506
- primary_id=primary_id,
507
- ref_id=ref_id
508
- )
509
-
510
- # 2) Convert back to geojson-like dict format
511
- updated_features = gdf_to_geojson_dicts(final_gdf, id_col=primary_id)
512
- return updated_features
513
-
514
- def load_geojsons_from_multiple_gz(file_paths):
515
- """
516
- Load GeoJSON features from multiple gzipped files.
505
+ Load GeoJSON features from multiple gzipped files into a GeoDataFrame.
517
506
 
518
507
  Args:
519
508
  file_paths (list): List of paths to gzipped GeoJSON files
520
509
 
521
510
  Returns:
522
- list: Combined list of GeoJSON features from all files
511
+ gpd.GeoDataFrame: GeoDataFrame containing features from all files
523
512
  """
524
513
  geojson_objects = []
525
514
  for gz_file_path in file_paths:
@@ -539,7 +528,15 @@ def load_geojsons_from_multiple_gz(file_paths):
539
528
  geojson_objects.append(data)
540
529
  except json.JSONDecodeError as e:
541
530
  print(f"Skipping line in {gz_file_path} due to JSONDecodeError: {e}")
542
- return geojson_objects
531
+
532
+ # Convert list of GeoJSON features to GeoDataFrame
533
+ # swap_coordinates(geojson_objects)
534
+ gdf = gpd.GeoDataFrame.from_features(geojson_objects)
535
+
536
+ # Set CRS to WGS84 which is typically used for these files
537
+ gdf.set_crs(epsg=4326, inplace=True)
538
+
539
+ return gdf
543
540
 
544
541
  def filter_buildings(geojson_data, plotting_box):
545
542
  """
@@ -573,24 +570,19 @@ def filter_buildings(geojson_data, plotting_box):
573
570
  print(f"Skipping feature due to geometry error: {e}")
574
571
  return filtered_features
575
572
 
576
- def extract_building_heights_from_geotiff(geotiff_path, geojson_data):
573
+ def extract_building_heights_from_geotiff(geotiff_path, gdf):
577
574
  """
578
- Extract building heights from a GeoTIFF raster for GeoJSON building footprints.
575
+ Extract building heights from a GeoTIFF raster for building footprints in a GeoDataFrame.
579
576
 
580
577
  Args:
581
578
  geotiff_path (str): Path to the GeoTIFF height raster
582
- geojson_data (Union[str, list]): GeoJSON features or JSON string of features
579
+ gdf (gpd.GeoDataFrame): GeoDataFrame containing building footprints
583
580
 
584
581
  Returns:
585
- Union[str, list]: Updated GeoJSON features with extracted heights in same format as input
582
+ gpd.GeoDataFrame: Updated GeoDataFrame with extracted heights
586
583
  """
587
- # Handle input format - convert string to object if needed
588
- if isinstance(geojson_data, str):
589
- geojson = json.loads(geojson_data)
590
- input_was_string = True
591
- else:
592
- geojson = geojson_data
593
- input_was_string = False
584
+ # Make a copy to avoid modifying the input
585
+ gdf = gdf.copy()
594
586
 
595
587
  # Initialize counters for statistics
596
588
  count_0 = 0 # Buildings without height
@@ -602,48 +594,43 @@ def extract_building_heights_from_geotiff(geotiff_path, geojson_data):
602
594
  # Create coordinate transformer from WGS84 to raster CRS
603
595
  transformer = Transformer.from_crs(CRS.from_epsg(4326), src.crs, always_xy=True)
604
596
 
605
- # Process each building feature
606
- for feature in geojson:
607
- if (feature['geometry']['type'] == 'Polygon') & (feature['properties']['height']<=0):
608
- count_0 += 1
609
- # Transform coordinates from (lon, lat) to raster CRS
610
- coords = feature['geometry']['coordinates'][0]
611
- transformed_coords = [transformer.transform(lon, lat) for lon, lat in coords]
612
-
613
- # Create polygon in raster CRS
614
- polygon = shape({"type": "Polygon", "coordinates": [transformed_coords]})
597
+ # Filter buildings that need height processing
598
+ mask_condition = (gdf.geometry.geom_type == 'Polygon') & (gdf.get('height', 0) <= 0)
599
+ buildings_to_process = gdf[mask_condition]
600
+ count_0 = len(buildings_to_process)
601
+
602
+ for idx, row in buildings_to_process.iterrows():
603
+ # Transform geometry to raster CRS
604
+ coords = list(row.geometry.exterior.coords)
605
+ transformed_coords = [transformer.transform(lon, lat) for lon, lat in coords]
606
+ polygon = shape({"type": "Polygon", "coordinates": [transformed_coords]})
607
+
608
+ try:
609
+ # Extract height values from raster within polygon
610
+ masked_data, _ = rasterio.mask.mask(src, [polygon], crop=True, all_touched=True)
611
+ heights = masked_data[0][masked_data[0] != src.nodata]
615
612
 
616
- try:
617
- # Extract height values from raster within polygon
618
- masked, mask_transform = mask(src, [polygon], crop=True, all_touched=True)
619
- heights = masked[0][masked[0] != src.nodata]
620
-
621
- # Calculate average height if valid samples exist
622
- if len(heights) > 0:
623
- count_1 += 1
624
- avg_height = np.mean(heights)
625
- feature['properties']['height'] = float(avg_height)
626
- else:
627
- count_2 += 1
628
- feature['properties']['height'] = 10
629
- print(f"No valid height data for feature: {feature['properties']}")
630
- except ValueError as e:
631
- print(f"Error processing feature: {feature['properties']}. Error: {str(e)}")
632
- feature['properties']['extracted_height'] = None
613
+ # Calculate average height if valid samples exist
614
+ if len(heights) > 0:
615
+ count_1 += 1
616
+ gdf.at[idx, 'height'] = float(np.mean(heights))
617
+ else:
618
+ count_2 += 1
619
+ gdf.at[idx, 'height'] = 10
620
+ print(f"No valid height data for building at index {idx}")
621
+ except ValueError as e:
622
+ print(f"Error processing building at index {idx}. Error: {str(e)}")
623
+ gdf.at[idx, 'height'] = None
633
624
 
634
625
  # Print statistics about height updates
635
626
  if count_0 > 0:
636
- print(f"{count_0} of the total {len(geojson_data)} building footprint from OSM did not have height data.")
627
+ print(f"{count_0} of the total {len(gdf)} building footprint from OSM did not have height data.")
637
628
  print(f"For {count_1} of these building footprints without height, values from Open Building 2.5D Temporal were assigned.")
638
629
  print(f"For {count_2} of these building footprints without height, no data exist in Open Building 2.5D Temporal. Height values of 10m were set instead")
639
630
 
640
- # Return result in same format as input
641
- if input_was_string:
642
- return json.dumps(geojson, indent=2)
643
- else:
644
- return geojson
631
+ return gdf
645
632
 
646
- def get_geojson_from_gpkg(gpkg_path, rectangle_vertices):
633
+ def get_gdf_from_gpkg(gpkg_path, rectangle_vertices):
647
634
  """
648
635
  Read a GeoPackage file and convert it to GeoJSON format within a bounding rectangle.
649
636
 
@@ -657,8 +644,18 @@ def get_geojson_from_gpkg(gpkg_path, rectangle_vertices):
657
644
  # Open and read the GPKG file
658
645
  print(f"Opening GPKG file: {gpkg_path}")
659
646
  gdf = gpd.read_file(gpkg_path)
660
- geojson = filter_and_convert_gdf_to_geojson(gdf, rectangle_vertices)
661
- return geojson
647
+
648
+ # Only set CRS if not already set
649
+ if gdf.crs is None:
650
+ gdf.set_crs(epsg=4326, inplace=True)
651
+ # Transform to WGS84 if needed
652
+ elif gdf.crs != "EPSG:4326":
653
+ gdf = gdf.to_crs(epsg=4326)
654
+
655
+ # Replace id column with index numbers
656
+ gdf['id'] = gdf.index
657
+
658
+ return gdf
662
659
 
663
660
  def swap_coordinates(features):
664
661
  """
@@ -702,12 +699,12 @@ def save_geojson(features, save_path):
702
699
  with open(save_path, 'w') as f:
703
700
  json.dump(geojson, f, indent=2)
704
701
 
705
- def find_building_containing_point(features, target_point):
702
+ def find_building_containing_point(building_gdf, target_point):
706
703
  """
707
704
  Find building IDs that contain a given point.
708
705
 
709
706
  Args:
710
- features (list): List of GeoJSON feature dictionaries
707
+ building_gdf (GeoDataFrame): GeoDataFrame containing building geometries and IDs
711
708
  target_point (tuple): Tuple of (lon, lat)
712
709
 
713
710
  Returns:
@@ -717,42 +714,29 @@ def find_building_containing_point(features, target_point):
717
714
  point = Point(target_point[0], target_point[1])
718
715
 
719
716
  id_list = []
720
- for feature in features:
721
- # Get polygon coordinates and convert to Shapely polygon
722
- coords = feature['geometry']['coordinates'][0]
723
- polygon = Polygon(coords)
724
-
717
+ for idx, row in building_gdf.iterrows():
718
+ # Skip any geometry that is not Polygon
719
+ if not isinstance(row.geometry, Polygon):
720
+ continue
721
+
725
722
  # Check if point is within polygon
726
- if polygon.contains(point):
727
- id_list.append(feature['properties']['id'])
723
+ if row.geometry.contains(point):
724
+ id_list.append(row.get('id', None))
728
725
 
729
726
  return id_list
730
727
 
731
- def get_buildings_in_drawn_polygon(building_geojson, drawn_polygon_vertices,
728
+ def get_buildings_in_drawn_polygon(building_gdf, drawn_polygon_vertices,
732
729
  operation='within'):
733
730
  """
734
- Given a list of building footprints and a set of drawn polygon
731
+ Given a GeoDataFrame of building footprints and a set of drawn polygon
735
732
  vertices (in lon, lat), return the building IDs that fall within or
736
733
  intersect the drawn polygon.
737
734
 
738
735
  Args:
739
- building_geojson (list):
740
- A list of GeoJSON features, each feature is a dict with:
741
- {
742
- "type": "Feature",
743
- "geometry": {
744
- "type": "Polygon",
745
- "coordinates": [
746
- [
747
- [lon1, lat1], [lon2, lat2], ...
748
- ]
749
- ]
750
- },
751
- "properties": {
752
- "id": ...
753
- ...
754
- }
755
- }
736
+ building_gdf (GeoDataFrame):
737
+ A GeoDataFrame containing building footprints with:
738
+ - geometry column containing Polygon geometries
739
+ - id column containing building IDs
756
740
 
757
741
  drawn_polygon_vertices (list):
758
742
  A list of (lon, lat) tuples representing the polygon drawn by the user.
@@ -771,25 +755,19 @@ def get_buildings_in_drawn_polygon(building_geojson, drawn_polygon_vertices,
771
755
 
772
756
  included_building_ids = []
773
757
 
774
- # Check each building in the GeoJSON
775
- for feature in building_geojson:
776
- # Skip any feature that is not Polygon
777
- if feature['geometry']['type'] != 'Polygon':
758
+ # Check each building in the GeoDataFrame
759
+ for idx, row in building_gdf.iterrows():
760
+ # Skip any geometry that is not Polygon
761
+ if not isinstance(row.geometry, Polygon):
778
762
  continue
779
763
 
780
- # Extract coordinates
781
- coords = feature['geometry']['coordinates'][0]
782
-
783
- # Create a Shapely polygon for the building
784
- building_polygon = Polygon(coords)
785
-
786
764
  # Depending on the operation, check the relationship
787
765
  if operation == 'intersect':
788
- if building_polygon.intersects(drawn_polygon_shapely):
789
- included_building_ids.append(feature['properties'].get('id', None))
766
+ if row.geometry.intersects(drawn_polygon_shapely):
767
+ included_building_ids.append(row.get('id', None))
790
768
  elif operation == 'within':
791
- if building_polygon.within(drawn_polygon_shapely):
792
- included_building_ids.append(feature['properties'].get('id', None))
769
+ if row.geometry.within(drawn_polygon_shapely):
770
+ included_building_ids.append(row.get('id', None))
793
771
  else:
794
772
  raise ValueError("operation must be 'intersect' or 'within'")
795
773
 
voxcity/simulator/view.py CHANGED
@@ -702,7 +702,7 @@ def compute_landmark_visibility(voxel_data, target_value=-30, view_height_voxel=
702
702
 
703
703
  return np.flipud(visibility_map)
704
704
 
705
- def get_landmark_visibility_map(voxcity_grid_ori, building_id_grid, building_geojson, meshsize, **kwargs):
705
+ def get_landmark_visibility_map(voxcity_grid_ori, building_id_grid, building_gdf, meshsize, **kwargs):
706
706
  """Generate a visibility map for landmark buildings in a voxel city.
707
707
 
708
708
  Places observers at valid locations and checks visibility to any part of the
@@ -712,7 +712,7 @@ def get_landmark_visibility_map(voxcity_grid_ori, building_id_grid, building_geo
712
712
  Args:
713
713
  voxcity_grid (ndarray): 3D array representing the voxel city
714
714
  building_id_grid (ndarray): 3D array mapping voxels to building IDs
715
- building_geojson (dict): GeoJSON data containing building features
715
+ building_gdf (GeoDataFrame): GeoDataFrame containing building features
716
716
  meshsize (float): Size of each voxel in meters
717
717
  **kwargs: Additional keyword arguments
718
718
  view_point_height (float): Height of observer viewpoint in meters
@@ -737,12 +737,11 @@ def get_landmark_visibility_map(voxcity_grid_ori, building_id_grid, building_geo
737
737
  colormap = kwargs.get("colormap", 'viridis')
738
738
 
739
739
  # Get landmark building IDs either directly or by finding buildings in rectangle
740
- features = building_geojson
741
740
  landmark_ids = kwargs.get('landmark_building_ids', None)
742
741
  landmark_polygon = kwargs.get('landmark_polygon', None)
743
742
  if landmark_ids is None:
744
743
  if landmark_polygon is not None:
745
- landmark_ids = get_buildings_in_drawn_polygon(building_geojson, landmark_polygon, operation='within')
744
+ landmark_ids = get_buildings_in_drawn_polygon(building_gdf, landmark_polygon, operation='within')
746
745
  else:
747
746
  rectangle_vertices = kwargs.get("rectangle_vertices", None)
748
747
  if rectangle_vertices is None:
@@ -757,7 +756,7 @@ def get_landmark_visibility_map(voxcity_grid_ori, building_id_grid, building_geo
757
756
  target_point = (center_lon, center_lat)
758
757
 
759
758
  # Find buildings at center point
760
- landmark_ids = find_building_containing_point(features, target_point)
759
+ landmark_ids = find_building_containing_point(building_gdf, target_point)
761
760
 
762
761
  # Mark landmark buildings in voxel grid with special value
763
762
  target_value = -30
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: voxcity
3
- Version: 0.3.13
3
+ Version: 0.3.14
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>
@@ -1,36 +1,36 @@
1
1
  voxcity/__init__.py,sha256=el9v3gfybHOF_GUYPeSOqN0-vCrTW0eU1mcvi0sEfeU,252
2
- voxcity/generator.py,sha256=n0nSzDYddmQDHi4pLOaN2YY8xfZC0Rbl8nD7MvYSmHg,33695
2
+ voxcity/generator.py,sha256=SDaX1gLAq8NEPVZD8h_bFzUQcm8FjcAJzAy6yMXVsxg,33441
3
3
  voxcity/downloader/__init__.py,sha256=OgGcGxOXF4tjcEL6DhOnt13DYPTvOigUelp5xIpTqM0,171
4
- voxcity/downloader/eubucco.py,sha256=BaaMitzPDFXjeH-F7XfDzULpq89hVNEDYpsxeStZYDM,15002
4
+ voxcity/downloader/eubucco.py,sha256=XCkkdEPNuWdrnuxzL80Ext37WsgiCiZGueb-aQV5rvI,14476
5
5
  voxcity/downloader/gee.py,sha256=j7jmzp44T3M6j_4DwhU9Y8Y6gqbZo1zFIlduQPc0jvk,14339
6
- voxcity/downloader/mbfp.py,sha256=xmITDh9mCMnLWjYfjarEr2FEwAW5812IncJxo33IC7c,3959
6
+ voxcity/downloader/mbfp.py,sha256=5fXq9S7qNVSLDdVtj67Da1pBAJP6kL4P8qLZTOmWqdw,3895
7
7
  voxcity/downloader/oemj.py,sha256=YlCuWBQfi40gfmwQcGDeHiPOs4Pk_jLZq65d5R3IGMU,7886
8
- voxcity/downloader/omt.py,sha256=EjzimZMFXcjWNRlUEwPIjeTmE4rPh_9bjsgZyro8_mo,8819
9
- voxcity/downloader/osm.py,sha256=HHSuj6jiQrThrfyJMWHE2nQ0Rqkx4UsXopk8AoNZS6Q,26536
10
- voxcity/downloader/overture.py,sha256=daOvsySC2KIcTcMJUSA7XdbMELJuyLAIM2vr1DRLGp0,7714
8
+ voxcity/downloader/omt.py,sha256=ByFvoQDnBOJF4qdVYNkDjn7cMvEhWwtD0mIV_T-zMEs,9017
9
+ voxcity/downloader/osm.py,sha256=0VpYuPRiO3iru3nHM_or0nTDFb8ytUUDZieFX6zxB9Q,26338
10
+ voxcity/downloader/overture.py,sha256=2m7pHymE60iaqxa3H4gxAMtJioHd831R5kCS73dxzW8,7821
11
11
  voxcity/downloader/utils.py,sha256=z6MdPxM96FWQVqvZW2Eg5pMewVHVysUP7F6ueeCwMfI,1375
12
12
  voxcity/exporter/__init_.py,sha256=cVyNyE6axEpSd3CT5hGuMOAlOyU1p8lVP4jkF1-0Ad8,94
13
13
  voxcity/exporter/envimet.py,sha256=m-y2IYw-yp45AT2wN9UIlxvMjvDvupTKzyfRJl057fE,24300
14
14
  voxcity/exporter/magicavoxel.py,sha256=Fsv7yGRXeKmp82xcG3rOb0t_HtoqltNq2tHl08xVlqY,7500
15
15
  voxcity/exporter/obj.py,sha256=oW-kPoZj53nfmO9tXP3Wvizq6Kkjh-QQR8UBexRuMiI,21609
16
16
  voxcity/geoprocessor/__init_.py,sha256=FFJFf6idmAtmNkwfKPt3ERGSIzjb8tt35D1n9QQbCA8,112
17
- voxcity/geoprocessor/draw.py,sha256=vhO5O8_Hc0eMOfnczz6k2ZqplTX64FA6DL-XryOxIJs,13557
18
- voxcity/geoprocessor/grid.py,sha256=bQvcNjUZ1D124c8QYAs1Pba63bfjRorGr-9wxde9M1w,36298
17
+ voxcity/geoprocessor/draw.py,sha256=7T99AUr2qdt1OTfn4W61_Lph7HGz0_WcgoY6Ezo7sB4,13246
18
+ voxcity/geoprocessor/grid.py,sha256=9i0LRa0Ko53a8V5QlnMwy_Qe2pNqq9C4M-ZaAQyjRUs,40129
19
19
  voxcity/geoprocessor/network.py,sha256=opb_kpUCAxDd1qtrWPStqR5reYZtVe96XxazNSen7Lk,18851
20
- voxcity/geoprocessor/polygon.py,sha256=1U8FYtTbOUrDmK6ZIdECXpf8FuUvT8QKgIhthypDQ54,33566
20
+ voxcity/geoprocessor/polygon.py,sha256=8fU2Ayu2Y_G1z7Mbj8KoSKVurdPuAVbASjGMVS36ftM,32249
21
21
  voxcity/geoprocessor/utils.py,sha256=1BRHp-DDeOA8HG8jplY7Eo75G3oXkVGL6DGONL4BA8A,19815
22
22
  voxcity/simulator/__init_.py,sha256=APdkcdaovj0v_RPOaA4SBvFUKT2RM7Hxuuz3Sux4gCo,65
23
23
  voxcity/simulator/solar.py,sha256=FOcHoUm4miJNyeCcGs2oL93Vu38Affyywt29dJcmIT4,31974
24
24
  voxcity/simulator/utils.py,sha256=sEYBB2-hLJxTiXQps1_-Fi7t1HN3-1OPOvBCWtgIisA,130
25
- voxcity/simulator/view.py,sha256=4H19dMrHNzLuGtkzVEdOTsF1fl_ZcRC29HIxGVJBlGE,36770
25
+ voxcity/simulator/view.py,sha256=zNbfTLQ2Jo0V5-rFA3-xamRjOuw3H3MBrLKpQp8x3hY,36737
26
26
  voxcity/utils/__init_.py,sha256=nLYrj2huBbDBNMqfchCwexGP8Tlt9O_XluVDG7MoFkw,98
27
27
  voxcity/utils/lc.py,sha256=RwPd-VY3POV3gTrBhM7TubgGb9MCd3nVah_G8iUEF7k,11562
28
28
  voxcity/utils/material.py,sha256=Vt3IID5Ft54HNJcEC4zi31BCPqi_687X3CSp7rXaRVY,5907
29
29
  voxcity/utils/visualization.py,sha256=3cNjJdwIaOlzwgK08S39F1VYNf7vu-x9d4UUiKwXRVo,31943
30
30
  voxcity/utils/weather.py,sha256=P6s1y_EstBL1OGP_MR_6u3vr-t6Uawg8uDckJnoI7FI,21482
31
- voxcity-0.3.13.dist-info/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
32
- voxcity-0.3.13.dist-info/LICENSE,sha256=-hGliOFiwUrUSoZiB5WF90xXGqinKyqiDI2t6hrnam8,1087
33
- voxcity-0.3.13.dist-info/METADATA,sha256=XWJTqfQEG1Qfvp9XsTMK_xUau4smVj0iUpQgjCCPlDw,25122
34
- voxcity-0.3.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
- voxcity-0.3.13.dist-info/top_level.txt,sha256=00b2U-LKfDllt6RL1R33MXie5MvxzUFye0NGD96t_8I,8
36
- voxcity-0.3.13.dist-info/RECORD,,
31
+ voxcity-0.3.14.dist-info/AUTHORS.rst,sha256=m82vkI5QokEGdcHof2OxK39lf81w1P58kG9ZNNAKS9U,175
32
+ voxcity-0.3.14.dist-info/LICENSE,sha256=-hGliOFiwUrUSoZiB5WF90xXGqinKyqiDI2t6hrnam8,1087
33
+ voxcity-0.3.14.dist-info/METADATA,sha256=qtO4S1XoVHN8Rd9dq2aKKo71tk2U7ap9WDH6koxMKWk,25122
34
+ voxcity-0.3.14.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
35
+ voxcity-0.3.14.dist-info/top_level.txt,sha256=00b2U-LKfDllt6RL1R33MXie5MvxzUFye0NGD96t_8I,8
36
+ voxcity-0.3.14.dist-info/RECORD,,