Urban Centers at Flood Risk Analysis#
This notebook guides you through:
Selecting an Area of Interest (AOI) interactively on a map.
Fetching demographic and flood risk data.
Identifying urban centers within the AOI that are at the highest risk due to flooding, using GHS settlement population metrics.
Visualizing and ranking these areas.
# !pip install geopandas ipyleaflet
import json
import requests
import pandas as pd
import geopandas as gpd
from shapely.geometry import shape
from ipyleaflet import Map, DrawControl
from space2stats_client import Space2StatsClient
from space2stats_client.widgets import AOISelector
/opt/homebrew/Caskroom/miniconda/base/envs/pkgtest/lib/python3.10/site-packages/pyproj/network.py:59: UserWarning: pyproj unable to set PROJ database path.
_set_context_ca_bundle_path(ca_bundle_path)
aoi_selector = AOISelector(center=(27.0, 29.7), zoom=6)
aoi_selector.display()
aoi = aoi_selector.aoi
aoi
geometry name
0 POLYGON ((30.08057 26.23036, 30.08057 29.12913... User AOI 1
client=Space2StatsClient()
fields = [
"sum_pop_2020", "sum_pop_f_2020", "sum_pop_m_2020", "pop_flood", "pop_flood_pct",
"ghs_11_pop", "ghs_12_pop", "ghs_13_pop", "ghs_21_pop", "ghs_22_pop", "ghs_23_pop", "ghs_30_pop"
]
# Get available topics/datasets
df = client.get_summary(
gdf=aoi.gdf,
spatial_join_method="centroid",
fields=fields,
geometry="polygon"
)
Fetching data for boundary 1 of 1...
# Convert df to gdf
if isinstance(df.geometry.iloc[0], str):
df["geometry"] = df.geometry.apply(json.loads)
df["geometry"] = df.geometry.apply(shape)
gdf = gpd.GeoDataFrame(df, geometry="geometry", crs="EPSG:4326")
# Define urban_pop to only include semi-dense urban clusters (22_POP), dense urban clusters (23_POP) and urban centres (30_POP)
gdf["urban_pop"] = gdf["ghs_22_pop"] + gdf["ghs_23_pop"] + gdf["ghs_30_pop"]
# Calculate risk score using only the updated urban_pop
gdf["risk_score"] = gdf["pop_flood_pct"] * gdf["urban_pop"]
# Filter to areas where urban_pop is significant (e.g., > 100 people)
urban_gdf = gdf[gdf["urban_pop"] > 100].copy()
max_score = urban_gdf["risk_score"].max()
urban_gdf["risk_score_norm"] = (urban_gdf["risk_score"] / max_score) * 100
# Now sorting and other operations will not raise SettingWithCopyWarning
urban_gdf = urban_gdf.sort_values("risk_score_norm", ascending=False)
urban_gdf.head()
| name | index_gdf | index_h3 | hex_id | geometry | sum_pop_2020 | sum_pop_f_2020 | sum_pop_m_2020 | pop_flood | pop_flood_pct | ghs_11_pop | ghs_12_pop | ghs_13_pop | ghs_21_pop | ghs_22_pop | ghs_23_pop | ghs_30_pop | urban_pop | risk_score | risk_score_norm | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 399 | User AOI 1 | 0 | 399 | 863e46657ffffff | POLYGON ((31.14573 27.21547, 31.12845 27.18234... | 1.593942e+10 | 7.927194e+09 | 8.012227e+09 | 368488.695989 | 0.650426 | 0.286214 | 43.898725 | 0.0 | 915.403993 | 0.0 | 20.302995 | 641632.233433 | 641652.536428 | 417347.318227 | 100.000000 |
| 303 | User AOI 1 | 0 | 303 | 863e461afffffff | POLYGON ((30.84588 27.76354, 30.82858 27.73036... | 1.593942e+10 | 7.927194e+09 | 8.012227e+09 | 158188.937597 | 0.669869 | 6.471631 | 1999.221003 | 0.0 | 2790.173053 | 0.0 | 21888.971348 | 274027.723812 | 295916.695159 | 198225.527161 | 47.496538 |
| 484 | User AOI 1 | 0 | 484 | 863e4699fffffff | POLYGON ((30.74981 28.10564, 30.73248 28.07245... | 1.593942e+10 | 7.927194e+09 | 8.012227e+09 | 171839.221415 | 0.603658 | 49.475881 | 623.250167 | 0.0 | 6253.058859 | 0.0 | 11949.665112 | 294007.560908 | 305957.226020 | 184693.640318 | 44.254182 |
| 276 | User AOI 1 | 0 | 276 | 863e460b7ffffff | POLYGON ((30.80029 27.60091, 30.78303 27.56772... | 1.593942e+10 | 7.927194e+09 | 8.012227e+09 | 143713.732009 | 0.696577 | 43.055753 | 585.262277 | 0.0 | 6080.221818 | 0.0 | 169.485524 | 254081.217079 | 254250.702603 | 177105.155543 | 42.435916 |
| 403 | User AOI 1 | 0 | 403 | 863e46677ffffff | POLYGON ((31.13969 27.2785, 31.12239 27.24537,... | 1.593942e+10 | 7.927194e+09 | 8.012227e+09 | 180460.999863 | 0.882970 | 0.000000 | 0.000000 | 0.0 | 12414.041634 | 0.0 | 26178.967637 | 169754.975996 | 195933.943634 | 173003.825605 | 41.453202 |
# Now create your risk map
m_risk = urban_gdf.explore(
column="risk_score_norm",
tooltip=["sum_pop_2020", "pop_flood", "pop_flood_pct", "urban_pop", "risk_score"],
cmap="OrRd",
legend=True,
scheme="quantiles",
legend_kwds=dict(colorbar=True, caption="Urban Flood Score", interval=False),
style_kwds=dict(weight=0.5, fillOpacity=0.8),
name="Urban Flood Risk",
)
aoi.gdf.explore(
m=m_risk, # Add to the existing map
color='red',
weight=3,
fill=False,
name="AOI Boundary"
)
m_risk
---------------------------------------------------------------------------
ModuleNotFoundError Traceback (most recent call last)
File /opt/homebrew/Caskroom/miniconda/base/envs/pkgtest/lib/python3.10/site-packages/geopandas/explore.py:289, in _explore(df, column, cmap, color, m, tiles, attr, tooltip, popup, highlight, categorical, legend, scheme, k, vmin, vmax, width, height, categories, classification_kwds, control_scale, marker_type, marker_kwds, style_kwds, highlight_kwds, missing_kwds, tooltip_kwds, popup_kwds, legend_kwds, map_kwds, **kwargs)
288 import branca as bc
--> 289 import folium
290 import matplotlib.pyplot as plt
ModuleNotFoundError: No module named 'folium'
During handling of the above exception, another exception occurred:
ImportError Traceback (most recent call last)
Cell In[12], line 2
1 # Now create your risk map
----> 2 m_risk = urban_gdf.explore(
3 column="risk_score_norm",
4 tooltip=["sum_pop_2020", "pop_flood", "pop_flood_pct", "urban_pop", "risk_score"],
5 cmap="OrRd",
6 legend=True,
7 scheme="quantiles",
8 legend_kwds=dict(colorbar=True, caption="Urban Flood Score", interval=False),
9 style_kwds=dict(weight=0.5, fillOpacity=0.8),
10 name="Urban Flood Risk",
11 )
13 aoi.gdf.explore(
14 m=m_risk, # Add to the existing map
15 color='red',
(...)
18 name="AOI Boundary"
19 )
21 m_risk
File /opt/homebrew/Caskroom/miniconda/base/envs/pkgtest/lib/python3.10/site-packages/geopandas/geodataframe.py:2482, in GeoDataFrame.explore(self, *args, **kwargs)
2480 @doc(_explore)
2481 def explore(self, *args, **kwargs) -> folium.Map:
-> 2482 return _explore(self, *args, **kwargs)
File /opt/homebrew/Caskroom/miniconda/base/envs/pkgtest/lib/python3.10/site-packages/geopandas/explore.py:296, in _explore(df, column, cmap, color, m, tiles, attr, tooltip, popup, highlight, categorical, legend, scheme, k, vmin, vmax, width, height, categories, classification_kwds, control_scale, marker_type, marker_kwds, style_kwds, highlight_kwds, missing_kwds, tooltip_kwds, popup_kwds, legend_kwds, map_kwds, **kwargs)
293 from matplotlib import colors
295 except (ImportError, ModuleNotFoundError):
--> 296 raise ImportError(
297 "The 'folium>=0.12', 'matplotlib' and 'mapclassify' packages "
298 "are required for 'explore()'. You can install them using "
299 "'conda install -c conda-forge \"folium>=0.12\" matplotlib mapclassify' "
300 "or 'pip install \"folium>=0.12\" matplotlib mapclassify'."
301 )
303 # xyservices is an optional dependency
304 try:
ImportError: The 'folium>=0.12', 'matplotlib' and 'mapclassify' packages are required for 'explore()'. You can install them using 'conda install -c conda-forge "folium>=0.12" matplotlib mapclassify' or 'pip install "folium>=0.12" matplotlib mapclassify'.