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.

import json
import requests
import pandas as pd
import geopandas as gpd
from shapely.geometry import shape
from ipyleaflet import Map, DrawControl
# Initialize a map for AOI selection
m = Map(center=(27,29.7), zoom=6)

draw_control = DrawControl(
    polygon={
        "shapeOptions": {
            "color": "#6e6d6b",
            "weight": 2,
            "fillOpacity": 0.5
        },
        "drawError": {
            "color": "#dd253b",
            "message": "Error drawing shape!"
        },
    },
    rectangle={
        "shapeOptions": {
            "color": "#6e6d6b",
            "weight": 2,
            "fillOpacity": 0.5
        }
    },
    marker={},
    circlemarker={},
    polyline={}
)

user_aoi = None

def handle_draw(self, action, geo_json):
    global user_aoi
    user_aoi = geo_json
    print("AOI captured:")
    print(json.dumps(user_aoi, indent=2))

draw_control.on_draw(handle_draw)
m.add_control(draw_control)
m
# Set the API Request String
BASE_URL = "https://w7tfg3g781.execute-api.us-east-1.amazonaws.com"
SUMMARY_ENDPOINT = f"{BASE_URL}/summary"

# Fields of interest: population, flood, and GHS settlement metrics
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"
]
# Check if AOI is set
if user_aoi is None:
    raise ValueError("No AOI selected. Please draw an AOI on the map above and rerun this cell.")

# Convert AOI to required format (Feature)
aoi_feature = {
    "type": "Feature",
    "geometry": user_aoi["geometry"],
    "properties": {"name": "User Selected AOI"}
}

request_payload = {
    "aoi": aoi_feature,
    "spatial_join_method": "touches",
    "fields": fields,
    "geometry": "polygon",
}

response = requests.post(SUMMARY_ENDPOINT, json=request_payload)

if response.status_code != 200:
    raise Exception("Failed to get summary data:", response.text)

summary_data = response.json()

df = pd.DataFrame(summary_data)
# Convert geometry field from GeoJSON to Shapely
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")

print("Data retrieved and converted to GeoDataFrame.")
#gdf.head()
Data retrieved and converted to GeoDataFrame.
# 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()
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
1391 863e46657ffffff POLYGON ((31.14573 27.21547, 31.12845 27.18234... 566627.893311 275516.716797 291111.176514 368488.695989 0.650426 0.286214 43.898725 0.0 915.403993 0.0 20.302995 641632.233433 641652.536428 417347.318227 100.000000
336 863e4205fffffff POLYGON ((31.69575 26.56058, 31.67844 26.52755... 329936.939209 163869.888794 166067.050415 251887.227171 0.760492 0.000000 486.578873 0.0 11407.403337 0.0 20818.880593 300402.691452 321221.572045 244286.310899 58.533097
1282 863e461afffffff POLYGON ((30.84588 27.76354, 30.82858 27.73036... 225762.972473 110895.939514 114867.032959 158188.937597 0.669869 6.471631 1999.221003 0.0 2790.173053 0.0 21888.971348 274027.723812 295916.695159 198225.527161 47.496538
1476 863e4699fffffff POLYGON ((30.74981 28.10564, 30.73248 28.07245... 294847.856201 145668.350464 149179.505737 171839.221415 0.603658 49.475881 623.250167 0.0 6253.058859 0.0 11949.665112 294007.560908 305957.226020 184693.640318 44.254182
1255 863e460b7ffffff POLYGON ((30.80029 27.60091, 30.78303 27.56772... 205633.663269 101348.879578 104284.783691 143713.732009 0.696577 43.055753 585.262277 0.0 6080.221818 0.0 169.485524 254081.217079 254250.702603 177105.155543 42.435916
aoi_gdf = gpd.GeoDataFrame.from_features([aoi_feature], crs="EPSG:4326")

# 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",
)

# Add AOI outline on the same map
aoi_gdf.explore(
    m=m_risk,  # Note: passing m_risk here to add AOI on the same map
    color='red',
    weight=3,
    fill=False,
    name="AOI Boundary"
)

m_risk
Make this Notebook Trusted to load map: File -> Trust Notebook