khushidhar1210 commited on
Commit
b4a6f1f
·
verified ·
1 Parent(s): 14d6d54

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +110 -101
app.py CHANGED
@@ -1,6 +1,6 @@
1
  import geopandas as gpd
2
  import pandas as pd
3
- from shapely.geometry import Polygon, LineString, Point
4
  from shapely.ops import unary_union
5
  import xml.etree.ElementTree as ET
6
  import zipfile
@@ -9,22 +9,8 @@ import matplotlib.pyplot as plt
9
  import streamlit as st
10
  from transformers import pipeline
11
 
12
- # Function to remove Z values (flatten 3D to 2D)
13
- def drop_z(geom):
14
- """Convert 3D geometry to 2D by removing Z values"""
15
- if geom.geom_type == 'Polygon':
16
- # Check if the geometry has a Z value and only then unpack
17
- return Polygon([(x, y) for x, y, *_ in geom.exterior.coords] if len(geom.exterior.coords[0]) == 3 else geom.exterior.coords)
18
- elif geom.geom_type == 'LineString':
19
- # Check if the geometry has a Z value and only then unpack
20
- return LineString([(x, y) for x, y, *_ in geom.coords] if len(geom.coords[0]) == 3 else geom.coords)
21
- elif geom.geom_type == 'Point':
22
- # Check if the point has a Z value and only then unpack
23
- return Point(geom.x, geom.y) if len(geom.coords) == 2 else Point(geom.x, geom.y, geom.z)
24
- return geom # Return unchanged if not 3D
25
-
26
- # ✅ Function to parse KML file
27
- def parse_kml(kml_file):
28
  cont = kml_file.decode('utf-8') # Decode bytes to string
29
  k = ET.ElementTree(ET.fromstring(cont))
30
  root = k.getroot()
@@ -32,142 +18,165 @@ def parse_kml(kml_file):
32
 
33
  shapes = []
34
  for mark in root.findall('.//kml:Placemark', ns):
35
- # Extract Polygon
36
- polygon = mark.find('.//kml:Polygon/kml:outerBoundaryIs/kml:LinearRing/kml:coordinates', ns)
37
  if polygon is not None:
38
  coordinates = polygon.text.strip().split()
39
- coords = [(float(lon), float(lat), float(alt)) for lon, lat, alt in [coord.split(',') for coord in coordinates]]
40
- shapes.append(Polygon(coords))
41
-
42
- # Extract LineString
43
  line = mark.find('.//kml:LineString/kml:coordinates', ns)
44
  if line is not None:
45
  coordinates = line.text.strip().split()
46
- coords = [(float(lon), float(lat), float(alt)) for lon, lat, alt in [coord.split(',') for coord in coordinates]]
47
  shapes.append(LineString(coords))
48
-
49
- # Extract Point
50
  point = mark.find('.//kml:Point/kml:coordinates', ns)
51
  if point is not None:
52
- lon, lat, alt = map(float, point.text.strip().split(','))
53
- shapes.append(Point(lon, lat, alt))
54
 
55
  return shapes if shapes else None
56
 
57
- # Function to extract KMZ file
58
- def extract_kmz(kmz_file):
59
  with zipfile.ZipFile(kmz_file, 'r') as zip_ref:
60
  zip_ref.extractall('temp_kml')
61
  kml_file = [f for f in os.listdir('temp_kml') if f.endswith('.kml')][0]
62
  with open(os.path.join('temp_kml', kml_file), 'rb') as f:
63
- return parse_kml(f.read())
64
 
65
- # Choose between KML and KMZ
66
- def choose_file(uploaded_file):
67
- file_bytes = uploaded_file.read()
68
- return extract_kmz(file_bytes) if uploaded_file.name.endswith('.kmz') else parse_kml(file_bytes)
 
 
 
69
 
70
- # Streamlit UI
71
  st.title("Flood Zone Analysis")
72
- uploaded_file = st.file_uploader("Upload KML/KMZ file", type=['kml', 'kmz'])
73
 
74
- # Compare boundaries
75
- def check_boundary(kml_shapes, gdf):
76
- if not kml_shapes:
77
- return "Invalid KML shape or no valid polygon found.", None
78
-
79
- kml_gdf = gpd.GeoDataFrame(geometry=kml_shapes, crs="EPSG:4326")
80
- kml_gdf['geometry'] = kml_gdf['geometry'].apply(drop_z) # Remove Z values
81
- kml_gdf = kml_gdf.to_crs(epsg=3857)
 
 
 
 
82
 
