Source code for cityImage.buildings
"""Building GeoDataFrame helpers for cityImage.
Live building acquisition is delegated to OSMnx and file IO is delegated to
GeoPandas. This module keeps only small schema/selection helpers that preserve
cityImage's downstream semantics.
"""
from __future__ import annotations
from typing import Any
import geopandas as gpd
def _geometry_union(geometry: gpd.GeoSeries) -> Any:
"""Return a geometry union compatible with older/newer GeoPandas versions."""
try:
return geometry.union_all()
except AttributeError:
return geometry.unary_union
[docs]
def select_buildings_by_study_area(
larger_buildings_gdf: gpd.GeoDataFrame,
*,
method: str = "polygon",
polygon: Any = None,
distance: float = 1000,
) -> gpd.GeoDataFrame:
"""Select buildings within a polygon or centroid-distance study area.
Use GeoPandas/OSMnx to acquire buildings, ``standardize_buildings_gdf`` to
normalise schema, then this helper if a cityImage-style study-area subset is
needed.
"""
if larger_buildings_gdf.empty:
return gpd.GeoDataFrame(
columns=larger_buildings_gdf.columns,
geometry=larger_buildings_gdf.geometry.name
if hasattr(larger_buildings_gdf, "geometry")
else None,
crs=getattr(larger_buildings_gdf, "crs", None),
)
if method == "distance":
study_area = _geometry_union(larger_buildings_gdf.geometry).centroid.buffer(distance)
elif method == "polygon":
study_area = polygon
else:
raise ValueError("method must be either 'polygon' or 'distance'")
if study_area is None:
return gpd.GeoDataFrame(
columns=larger_buildings_gdf.columns,
geometry=larger_buildings_gdf.geometry.name,
crs=larger_buildings_gdf.crs,
)
return larger_buildings_gdf[larger_buildings_gdf.geometry.within(study_area)].copy()