npc0 commited on
Commit
6c809c9
·
verified ·
1 Parent(s): a2f75f4

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +107 -88
src/streamlit_app.py CHANGED
@@ -87,50 +87,49 @@ df = load_and_process_data()
87
  # This will be updated based on spatial and attribute filters
88
  filtered_df = df.copy()
89
 
90
- # --- 2. Initialize the Folium Map for Drawing (no initial points plotted) ---
91
- # Center the map around the mean of the actual data's centroids
92
- m = folium.Map(location=[df['latitude'].mean(), df['longitude'].mean()], zoom_start=12)
93
-
94
- # Add drawing tools to the map
95
- draw = Draw(
96
- export=True,
97
- filename="drawn_polygon.geojson",
98
- position="topleft",
99
- draw_options={
100
- "polyline": False, "rectangle": False, "circlemarker": False,
101
- "circle": False, "marker": False,
102
- "polygon": {
103
- "allowIntersection": False,
104
- "drawError": {"color": "#e0115f", "message": "Oups!"},
105
- "shapeOptions": {"color": "#ef233c", "fillOpacity": 0.5},
 
 
106
  },
107
- },
108
- edit_options={"edit": False, "remove": True},
109
- )
110
- m.add_child(draw)
111
 
112
- with st.expander("Draw a polygon on the map to spatially filter properties"):
113
- st.subheader("Draw a Polygon on the Map")
114
  st.info("Draw a polygon on the map to spatially filter properties. The filtered results will appear below.")
115
  output = st_folium(m, width=1000, height=600, returned_objects=["all_draw_features"])
116
-
117
  polygon_drawn = False
118
  shapely_polygon = None
119
  polygon_coords = None
120
-
121
  if output and output["all_draw_features"]:
122
  polygons = [
123
  feature["geometry"]["coordinates"]
124
  for feature in output["all_draw_features"]
125
  if feature["geometry"]["type"] == "Polygon"
126
  ]
127
-
128
  if polygons:
129
  polygon_coords = polygons[-1][0] # Get the coordinates of the last drawn polygon
130
  # Shapely Polygon expects (lon, lat) tuples, Folium provides (lat, lon)
131
  shapely_polygon = Polygon([(lon, lat) for lat, lon in polygon_coords])
132
  polygon_drawn = True
133
-
134
  # Apply spatial filter to the full dataframe based on centroid containment