83
- overlaps = []
84
- for kml_shape in kml_gdf.geometry:
 
 
 
 
85
  intersection = gdf[gdf.intersects(kml_shape)]
86
  if not intersection.empty:
87
  overlaps.append(intersection)
88
-
89
  if not overlaps:
90
  return "Boundary doesn't match", None
91
-
92
  every_int = unary_union([geom for intersect in overlaps for geom in intersect.geometry])
93
  return overlaps, every_int
94
 
95
- # Calculate land use and flood zones
96
- def calculate_land(overlaps, every_int):
97
- all_data = pd.concat(overlaps)
98
- all_data['area_acres'] = all_data.geometry.area / 4046.86 # Convert to acres
99
-
100
- flood_zones = {zone: all_data[all_data['FLD_ZONE'] == zone]['area_acres'].sum() for zone in all_data['FLD_ZONE'].unique()}
101
- non_flooded_area = all_data[~all_data['FLD_ZONE'].isin(['A', 'AE', 'AH', 'AO', 'VE'])]['area_acres'].sum()
102
- merged_acreage = every_int.area / 4046.86
103
-
104
- return flood_zones, non_flooded_area, merged_acreage
105
-
106
- # Generate summary
107
- def generate_summary(flood_zones, non_flooded, total_acreage):
108
  summarizer = pipeline("summarization", model="gpt2")
109
- flood_summary = "\n".join([f" Zone {zone}: {flood_zones.get(zone, 0):.2f} acres" for zone in ['A', 'AE', 'AH', 'AO', 'VE']])
110
 
 
 
 
111
  prompt = f"""
112
  **Total Land Area**: {total_acreage:.2f} acres
113
- **Usable Area**: {non_flooded:.2f} acres
114
  **Flood-prone Zones**:
115
- {flood_summary}
116
- Summarize this in 2-3 sentences.
117
  """
 
118
  response = summarizer(prompt, max_length=200, min_length=30, do_sample=False)
 
119
  return response[0]['summary_text']
120
 
121
- # Main processing
122
- if uploaded_file is not None:
123
- # Load shapefile
124
  kent = gpd.read_file("K_FLD_HAZ_AR.shp")
125
  nc = gpd.read_file("N_FLD_HAZ_AR.shp")
126
  sussex = gpd.read_file("S_FLD_HAZ_AR.shp")
 
 
127
  dela = gpd.GeoDataFrame(pd.concat([kent, nc, sussex], ignore_index=True))
128
 
129
- # First, check if the shapefile already has a CRS
130
- if dela.crs is None:
131
- # Set the CRS for the shapefile if it doesn't have one
132
- dela = dela.set_crs("EPSG:4326", allow_override=True)
133
- else:
134
- # If the shapefile already has a CRS, make sure it's in the same CRS
135
- dela = dela.to_crs("EPSG:4326")
136
 
137
- # Reproject to Web Mercator (EPSG:3857)
138
  dela = dela.to_crs(epsg=3857)
139
 
140
- # Ensure valid geometries
141
  dela['geometry'] = dela['geometry'].apply(lambda x: x.buffer(0) if not x.is_valid else x)
142
 
143
- # Process uploaded KML/KMZ
144
- kml_shapes = choose_file(uploaded_file)
145
- if kml_shapes:
146
- # Remove Z values
147
- kml_shapes = [drop_z(geom) for geom in kml_shapes]
148
 
149
- # Compare with SHP
150
- result, every_int = check_boundary(kml_shapes, dela)
 
 
151
 
152
- if isinstance(result, str):
153
- st.write(result)
 
 
 
 
 
 
154
  else:
155
- flood_zones, non_flooded, merged_acreage = calculate_land(result, every_int)
156
  st.write(f"Flood Zone Areas:")
157
- for zone, area in flood_zones.items():
158
  st.write(f" Zone {zone}: {area:.2f} acres")
159
- st.write(f"\nNon-Flooded Land Area: {non_flooded:.2f} acres")
160
- st.write(f"\nMerged Area of Intersected Boundary: {merged_acreage:.2f} acres")
161
- summary = generate_summary(flood_zones, non_flooded, merged_acreage)
162
- st.write(f"Summary: {summary}")
163
-
164
- # Show map
165
- fig, ax = plt.subplots(figsize=(10, 10))
166
- dela.plot(ax=ax, color='blue', alpha=0.5)
167
- gpd.GeoDataFrame(geometry=[every_int], crs=dela.crs).plot(ax=ax, color='red', alpha=0.7)
168
- gpd.GeoDataFrame(geometry=kml_shapes, crs="EPSG:4326").to_crs(epsg=3857).plot(ax=ax, color='green', alpha=0.3)
169
- st.pyplot(fig)
 
 
 
 
 
 
 
 
 
 
170
  else:
