Spaces:
Running
Running
import geopandas as gpd | |
import pandas as pd | |
from shapely.geometry import shape, Polygon, LineString, Point | |
from shapely.ops import unary_union | |
import xml.etree.ElementTree as ET | |
import zipfile | |
import os | |
import matplotlib.pyplot as plt | |
import streamlit as st | |
from transformers import pipeline | |
# For KML access, extracting 3D coordinates (Polygon Z) | |
def p(kml_file): | |
cont = kml_file.decode('utf-8') # Decode bytes to string | |
k = ET.ElementTree(ET.fromstring(cont)) | |
root = k.getroot() | |
ns = {'kml': 'http://www.opengis.net/kml/2.2'} | |
shapes = [] | |
for mark in root.findall('.//kml:Placemark', ns): | |
polygon = mark.find('.//kml:Polygon/kml:coordinates', ns) | |
if polygon is not None: | |
coordinates = polygon.text.strip().split() | |
coords = [(float(lon), float(lat), float(z) if z else 0) for lon, lat, z in [coord.split(',') for coord in coordinates]] | |
shapes.append(Polygon([coords])) # Make it a Polygon with Z | |
line = mark.find('.//kml:LineString/kml:coordinates', ns) | |
if line is not None: | |
coordinates = line.text.strip().split() | |
coords = [(float(lon), float(lat), float(z) if z else 0) for lon, lat, z in [coord.split(',') for coord in coordinates]] | |
shapes.append(LineString(coords)) | |
point = mark.find('.//kml:Point/kml:coordinates', ns) | |
if point is not None: | |
lon, lat, z = point.text.strip().split(',') | |
shapes.append(Point(float(lon), float(lat), float(z) if z else 0)) | |
return shapes if shapes else None | |
# For file extraction if it is in KMZ form | |
def ext(kmz_file): | |
with zipfile.ZipFile(kmz_file, 'r') as zip_ref: | |
zip_ref.extractall('temp_kml') | |
kml_file = [f for f in os.listdir('temp_kml') if f.endswith('.kml')][0] | |
with open(os.path.join('temp_kml', kml_file), 'rb') as f: | |
return p(f.read()) | |
# See if it is a kml or kmz file | |
def choose(upf): | |
file_bytes = upf.read() | |
if upf.name.endswith('.kmz'): | |
return ext(file_bytes) | |
else: | |
return p(file_bytes) | |
# For file uploading | |
st.title("Flood Zone Analysis") | |
upf = st.file_uploader("Upload KML/KMZ file", type=['kml', 'kmz']) | |
# Convert 2D to 3D if needed (add default Z = 0) | |
def convert_to_3d(geom): | |
"""Convert 2D geometries to 3D by adding a Z value (default = 0)""" | |
if geom.geom_type == 'Polygon': | |
coords = [(x, y, 0) for x, y in geom.exterior.coords] | |
return Polygon(coords) | |
elif geom.geom_type == 'LineString': | |
coords = [(x, y, 0) for x, y in geom.coords] | |
return LineString(coords) | |
elif geom.geom_type == 'Point': | |
return Point(geom.x, geom.y, 0) | |
return geom # Return unchanged if not 2D | |
# For comparing the boundary between KML and shapefile | |
def bound(f, gdf): | |
if f.empty: # Handle invalid KML shapes | |
return "Invalid KML shape or no valid polygon found.", None | |
overlaps = [] # Save matching boundaries | |
for kml_shape in f: | |
intersection = gdf[gdf.intersects(kml_shape)] | |
if not intersection.empty: | |
overlaps.append(intersection) | |
if not overlaps: | |
return "Boundary doesn't match", None | |
every_int = unary_union([geom for intersect in overlaps for geom in intersect.geometry]) | |
return overlaps, every_int | |
# Find common bound's Acreage and Usable Land | |
def land(overlaps, every_int): | |
all = pd.concat(overlaps) | |
all['area'] = all.geometry.area | |
all['area_acres'] = all['area'] / 4046.86 # Convert to acres | |
fza = {zone: all[all['FLD_ZONE'] == zone]['area_acres'].sum() for zone in all['FLD_ZONE'].unique()} | |
areas = ['A', 'AE', 'AH', 'AO', 'VE'] | |
non = all[~all['FLD_ZONE'].isin(areas)]['area_acres'].sum() | |
merged_area = every_int.area / 4046.86 | |
return fza, non, merged_area | |
# Initial summary was with GPT-2 | |
def summ(fza, non, total_acreage): | |
summarizer = pipeline("summarization", model="gpt2") | |
areas = ['A', 'AE', 'AH', 'AO', 'VE'] | |
flood_zone_summary = "\n".join([f" Zone {zone}: {fza.get(zone, 0):.2f} acres" for zone in areas]) | |
prompt = f""" | |
**Total Land Area**: {total_acreage:.2f} acres | |
**Usable Area**: {non:.2f} acres | |
**Flood-prone Zones**: | |
{flood_zone_summary} | |
Summarize the above given data in 2-3 sentences. | |
""" | |
response = summarizer(prompt, max_length=200, min_length=30, do_sample=False) | |
return response[0]['summary_text'] | |
if upf is not None: | |
# Read shapefiles and convert them to 3D if needed | |
kent = gpd.read_file("K_FLD_HAZ_AR.shp") | |
nc = gpd.read_file("N_FLD_HAZ_AR.shp") | |
sussex = gpd.read_file("S_FLD_HAZ_AR.shp") | |
# Combine them | |
dela = gpd.GeoDataFrame(pd.concat([kent, nc, sussex], ignore_index=True)) | |
# Add Coordinate Reference System | |
dela = dela.set_crs(kent.crs, allow_override=True) | |
# Convert to 3D (add Z = 0 if missing) | |
dela['geometry'] = dela['geometry'].apply(convert_to_3d) | |
dela = dela.to_crs(epsg=3857) | |
# Fix invalid geometries | |
dela['geometry'] = dela['geometry'].apply(lambda x: x.buffer(0) if not x.is_valid else x) | |
# Upload KML/KMZ file | |
f = choose(upf) | |
if f: | |
# Check if KML has valid geometries | |
kml_gdf = gpd.GeoDataFrame(geometry=f, crs="EPSG:4326") | |
kml_gdf = kml_gdf.to_crs(epsg=3857) | |
# Convert KML to 3D (add Z = 0 if missing) | |
kml_gdf['geometry'] = kml_gdf['geometry'].apply(convert_to_3d) | |
# Compare KML and Shapefile | |
intersection, every_int = bound(kml_gdf.geometry, dela) | |
if isinstance(intersection, str): | |
st.write(intersection) | |
else: | |
flood_zone_areas, non, merged_area = land(intersection, every_int) | |
st.write(f"Flood Zone Areas:") | |
for zone, area in flood_zone_areas.items(): | |
st.write(f" Zone {zone}: {area:.2f} acres") | |
st.write(f"\nNon-Flooded Land Area: {non:.2f} acres") | |
st.write(f"\nMerged Area of Intersected Boundary: {merged_area:.2f} acres") | |
summary = summ(flood_zone_areas, non, merged_area) | |
st.write(f"GPT-2 Summary: {summary}") | |
# Show map | |
fig, ax = plt.subplots(figsize=(10, 10)) | |
# shapefile | |
dela.plot(ax=ax, color='blue', alpha=0.5) | |
# Show overlap with KML | |
if intersection: | |
intersection_geom = unary_union([geom for intersect in intersection for geom in intersect.geometry]) | |
gpd.GeoDataFrame(geometry=[intersection_geom], crs=dela.crs).plot(ax=ax, color='red', alpha=0.7) | |
# Plot the KML boundary (green color) | |
kml_gdf.plot(ax=ax, color='green', alpha=0.3) | |
# Display the plot | |
st.pyplot(fig) | |
else: | |
st.write("No valid geometries found in the uploaded KML file.") | |
else: | |
st.write("Please upload a KML/KMZ file to continue.") | |