3.5.c Riesgo por incendios y humo
Interfaz urbano-forestal, edificaciones cercanas a focos y exposición al humo
El riesgo por incendios forestales en Leticia tiene dos caras: el fuego en la interfaz entre la ciudad y el bosque o los rastrojos (viviendas de la periferia sobre la vía a Tarapacá), y el humo, que afecta a toda la población cuando arde la región en temporada seca.
import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from IPython.display import Markdown, display
from matplotlib.patches import Patch
from leticia import aoi, clima, espacial, exposicion, fuentes, riesgo, viz
from leticia.config import parametros
pi = parametros()["incendios"]
urbana = aoi.area_urbana()
mun = fuentes.limite_municipal()
def focos(sensor="modis", anios=None):
f = fuentes.focos_calor(sensor, anios)
if sensor == "modis":
f = f[f["confidence"] >= pi["confianza_modis_min"]]
else:
f = f[f["confidence"].astype(str).str.lower().isin(pi["confianza_viirs"])]
f = f[f["type"] == 0] if "type" in f else f # solo vegetación (excluye fuentes estáticas y volcanes)
return gpd.GeoDataFrame(f, geometry=gpd.points_from_xy(f.longitude, f.latitude), crs=4326)1. Interfaz urbano-forestal¶
wc = fuentes.raster_estatico("esa-worldcover", "map", clave_bbox="bbox_urbano", resolucion=0.0000898)
vegetacion = wc.isin([10, 20, 30]) # árboles, arbustos y pastos (combustible)
pol_veg = espacial.vectorizar(vegetacion.rio.write_crs(4326), 1000).to_crs(aoi.crs()) # parches continuos ≥ ~10 ha
edif = exposicion.edificaciones_urbanas()
pts = edif.representative_point()
d_veg = pts.distance(pol_veg.union_all().boundary)
viirs = focos("viirs-snpp", list(range(2012, 2026))).to_crs(aoi.crs())
from scipy.spatial import cKDTree
arbol = cKDTree(np.column_stack([viirs.geometry.x, viirs.geometry.y])) if len(viirs) else None
d_foco = pd.Series(arbol.query(np.column_stack([pts.x, pts.y]))[0], index=edif.index) if arbol is not None else pd.Series(np.inf, index=edif.index)
edif["interfaz"] = d_veg <= 50
edif["foco_500m"] = d_foco <= pi["bufer_interfaz_m"]
tabla = pd.DataFrame({
"edificaciones": [int(edif.interfaz.sum()), int(edif.foco_500m.sum()), int((edif.interfaz & edif.foco_500m).sum())],
"personas": [edif.loc[edif.interfaz, "poblacion"].sum(), edif.loc[edif.foco_500m, "poblacion"].sum(),
edif.loc[edif.interfaz & edif.foco_500m, "poblacion"].sum()]},
index=["a ≤ 50 m de vegetación continua ≥ 10 ha (interfaz)", f"a ≤ {pi['bufer_interfaz_m']} m de un foco VIIRS 2012–2025", "ambas condiciones"])
display(tabla.round(0))fig, ax = plt.subplots(figsize=(6.6, 7))
viz.fondo(ax)
edif[~edif.interfaz].to_crs(4326).plot(ax=ax, color="#bbbbbb", linewidth=0, zorder=2)
edif[edif.interfaz].to_crs(4326).plot(ax=ax, color="#fdae61", linewidth=0, zorder=3)
edif[edif.interfaz & edif.foco_500m].to_crs(4326).plot(ax=ax, color=viz.ROJO, linewidth=0, zorder=4)
if len(viirs):
viirs.to_crs(4326).plot(ax=ax, color="yellow", markersize=8, marker="*", zorder=5)
ax.legend(handles=[Patch(color="#fdae61", label="edificación en la interfaz"), Patch(color=viz.ROJO, label="interfaz y foco a ≤ 500 m"),
Patch(color="yellow", label="foco VIIRS 2012–2025")], loc="lower right", fontsize=7.5, frameon=True)
viz.marco(ax, titulo="Interfaz urbano-forestal y focos de calor")
Contraste con la cartografía nacional. La interfaz anterior usa ESA WorldCover a 10 m. El SINCHI cartografía las coberturas de la tierra de la Amazonia colombiana con la metodología Corine Land Cover adaptada para Colombia, a escala 1:100.000; su unidad mínima es mucho mayor, pero es la referencia oficial de la región.
cob = fuentes.siatac("siatac_coberturas", anio=pi["anio_coberturas_siatac"]).to_crs(aoi.crs())
combustible = cob[cob["cob_agrup"].isin(pi["coberturas_combustible_siatac"])]
interfaz_sinchi = pts.distance(combustible.union_all()) <= 50
comp = pd.DataFrame({"edificaciones": [int(edif.interfaz.sum()), int(interfaz_sinchi.sum()), int((edif.interfaz & interfaz_sinchi).sum())],
"personas": [edif.loc[edif.interfaz, "poblacion"].sum(), edif.loc[interfaz_sinchi, "poblacion"].sum(),
edif.loc[edif.interfaz & interfaz_sinchi, "poblacion"].sum()]},
index=["interfaz con ESA WorldCover (10 m)", f"interfaz con coberturas del SINCHI {pi['anio_coberturas_siatac']} (1:100.000)", "en ambas"])
display(comp.round(0))
urb = cob[cob["cob_agrup"] == "Territorios artificializados"]
display(Markdown(f"Coberturas del SINCHI que cuentan como combustible: {', '.join(pi['coberturas_combustible_siatac']).lower()}. Territorios artificializados "
f"en la extensión urbana: **{urb.area.sum() / 1e4:.0f} ha**."))Lectura. A escala 1:100.000 la interfaz es más pequeña: el SINCHI agrupa la ciudad y sus parches verdes en una sola unidad de territorio artificializado, de modo que solo cuentan como interfaz las edificaciones del borde urbano. WorldCover, a 10 m, ve también los rastrojos y potreros dentro de la periferia. Las dos fuentes coinciden en unas 1 500 edificaciones del borde, que son la prioridad para las franjas de manejo de combustible (3.5.e).
2. Riesgo relativo por celda¶
malla = exposicion.malla_exposicion()
g = edif.groupby("id_celda")
malla["frac_interfaz"] = g["interfaz"].mean().reindex(malla.index)
malla["dist_foco_m"] = malla.geometry.centroid.apply(lambda p: arbol.query([p.x, p.y])[0] if arbol is not None else np.inf)
amenaza = 0.5 * riesgo.normalizar(malla["dist_foco_m"], invertir=True) + 0.5 * riesgo.normalizar(malla["frac_interfaz"].fillna(0))
expo = 0.5 * riesgo.normalizar(malla["poblacion"]) + 0.5 * riesgo.normalizar(malla["n_edif"])
malla["riesgo_incendio"] = riesgo.matriz_riesgo(riesgo.clasificar(amenaza), riesgo.clasificar(expo.where(malla.n_edif > 0)))
con = malla[malla.n_edif > 0]
fig, ax = plt.subplots(figsize=(6.4, 7))
viz.mapa_celdas(con, "riesgo_incendio", ax=ax, categorias=riesgo.CLASES, colores=riesgo.COLORES_CLASES, etiqueta="riesgo relativo",
titulo="Riesgo relativo por incendio en la interfaz")
display(con.groupby("riesgo_incendio", observed=False)[["n_edif", "poblacion"]].sum().round(0))
malla[["frac_interfaz", "dist_foco_m", "riesgo_incendio"]].to_csv(fuentes.ruta("riesgo_incendio_celdas.csv"))
3. Humo: un riesgo regional¶
El humo que llega a Leticia proviene sobre todo de incendios en Brasil, Perú y Bolivia durante las sequías, que el archivo colombiano de FIRMS no incluye. En la sequía de 2023 los incendios en la Amazonía brasileña aumentaron 52 % en septiembre–octubre frente a agosto y unas 150 000 personas tuvieron problemas de salud asociados al fuego Costa & Marengo, 2023. Toda la población de Leticia está expuesta en esas temporadas; los más sensibles son los niños, las personas mayores y quienes tienen enfermedades respiratorias.
Lectura. El riesgo directo por fuego se concentra en la periferia, en las viviendas contiguas a rastrojos y bosques sobre la vía a Tarapacá. Las medidas clave son la prohibición y control de quemas en temporada seca, la vigilancia con alertas de focos (FIRMS, IDEAM), franjas de protección en la interfaz y la conservación del bosque periurbano. Para el humo: alertas de calidad del aire y protocolos de salud pública en sequías.
- Costa, F., & Marengo, J. (2023). Statement on the 2023 Amazon drought and its unforeseen consequences [Techreport]. Science Panel for the Amazon. https://eng-other-pub.sp-amazon.org/231208%20Statement%20on%20the%202023%20Amazon%20Drought%20(English).pdf