171
  st.write("No valid geometries found in the uploaded KML file.")
172
  else:
173
- st.write("Please upload a KML/KMZ file.")
 
1
  import geopandas as gpd
2
  import pandas as pd
3
+ from shapely.geometry import shape, Polygon, LineString, Point
4
  from shapely.ops import unary_union
5
  import xml.etree.ElementTree as ET
6
  import zipfile
 
9
  import streamlit as st
10
  from transformers import pipeline
11
 
12
+ # For KML access, extracting 3D coordinates (Polygon Z)
13
+ def p(kml_file):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  cont = kml_file.decode('utf-8') # Decode bytes to string
15
  k = ET.ElementTree(ET.fromstring(cont))
16
  root = k.getroot()
 
18
 
19
  shapes = []
20
  for mark in root.findall('.//kml:Placemark', ns):
21
+ polygon = mark.find('.//kml:Polygon/kml:coordinates', ns)
 
22
  if polygon is not None:
23
  coordinates = polygon.text.strip().split()
24
+ coords = [(float(lon), float(lat), float(z) if z else 0) for lon, lat, z in [coord.split(',') for coord in coordinates]]
25
+ shapes.append(Polygon([coords])) # Make it a Polygon with Z
26
+
 
27
  line = mark.find('.//kml:LineString/kml:coordinates', ns)
28
  if line is not None:
29
  coordinates = line.text.strip().split()
30
+ coords = [(float(lon), float(lat), float(z) if z else 0) for lon, lat, z in [coord.split(',') for coord in coordinates]]
31
  shapes.append(LineString(coords))
32
+
 
33
  point = mark.find('.//kml:Point/kml:coordinates', ns)
34
  if point is not None:
35
+ lon, lat, z = point.text.strip().split(',')
36
+ shapes.append(Point(float(lon), float(lat), float(z) if z else 0))
37
 
38
  return shapes if shapes else None
39
 
40
+ # For file extraction if it is in KMZ form
41
+ def ext(kmz_file):
42
  with zipfile.ZipFile(kmz_file, 'r') as zip_ref:
43
  zip_ref.extractall('temp_kml')
44
  kml_file = [f for f in os.listdir('temp_kml') if f.endswith('.kml')][0]
45
  with open(os.path.join('temp_kml', kml_file), 'rb') as f:
46
+ return p(f.read())
47
 
48
+ # See if it is a kml or kmz file
49
+ def choose(upf):
50
+ file_bytes = upf.read()
51
+ if upf.name.endswith('.kmz'):
52
+ return ext(file_bytes)
53
+ else:
54
+ return p(file_bytes)
55
 
56
+ # For file uploading
57
  st.title("Flood Zone Analysis")
58
+ upf = st.file_uploader("Upload KML/KMZ file", type=['kml', 'kmz'])
59
 
60
+ # Convert 2D to 3D if needed (add default Z = 0)
61
+ def convert_to_3d(geom):
62
+ """Convert 2D geometries to 3D by adding a Z value (default = 0)"""
63
+ if geom.geom_type == 'Polygon':
64
+ coords = [(x, y, 0) for x, y in geom.exterior.coords]
65
+ return Polygon(coords)
66
+ elif geom.geom_type == 'LineString':
67
+ coords = [(x, y, 0) for x, y in geom.coords]
68
+ return LineString(coords)
69
+ elif geom.geom_type == 'Point':
70
+ return Point(geom.x, geom.y, 0)
71
+ return geom # Return unchanged if not 2D
72
 
73
+ # For comparing the boundary between KML and shapefile
74
+ def bound(f, gdf):
75
+ if f.empty: # Handle invalid KML shapes
76
+ return "Invalid KML shape or no valid polygon found.", None
77
+ overlaps = [] # Save matching boundaries
78
+ for kml_shape in f:
79
  intersection = gdf[gdf.intersects(kml_shape)]
80
  if not intersection.empty:
81
  overlaps.append(intersection)
 
82
  if not overlaps:
83
  return "Boundary doesn't match", None
 
84
  every_int = unary_union([geom for intersect in overlaps for geom in intersect.geometry])
85
  return overlaps, every_int
86
 