135
  filtered_df = df[
136
  df.apply(
@@ -203,74 +202,94 @@ with st.form("attribute_filters"):
203
  st.info("Adjust filters and click 'Apply Attribute Filters'.")
204
 
205
 
206
- # --- 4. Display Filtered Data on a New Map and as a Table ---
207
- st.subheader("Filtered Properties Display")
208
- if not filtered_df.empty:
209
- # Calculate bounds for filtered data to set appropriate zoom
210
- min_lat, max_lat = filtered_df['latitude'].min(), filtered_df['latitude'].max()
211
- min_lon, max_lon = filtered_df['longitude'].min(), filtered_df['longitude'].max()
 
212
 
213
- if min_lat == max_lat and min_lon == max_lon: # Single point case
214
- filtered_map_center = [min_lat, min_lon]
215
- filtered_map_zoom = 18
216
- else:
217
- filtered_map_center = [filtered_df['latitude'].mean(), filtered_df['longitude'].mean()]
218
- lat_diff = max_lat - min_lat
219
- lon_diff = max_lon - min_lon
220
- # Heuristic for zoom level
221
- if max(lat_diff, lon_diff) < 0.001: filtered_map_zoom = 18
222
- elif max(lat_diff, lon_diff) < 0.01: filtered_map_zoom = 16
223
- elif max(lat_diff, lon_diff) < 0.1: filtered_map_zoom = 14
224
- else: filtered_map_zoom = 12
225
-
226
- filtered_m = folium.Map(location=filtered_map_center, zoom_start=filtered_map_zoom)
227
-
228
- # Add the drawn polygon to the new map if it exists
229
- if polygon_drawn and polygon_coords:
230
- folium.Polygon(
231
- locations=polygon_coords,
232
- color="#ef233c",
233
- fill=True,
234
- fill_color="#ef233c",
235
- fill_opacity=0.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  ).add_to(filtered_m)
237
 
238
- # Convert filtered_df back to GeoDataFrame for direct plotting of geometries
239
- filtered_gdf = gpd.GeoDataFrame(filtered_df, geometry='geometry')
240
-
241
- # Add filtered polygons to the map as GeoJSON layer
242
- folium.GeoJson(
243
- filtered_gdf.to_json(),
244
- style_function=lambda x: {
245
- 'fillColor': 'green',
246
- 'color': 'darkgreen',
247
- 'weight': 1,
248
- 'fillOpacity': 0.7
249
- },
250
- tooltip=folium.GeoJsonTooltip(
251
- fields=['PARCELID', 'zn_type', 'zn_area', 'fsi_total', 'prcnt_cver', 'height_metres', 'stories', 'ADDRESS_NUMBER', 'LINEAR_NAME_FULL'],
252
- aliases=['Parcel ID:', 'Zoning Type:', 'Lot Area (m²):', 'FSI:', 'Coverage (%):', 'Height (m):', 'Stories:', 'Address Num:', 'Street:'],
253
- localize=True
254
- )
255
- ).add_to(filtered_m)
256
-
257
- st_folium(filtered_m, width=1000, height=500)
258
 
259
- st.subheader("Filtered Properties Table")
260
- display_cols = ['PARCELID', 'zn_type', 'zn_area', 'fsi_total', 'prcnt_cver', 'height_metres', 'stories', 'ADDRESS_NUMBER', 'LINEAR_NAME_FULL']
261
- st.dataframe(filtered_df[display_cols])
 
262
 
263
- # --- 5. Export Data Button ---
264
- csv = filtered_df.to_csv(index=False).encode('utf-8')
265
- st.download_button(
266
- label="Export Filtered Data to CSV",
267
- data=csv,
268
- file_name="multiplex_coop_filtered_properties.csv",
269
- mime="text/csv",
270
- )
 
 
 
 
 
 
271
 
272
- else:
273
- st.warning("No properties match the current filters. Adjust your criteria or draw a polygon on the map.")
274
 
 
275
  st.markdown("---")
 
 
 
 
 
 
 
 
 
 
 
276
  st.markdown("This app demonstrates spatial and attribute filtering on the ProjectMultiplexCoop/PropertyBoundaries dataset from Hugging Face. FSI, Building Coverage, Height, and Stories are synthetic for demonstration.")
 
87
  # This will be updated based on spatial and attribute filters
88
  filtered_df = df.copy()
89
 
90
+ # --- 2. Map for Drawing (now in an expander) ---
91
+ with st.expander("Draw a Polygon on the Map", expanded=False):
92
+ # Center the map around the mean of the actual data's centroids
93
+ m = folium.Map(location=[df['latitude'].mean(), df['longitude'].mean()], zoom_start=12)
94
+
95
+ # Add drawing tools to the map
96
+ draw = Draw(
97
+ export=True,
98
+ filename="drawn_polygon.geojson",
99
+ position="topleft",
100
+ draw_options={
101
+ "polyline": False, "rectangle": False, "circlemarker": False,
102
+ "circle": False, "marker": False,
103
+ "polygon": {
104
+ "allowIntersection": False,
105
+ "drawError": {"color": "#e0115f", "message": "Oups!"},
106
+ "shapeOptions": {"color": "#ef233c", "fillOpacity": 0.5},
107
+ },
108
  },
109
+ edit_options={"edit": False, "remove": True},
110
+ )
111
+ m.add_child(draw)
 
112
 
 
 
113
  st.info("Draw a polygon on the map to spatially filter properties. The filtered results will appear below.")
114
  output = st_folium(m, width=1000, height=600, returned_objects=["all_draw_features"])
115
+
116
  polygon_drawn = False
117
  shapely_polygon = None
118
  polygon_coords = None
119
+
120
  if output and output["all_draw_features"]:
121
  polygons = [
122
  feature["geometry"]["coordinates"]
123
  for feature in output["all_draw_features"]
124
  if feature["geometry"]["type"] == "Polygon"
125
  ]
126
+
127
  if polygons:
128
  polygon_coords = polygons[-1][0] # Get the coordinates of the last drawn polygon
129
  # Shapely Polygon expects (lon, lat) tuples, Folium provides (lat, lon)
130
  shapely_polygon = Polygon([(lon, lat) for lat, lon in polygon_coords])
131
  polygon_drawn = True
132
+
133
  # Apply spatial filter to the full dataframe based on centroid containment
134
  filtered_df = df[
135
  df.apply(
 
202
  st.info("Adjust filters and click 'Apply Attribute Filters'.")
203
 
204
 
205
+ # --- 4. Display Filtered Data on a New Map and as a Table (now in an expander) ---
206
+ # The expander is expanded by default to show results immediately after filtering
207
+ with st.expander("Filtered Properties Display", expanded=True):
208
+ if not filtered_df.empty:
209
+ # Calculate bounds for filtered data to set appropriate zoom
210
+ min_lat, max_lat = filtered_df['latitude'].min(), filtered_df['latitude'].max()
211
+ min_lon, max_lon = filtered_df['longitude'].min(), filtered_df['longitude'].max()
212
 
213
+ if min_lat == max_lat and min_lon == max_lon: # Single point case
214
+ filtered_map_center = [min_lat, min_lon]
215
+ filtered_map_zoom = 18
216
+ else:
217
+ filtered_map_center = [filtered_df['latitude'].mean(), filtered_df['longitude'].mean()]
218
+ lat_diff = max_lat - min_lat
219
+ lon_diff = max_lon - min_lon
220
+ # Heuristic for zoom level
221
+ if max(lat_diff, lon_diff) < 0.001: filtered_map_zoom = 18
222
+ elif max(lat_diff, lon_diff) < 0.01: filtered_map_zoom = 16
223
+ elif max(lat_diff, lon_diff) < 0.1: filtered_map_zoom = 14
224
+ else: filtered_map_zoom = 12
225
+
226
+ filtered_m = folium.Map(location=filtered_map_center, zoom_start=filtered_map_zoom)
227
+
228
+ # Add the drawn polygon to the new map if it exists
229
+ if polygon_drawn and polygon_coords:
230
+ folium.Polygon(
231
+ locations=polygon_coords,
232
+ color="#ef233c",
233
+ fill=True,
234
+ fill_color="#ef233c",
235
+ fill_opacity=0.5
236
+ ).add_to(filtered_m)
237
+
238
+ # Convert filtered_df back to GeoDataFrame for direct plotting of geometries
239
+ filtered_gdf = gpd.GeoDataFrame(filtered_df, geometry='geometry')
240
+
241
+ # Add filtered polygons to the map as GeoJSON layer
242
+ folium.GeoJson(
243
+ filtered_gdf.to_json(),
244
+ style_function=lambda x: {
245
+ 'fillColor': 'green',
246
+ 'color': 'darkgreen',
247
+ 'weight': 1,
248
+ 'fillOpacity': 0.7
249
+ },
250
+ tooltip=folium.GeoJsonTooltip(
251
+ fields=['PARCELID', 'zn_type', 'zn_area', 'fsi_total', 'prcnt_cver', 'height_metres', 'stories', 'ADDRESS_NUMBER', 'LINEAR_NAME_FULL'],
252
+ aliases=['Parcel ID:', 'Zoning Type:', 'Lot Area (m²):', 'FSI:', 'Coverage (%):', 'Height (m):', 'Stories:', 'Address Num:', 'Street:'],
253
+ localize=True
254
+ )
255
  ).add_to(filtered_m)
256
 
257
+ st_folium(filtered_m, width=1000, height=500)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
258
 
259
+ st.subheader("Filtered Properties Table")
260
+ # Limit rows displayed in the dataframe to prevent MessageSizeError
261
+ MAX_ROWS_DISPLAY = 1000 # Define a reasonable limit
262
+ display_cols = ['PARCELID', 'zn_type', 'zn_area', 'fsi_total', 'prcnt_cver', 'height_metres', 'stories', 'ADDRESS_NUMBER', 'LINEAR_NAME_FULL']
263
 
264
+ if len(filtered_df) > MAX_ROWS_DISPLAY:
265
+ st.warning(f"Displaying only the first {MAX_ROWS_DISPLAY} rows of the filtered data ({len(filtered_df)} total properties). Download the full dataset below.")
266
+ st.dataframe(filtered_df[display_cols].head(MAX_ROWS_DISPLAY))
267
+ else:
268
+ st.dataframe(filtered_df[display_cols])
269
+
270
+ # --- 5. Export Data Button ---
271
+ csv = filtered_df.to_csv(index=False).encode('utf-8')
272
+ st.download_button(
273
+ label="Export Full Filtered Data to CSV",
274
+ data=csv,
275
+ file_name="multiplex_coop_filtered_properties.csv",
276
+ mime="text/csv",
277
+ )
278
 
279
+ else:
280
+ st.warning("No properties match the current filters. Adjust your criteria or draw a polygon on the map.")
281
 
282
+ # Add a note about the MessageSizeError and config option
283
  st.markdown("---")
284
+ st.markdown(
285
+ """
286
+ **Troubleshooting Large Data:**
287
+ If you encounter a `MessageSizeError` when displaying a large number of filtered properties,
288
+ it means the data size exceeds Streamlit's default limit.
289
+ You can increase this limit by adding `server.maxMessageSize = <size_in_mb>`
290
+ (e.g., `server.maxMessageSize = 500`) to your Streamlit `config.toml` file.
291
+ However, be aware that increasing this limit can lead to longer loading times and higher
292
+ memory consumption in your browser and on the Streamlit server.
293
+ """
294
+ )
295
  st.markdown("This app demonstrates spatial and attribute filtering on the ProjectMultiplexCoop/PropertyBoundaries dataset from Hugging Face. FSI, Building Coverage, Height, and Stories are synthetic for demonstration.")