Urban Centers at Flood Risk Analysis

Urban Centers at Flood Risk Analysis#

This notebook guides you through:

  1. Selecting an Area of Interest (AOI) interactively on a map.

  2. Fetching demographic and flood risk data.

  3. Identifying urban centers within the AOI that are at the highest risk due to flooding, using GHS settlement population metrics.

  4. Visualizing and ranking these areas.

Open In Colab

# !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'.