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.
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