import geopandas as gpd import pandas as pd from arcgis.features import FeatureLayer from arcgis.geometry import Geometry from shapely.geometry import shape from shapely.ops import unary_union # all data processing functions def get_gdf_from_feature_layer(url): # Access the ArcGIS feature layer feature_layer = FeatureLayer(url) # Use the query() method to get all features where 'Borough' is 'MN' sdf = feature_layer.query(where="Borough='MN'", out_sr=4326, as_df=True) # Convert the 'SHAPE' column from ArcGIS's JSON-based format into a Shapely geometry sdf['geometry'] = sdf['SHAPE'].apply(lambda x: Geometry(x).as_shapely) # Convert the SpatialDataFrame to a GeoDataFrame gdf = gpd.GeoDataFrame(sdf, geometry='geometry') return gdf def process_buildings(input_gdf, sensitive_sites_gdf, default_building_height_m, multiplier_factor): # List to store all intersected sensitive sites intersected_sites = [] # List to store all buffers buffers = [] intersection_desc = "" # Iterate over each building in the input file for idx, building in input_gdf.iterrows(): building_name = building.get('building_name', 'Unnamed building') # If the 'building_height' field exists and its value is not null or zero for this building, # use it as the building height. Otherwise, use the default building height provided by the user. if 'building_height' in building and pd.notnull(building['building_height']) and building['building_height'] != 0: building_height_m = building['building_height'] * 0.3048 else: building_height_m = default_building_height_m buffer_distance_m = building_height_m * multiplier_factor # Convert building's geometry to EPSG:3857 for accurate meter-based distance measurement building_geometry = gpd.GeoSeries([building['geometry']], crs="EPSG:4326") building_geometry_m = building_geometry.to_crs("EPSG:3857") # Create a buffer around the building and convert it to a GeoDataFrame building_buffer = building_geometry_m.buffer(buffer_distance_m) building_buffer_gdf = gpd.GeoDataFrame(geometry=building_buffer, crs="EPSG:3857") building_buffer_gdf = building_buffer_gdf.to_crs("EPSG:4326") # Convert back to feet for storing and printing, rounding to the nearest foot building_height_ft = round(building_height_m / 0.3048) buffer_distance_ft = round(buffer_distance_m / 0.3048) # Assign additional attributes building_buffer_gdf['building_name'] = building_name building_buffer_gdf['building_height'] = building_height_ft building_buffer_gdf['buffer_distance'] = buffer_distance_ft buffers.append(building_buffer_gdf) # Check if the buffer intersects with any sensitive sites intersects = gpd.overlay(building_buffer_gdf, sensitive_sites_gdf, how='intersection') if not intersects.empty: building_intersect_desc = f"Building {idx} ({building_name}), height: {building_height_ft}, buffer distance: {buffer_distance_ft} is in the vicinity of a sensitive site." intersected_sites.append(intersects) else: building_intersect_desc = f"Building {idx} ({building_name}), height: {building_height_ft}, buffer distance: {buffer_distance_ft} is not in the vicinity of any sensitive sites." if intersection_desc == "": intersection_desc = building_intersect_desc else: intersection_desc += "\n" + building_intersect_desc return buffers, intersected_sites, intersection_desc def get_max_extent(*gdfs): # takes in unlimited number of gdfs and calculates max/min xy extents minx = min(gdf.total_bounds[0] for gdf in gdfs) miny = min(gdf.total_bounds[1] for gdf in gdfs) maxx = max(gdf.total_bounds[2] for gdf in gdfs) maxy = max(gdf.total_bounds[3] for gdf in gdfs) return minx, miny, maxx, maxy