87
+ # Find common bound's Acreage and Usable Land
88
+ def land(overlaps, every_int):
89
+ all = pd.concat(overlaps)
90
+ all['area'] = all.geometry.area
91
+ all['area_acres'] = all['area'] / 4046.86 # Convert to acres
92
+ fza = {zone: all[all['FLD_ZONE'] == zone]['area_acres'].sum() for zone in all['FLD_ZONE'].unique()}
93
+ areas = ['A', 'AE', 'AH', 'AO', 'VE']
94
+ non = all[~all['FLD_ZONE'].isin(areas)]['area_acres'].sum()
95
+ merged_area = every_int.area / 4046.86
96
+ return fza, non, merged_area
97
+
98
+ # Initial summary was with GPT-2
99
+ def summ(fza, non, total_acreage):
100
  summarizer = pipeline("summarization", model="gpt2")
 
101
 
102
+ areas = ['A', 'AE', 'AH', 'AO', 'VE']
103
+ flood_zone_summary = "\n".join([f" Zone {zone}: {fza.get(zone, 0):.2f} acres" for zone in areas])
104
+
105
  prompt = f"""
106
  **Total Land Area**: {total_acreage:.2f} acres
107
+ **Usable Area**: {non:.2f} acres
108
  **Flood-prone Zones**:
109
+ {flood_zone_summary}
110
+ Summarize the above given data in 2-3 sentences.
111
  """
112
+
113
  response = summarizer(prompt, max_length=200, min_length=30, do_sample=False)
114
+
115
  return response[0]['summary_text']
116
 
117
+ if upf is not None:
118
+ # Read shapefiles and convert them to 3D if needed
 
119
  kent = gpd.read_file("K_FLD_HAZ_AR.shp")
120
  nc = gpd.read_file("N_FLD_HAZ_AR.shp")
121
  sussex = gpd.read_file("S_FLD_HAZ_AR.shp")
122
+
123
+ # Combine them
124
  dela = gpd.GeoDataFrame(pd.concat([kent, nc, sussex], ignore_index=True))
125
 
126
+ # Add Coordinate Reference System
127
+ dela = dela.set_crs(kent.crs, allow_override=True)
128
+
129
+ # Convert to 3D (add Z = 0 if missing)
130
+ dela['geometry'] = dela['geometry'].apply(convert_to_3d)
 
 
131
 
 
132
  dela = dela.to_crs(epsg=3857)
133
 
134
+ # Fix invalid geometries
135
  dela['geometry'] = dela['geometry'].apply(lambda x: x.buffer(0) if not x.is_valid else x)
136
 
137
+ # Upload KML/KMZ file
138
+ f = choose(upf)
 
 
 
139
 
140
+ if f:
141
+ # Check if KML has valid geometries
142
+ kml_gdf = gpd.GeoDataFrame(geometry=f, crs="EPSG:4326")
143
+ kml_gdf = kml_gdf.to_crs(epsg=3857)
144
 
145
+ # Convert KML to 3D (add Z = 0 if missing)
146
+ kml_gdf['geometry'] = kml_gdf['geometry'].apply(convert_to_3d)
147
+
148
+ # Compare KML and Shapefile
149
+ intersection, every_int = bound(kml_gdf.geometry, dela)
150
+
151
+ if isinstance(intersection, str):
152
+ st.write(intersection)
153
  else:
154
+ flood_zone_areas, non, merged_area = land(intersection, every_int)
155
  st.write(f"Flood Zone Areas:")
156
+ for zone, area in flood_zone_areas.items():
157
  st.write(f" Zone {zone}: {area:.2f} acres")
158
+ st.write(f"\nNon-Flooded Land Area: {non:.2f} acres")
159
+ st.write(f"\nMerged Area of Intersected Boundary: {merged_area:.2f} acres")
160
+ summary = summ(flood_zone_areas, non, merged_area)
161
+ st.write(f"GPT-2 Summary: {summary}")
162
+
163
+ # Show map
164
+ fig, ax = plt.subplots(figsize=(10, 10))
165
+
166
+ # shapefile
167
+ dela.plot(ax=ax, color='blue', alpha=0.5)
168
+
169
+ # Show overlap with KML
170
+ if intersection:
171
+ intersection_geom = unary_union([geom for intersect in intersection for geom in intersect.geometry])
172
+ gpd.GeoDataFrame(geometry=[intersection_geom], crs=dela.crs).plot(ax=ax, color='red', alpha=0.7)
173
+
174
+ # Plot the KML boundary (green color)
175
+ kml_gdf.plot(ax=ax, color='green', alpha=0.3)
176
+
177
+ # Display the plot
178
+ st.pyplot(fig)
179
  else:
180
  st.write("No valid geometries found in the uploaded KML file.")
181
  else:
182
+ st.write("Please upload a KML/KMZ file to continue.")