3.1.c Riesgo por inundación fluvial
Edificaciones y personas expuestas, daño esperado y zonas bajas de la ciudad
El riesgo resulta de cruzar la amenaza (profundidad para cada periodo de retorno) con la exposición (edificaciones y población de 2.6) y una función de vulnerabilidad (curva profundidad–daño). El procedimiento sigue la evaluación espacial del impacto de CCDR-tools Amadio, 2024: intersección, fracción de daño e integración sobre la curva de excedencia.
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from IPython.display import Markdown, display
from leticia import aoi, espacial, exposicion, fuentes, riesgo, viz
from leticia.config import parametros
p = parametros()["inundacion"]
edif = exposicion.edificaciones_urbanas()
malla = exposicion.malla_exposicion()
pts = edif.representative_point().to_crs(4326)1. Exposición por periodo de retorno¶
filas, prof_por_rp = [], {}
for rp in p["periodos_jrc"]:
d = fuentes.inundacion_jrc(rp)
prof = np.nan_to_num(d.interp(x=("p", pts.x.values), y=("p", pts.y.values), method="nearest").values)
prof_por_rp[rp] = prof
expuesta = prof >= p["profundidad_minima_m"]
danio = riesgo.fraccion_danio(prof) * edif["area_m2"].values * p["costo_reposicion_cop_m2"]
filas.append({"periodo_retorno": rp, "edificaciones": int(expuesta.sum()), "personas": edif.loc[expuesta, "poblacion"].sum(),
"profundidad_media_m": prof[expuesta].mean() if expuesta.any() else 0, "danio_mm_cop": danio.sum() / 1e6})
tabla = pd.DataFrame(filas).set_index("periodo_retorno")
display(tabla.round({"personas": 0, "profundidad_media_m": 2, "danio_mm_cop": 0}))pae = riesgo.perdida_anual_esperada(tabla["danio_mm_cop"])
eae = riesgo.perdida_anual_esperada(tabla["personas"])
display(Markdown(f"**Pérdida anual esperada (daño directo a edificaciones):** ≈ {pae:,.0f} millones de COP/año. "
f"**Población expuesta anual esperada:** ≈ {eae:,.0f} personas/año. "
"Ambas cifras dependen del costo de reposición ilustrativo de `parametros.yaml` y de la resolución de 90 m del JRC.".replace(",", ".")))2. Pérdida anual esperada con CLIMADA¶
CLIMADA es la plataforma abierta de evaluación de riesgo climático de la ETH Zúrich, usada por el Banco Mundial y por CLIMAAX: combina amenaza, exposición y funciones de impacto en un cálculo probabilístico por eventos Aznar-Siguan & Bresch, 2019. Aquí se le dan los mismos insumos de la sección 1 —los seis mapas del JRC, las edificaciones con su valor ilustrativo y la curva profundidad–daño de Huizinga et al. Huizinga et al., 2017— para comparar resultados y evaluar una medida de adaptación: elevar las viviendas que se inundan con la creciente de 10 años, como las casas palafíticas tradicionales de la várzea. En Colab, CLIMADA se instala con la biblioteca GDAL del sistema al comienzo del cuaderno (unos minutos).
from leticia.libro import intentar
valores = edif["area_m2"].values * p["costo_reposicion_cop_m2"]
inundable_10 = (prof_por_rp[min(prof_por_rp)] > 0).astype(int) # se elevan las que se inundan con T = 10 años
res = intentar(riesgo.impacto_climada, pts.x.values, pts.y.values, valores, prof_por_rp, p["curva_danio"],
elevaciones_m=p["elevaciones_climada_m"], region=inundable_10, mensaje="CLIMADA no está disponible")
if res is not None:
comp = pd.DataFrame({"CLIMADA": res["por_periodo"] / 1e6, "sección 1": tabla["danio_mm_cop"]})
comp.index.name = "periodo de retorno (años)"
display(Markdown("**Daño por creciente (millones de COP):**"))
display(comp.round(0))
display(Markdown(f"Pérdida anual esperada con CLIMADA: **{res['pae'] / 1e6:,.0f} millones de COP/año**; con la integración de la "
f"sección 1: {pae:,.0f}. Edificaciones que se inundan con T = 10 años: {int(inundable_10.sum()):,}.".replace(",", ".")))
med = res["medidas"].copy()
med["pae"] = med["pae"] / 1e6
display(med.rename(columns={"pae": "pérdida anual esperada (mm COP/año)", "reduccion_%": "reducción (%)"}).round(1))if res is not None:
edif["pae_climada"] = res["pae_por_punto"] / 1e6
malla["pae_climada"] = edif.groupby("id_celda")["pae_climada"].sum().reindex(malla.index).fillna(0)
fig, ax = plt.subplots(figsize=(6.4, 7))
viz.mapa_celdas(malla[malla["pae_climada"] > 0], "pae_climada", ax=ax, cmap="Reds", etiqueta="millones de COP/año",
titulo="Pérdida anual esperada por celda (CLIMADA)")
top = malla["pae_climada"].sort_values(ascending=False)
display(Markdown(f"El 10 % de las celdas con más pérdida concentra el **{100 * top.iloc[:max(1, len(top[top > 0]) // 10)].sum() / top.sum():.0f} %** "
"de la pérdida anual esperada."))
Lectura. Con los mismos insumos, CLIMADA estima una pérdida anual esperada menor que la de la sección 1. La diferencia no es un error de ninguno: CLIMADA asigna a cada mapa la probabilidad que va hasta el mapa siguiente y no cuenta daños con crecientes más frecuentes que la de 10 años, mientras que la sección 1 supone que el daño empieza con la creciente de 5 años. La pérdida real está entre ambas, y precisarla exige saber con qué frecuencia el agua alcanza las primeras viviendas: un dato que las inundaciones registradas por la UNGRD y la regla de Leticia pueden aportar. Elevar las viviendas que se inundan con la creciente de 10 años reduce la pérdida esperada en 6 %, 13 % y 28 % con 0,5, 1 y 2 m: menos de lo que podría pensarse, porque según el mapa del JRC esas viviendas tendrían en promedio unos 4 m de agua ya con esa creciente. Elevar reduce el daño, pero no lo evita; para bajar el riesgo de fondo hace falta combinarlo con la reubicación de las viviendas más bajas y con el control de las construcciones nuevas en la franja inundable (medidas). La pérdida se concentra: el 10 % de las celdas con más pérdida reúne cerca del 60 % del total.
3. Altura sobre el río: dónde están las zonas bajas¶
dem = fuentes.raster_estatico("cop-dem-glo-30", "data")
occ = fuentes.raster_estatico("jrc-gsw", "occurrence")
agua_perm = occ >= 90
hand = espacial.altura_sobre_agua(dem, agua_perm)
malla["hand_m"] = espacial.estadistica_zonal(hand, malla, "mean")
malla["prof_rp100_m"] = espacial.estadistica_zonal(fuentes.inundacion_jrc(100).fillna(0), malla, "max")
fig, ax = plt.subplots(1, 2, figsize=(12, 6.2))
viz.mapa_celdas(malla, "hand_m", ax=ax[0], cmap="RdYlBu", etiqueta="m sobre el agua permanente", vmin=0, vmax=25,
titulo="Altura sobre el río (aprox. HAND)")
viz.mapa_celdas(malla[malla.prof_rp100_m > 0], "prof_rp100_m", ax=ax[1], cmap="Blues", etiqueta="profundidad máxima (m)",
titulo="Inundación de 100 años (JRC) en la malla")
fig.tight_layout()
La altura sobre el río compara la elevación de cada lugar con la del agua permanente más cercana Rennó et al., 2008. Como el modelo de superficie incluye árboles y techos, sobreestima la altura en zonas arboladas; aun así ordena bien los barrios: los valores bajos señalan la franja ribereña y los fondos de quebrada, que una creciente extraordinaria alcanza primero.
4. Sensibilidad: ¿qué pasa si el río sube más?¶
edif_hand = np.nan_to_num(hand.interp(x=("p", pts.x.values), y=("p", pts.y.values), method="nearest").values, nan=99)
umbrales = np.arange(0, 16, 1.0)
curva = pd.DataFrame({"altura_m": umbrales,
"edificaciones": [(edif_hand <= h).sum() for h in umbrales],
"personas": [edif.loc[edif_hand <= h, "poblacion"].sum() for h in umbrales]}).set_index("altura_m")
fig, ax = plt.subplots(figsize=(8, 3.6))
ax.plot(curva.index, curva["edificaciones"], color=viz.AZUL_RIO, marker="o", ms=3)
ax.set_xlabel("altura sobre el agua permanente (m)")
ax.set_ylabel("edificaciones por debajo")
ax.set_title("Edificaciones según su altura sobre el río")
ax2 = ax.twinx()
ax2.plot(curva.index, curva["personas"], color=viz.TIERRA, ls="--")
ax2.set_ylabel("personas", color=viz.TIERRA)
ax2.grid(False)
fig.tight_layout()
display(curva.iloc[::2].round(0).astype(int).T)
Lectura. La curva muestra cuánto crece la exposición por cada metro adicional de agua sobre el nivel de referencia del agua permanente. Es la forma más directa de traducir los escenarios de la sección d —crecientes más altas por intensificación del ciclo hidrológico— en edificaciones y personas.
5. Clases de riesgo por celda¶
malla["dist_rio_m"] = espacial.distancia_a(malla, espacial.vectorizar(agua_perm.rio.write_crs(4326), 200))
lst = espacial.temperatura_superficie(2024, parametros()["calor"]["meses_landsat"])
malla["ndvi"] = espacial.estadistica_zonal(lst["ndvi"].rio.write_crs(4326), malla)
malla["vulnerabilidad"] = riesgo.indice_vulnerabilidad(malla)
# amenaza: profundidad de 100 años y altura sobre el río (más bajo = más amenaza)
amenaza_idx = 0.5 * riesgo.normalizar(malla["prof_rp100_m"].fillna(0)) + 0.5 * riesgo.normalizar(malla["hand_m"], invertir=True)
malla["amenaza_clase"] = riesgo.clasificar(amenaza_idx)
malla["vuln_clase"] = riesgo.clasificar(malla["vulnerabilidad"])
malla["riesgo_inundacion"] = riesgo.matriz_riesgo(malla["amenaza_clase"], malla["vuln_clase"])
con_edif = malla[malla.n_edif > 0]
fig, ax = plt.subplots(figsize=(6.4, 7))
viz.mapa_celdas(con_edif, "riesgo_inundacion", ax=ax, categorias=riesgo.CLASES, colores=riesgo.COLORES_CLASES,
etiqueta="riesgo relativo", titulo="Riesgo relativo por inundación fluvial")
resumen = con_edif.groupby("riesgo_inundacion", observed=False)[["n_edif", "poblacion"]].sum().round(0)
display(resumen)
malla[["hand_m", "prof_rp100_m", "dist_rio_m", "ndvi", "vulnerabilidad", "riesgo_inundacion"]].to_csv(fuentes.ruta("riesgo_inundacion_celdas.csv"))
Las clases son relativas dentro de Leticia (quintiles), no absolutas. La amenaza combina la profundidad
del JRC y la altura sobre el río; la vulnerabilidad, la densidad construida, la población, la cercanía al río y
la falta de vegetación (pesos en parametros.yaml). La tabla de celdas queda guardada para la síntesis
(3.8).
- Amadio, M. (2024). CCDR tools data and methodology. Global Facility for Disaster Reduction and Recovery (GFDRR), World Bank. https://gfdrr.github.io/CCDR-tools
- Aznar-Siguan, G., & Bresch, D. N. (2019). CLIMADA v1: a global weather and climate risk assessment platform. Geoscientific Model Development, 12(7), 3085–3097. 10.5194/gmd-12-3085-2019
- Huizinga, J., de Moel, H., & Szewczyk, W. (2017). Global flood depth-damage functions: Methodology and the database with guidelines (Techreport EUR 28552 EN). Publications Office of the European Union. 10.2760/16510
- Rennó, C. D., Nobre, A. D., Cuartas, L. A., Soares, J. V., Hodnett, M. G., Tomasella, J., & Waterloo, M. J. (2008). HAND, a new terrain descriptor using SRTM-DEM: Mapping terra-firme rainforest environments in Amazonia. Remote Sensing of Environment, 112(9), 3469–3481. 10.1016/j.rse.2008.03.018