Creating an Interactive Flood Vulnerability Dashboard for New York City Neighborhoods with Panel Library in Python
Introduction
Our previous blog introduced a Python application that allowed users to select a neighborhood and view a line chart depicting the flooding categories within that area. In this follow-up, we’ll enhance this application by adding an interactive map feature that displays neighborhood divisions and annotations. This interactive dashboard, powered by Panel, will provide users with a more comprehensive understanding of New York City’s neighborhoods and their flood vulnerabilities. In this blog post, we’ll walk you through the code used to develop the dashboard and explain its key components.
Libraries Used
Before we dive into the code, let’s briefly introduce the Python libraries used in this project:
-
Panel (pn): Panel is a high-level app and dashboarding solution that allows you to create interactive web apps with minimal code.
-
Plotly Express (px): Plotly Express is a Python graphing library that makes it easy to create interactive plots and charts.
-
Pandas (pd): Pandas is a popular data manipulation library that simplifies data analysis and preparation.
-
GeoPandas (gpd): GeoPandas extends Pandas to enable working with geospatial data. It simplifies the manipulation and analysis of geographic data.
-
Shapely: Shapely is a Python library for the manipulation and analysis of geometric objects. In this project, it’s used to create subsections of a neighborhood for mapping.
-
Matplotlib: Matplotlib is a versatile plotting library used for creating static plots and maps.
Interactive Stormwater Flooding Dashboard
In this project, we aim to provide an interactive dashboard that allows users to explore stormwater flooding data and visualize the potential impact of extreme flooding with a sea level rise scenario. We will break down the main components of the code used to create this dashboard.
Data Loading
The first step is to load the necessary data. The data includes information about neighborhoods and stormwater flooding. This data is typically obtained from various sources, such as government agencies, and is loaded into Pandas DataFrames for analysis.
import panel as pn
import plotly.express as px
import pandas as pd
import geopandas as gpd
import shapely
import matplotlib.pyplot as plt
pn.extension('plotly')
Data Visualization
We use Plotly Express to create an interactive line chart that displays the flooding data for a selected neighborhood. The plot_neighborhood
function generates this visualization.
def plot_neighborhood(neighborhood):
fig = px.line(df[df['Neighborhood'] == neighborhood], x='Section', y=['Nuisance', 'Deep', 'High Tide'])
fig.update_layout(title='Stormwater Flood Map - Extreme Flood with 2080 Sea Level Rise')
fig.update_layout(yaxis_title='Fraction')
fig.update_layout(legend_title_text='Flooding Category')
fig.update_layout(width=600)
fig.update_layout(title_font=dict(size=14))
fig.update_layout(legend=dict(orientation="h", x=0.1, y=1.1,font=dict(size=8)))
return fig
Geospatial Mapping
We also create a geospatial map to display subsections of a selected neighborhood. The show_map
function generates this map. It uses GeoPandas to manipulate geospatial data and Matplotlib for plotting.
def show_map(neighborhood):
nbhdstar = nbhds[nbhds['FIRST_UHF_'] == neighborhood]
rows, cols = 5, 5
min_x, min_y, max_x, max_y = nbhdstar.total_bounds
width = (max_x - min_x) / cols
height = (max_y - min_y) / rows
# Create an empty GeoDataFrame to store all subsections
all_subsections_gdf = gpd.GeoDataFrame(columns=['geometry'], crs=nbhdstar.crs)
# Iterate through rows and columns to create and add subsections to the all_subsections_gdf
counter = 0
for row in range(rows):
for col in range(cols):
counter += 1
left = min_x + col * width
right = left + width
top = max_y - row * height
bottom = top - height
subsection = shapely.geometry.box(left, bottom, right, top)
subsection_gdf = gpd.GeoDataFrame({'geometry': [subsection]}, crs=nbhdstar.crs) # Set CRS for each subsection
all_subsections_gdf = gpd.GeoDataFrame(pd.concat([all_subsections_gdf, subsection_gdf], ignore_index=True))
# Plot the original GeoDataFrame and all subsections
fig, ax = plt.subplots(figsize=(2, 2));
# Plot the original GeoDataFrame
nbhdstar.plot(ax=ax, color='black', alpha=0.5, edgecolor='k');
# Plot all subsections
all_subsections_gdf.plot(ax=ax, color='white', alpha=0.2, edgecolor='k')
# Annotate each subsection with a counter
for idx, geom in enumerate(all_subsections_gdf['geometry']):
x, y = geom.centroid.coords[0] # Get the centroid of the subsection
ax.annotate(str(idx + 1), xy=(x, y), ha='center', va='center', fontsize=10);
ax.set_aspect('equal')
ax.set_axis_off();
#plt.title(neighborhood)
show = False
plt.close(fig)
return fig
Interactive Widgets
To make the dashboard interactive, we use Panel widgets. Users can select a neighborhood from a dropdown menu, and the visualizations are updated accordingly. The neighborhood_dropdown
widget allows users to select a neighborhood, and update_plot
and update_map
functions update the visuals based on the selected neighborhood.
neighborhood_dropdown = pn.widgets.Select(name='', options=sorted(df['Neighborhood'].unique().tolist()))
plot = pn.panel(plot_neighborhood(neighborhood_dropdown.value))
@pn.depends(neighborhood=neighborhood_dropdown.param.value)
def update_plot(neighborhood):
return plot_neighborhood(neighborhood)
@pn.depends(neighborhood=neighborhood_dropdown.param.value)
def update_map(neighborhood):
return show_map(neighborhood)
dashboard = pn.Column(neighborhood_dropdown, pn.Row(update_plot, update_map))
Conclusion
In this blog post, we’ve demonstrated how to create an interactive dashboard for exploring stormwater flooding data using Python. By combining data visualization libraries like Plotly Express, geospatial analysis with GeoPandas, and interactive widgets with Panel, we’ve built a powerful tool for visualizing and analyzing stormwater flooding data.