3.5.d Escenarios: fuego, sequía y punto de no retorno
Proyecciones de condiciones secas y cálidas que favorecen el fuego
El riesgo de incendio en el futuro depende de dos fuerzas: el clima, que hará las estaciones secas más cálidas y con mayor déficit hídrico, y el uso del suelo, que decide cuánto bosque se abre y cuánta ignición hay. Flores et al. estiman que hacia 2050 entre el 10 % y el 47 % del bosque amazónico estará expuesto a perturbaciones combinadas que podrían desencadenar transiciones del ecosistema Flores et al., 2024.
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. Condiciones que favorecen el fuego (CCKP)¶
VARS = {"tasmax": "temperatura máxima (°C)", "cdd": "máx. días secos consecutivos", "spei12": "SPEI-12", "hd35": "días con Tmax > 35 °C"}
tabla = fuentes.cckp_tabla(list(VARS))
med = tabla[tabla.percentil == "median"].pivot_table(index="variable", columns=["periodo", "escenario"], values="valor")
med.columns = [f"{p} {viz.NOMBRE_ESC[e]}" for p, e in med.columns]
med.index = [VARS[v] for v in med.index]
display(Markdown("**Cambio proyectado respecto a 1995–2014 (mediana del ensamble):**"))
display(med.round(2))2. Anticipar la temporada: el Atlántico Norte tropical¶
Chen et al. mostraron que las anomalías de la temperatura superficial del mar (TSM) permiten anticipar la severidad de la temporada de incendios en Suramérica con tres a cinco meses de anticipación Chen et al., 2011. Para la Amazonía occidental, Fernandes et al. encontraron que la lluvia de julio–septiembre, y con ella los focos de calor, depende sobre todo de la TSM del Atlántico Norte tropical, la misma que acompañó las sequías de 2005 y 2010 Marengo et al., 2021. Con una regresión lineal entre los focos y un índice de esa TSM pronosticaron desde abril la temporada de 2010 Fernandes et al., 2011. Aquí se replica el método para la triple frontera (Colombia, Perú y Brasil), donde se quema la mayor parte de lo que llega a Leticia como humo, con el índice TNA de marzo–mayo, que se conoce a comienzos de junio.
from leticia import pronostico
from leticia.config import aoi_cfg
from scipy import stats
reg = fuentes.focos_calor("modis", clave_bbox="bbox_fuego", paises=aoi_cfg()["paises_fuego"])
reg = reg[(reg["confidence"] >= pi["confianza_modis_min"]) & (reg["type"] == 0)]
anios = range(pi["anio_inicio_modis"], int(reg.index.year.max()) + 1)
mensual = reg[reg.index.year >= pi["anio_inicio_modis"]].groupby(reg.index.month[reg.index.year >= pi["anio_inicio_modis"]]).size().reindex(range(1, 13), fill_value=0)
temp = reg[reg.index.month.isin(pi["meses_temporada"])]
y = temp.groupby(temp.index.year).size().reindex(anios, fill_value=0)
tna, oni = fuentes.indice_tna(), fuentes.oni()
nombres = "EFMAMJJASOND"
ventanas = [[m, m + 1, m + 2] for m in range(1, 9)]
corr = pd.DataFrame({"TNA": [stats.spearmanr(pronostico.media_meses(tna, v).reindex(y.index), y)[0] for v in ventanas],
"ONI (El Niño)": [stats.spearmanr(pronostico.media_meses(oni, v).reindex(y.index), y)[0] for v in ventanas]},
index=["".join(nombres[m - 1] for m in v) for v in ventanas])
fig, ax = plt.subplots(1, 2, figsize=(12, 3.6), gridspec_kw={"width_ratios": [1, 1.3]})
ax[0].bar(mensual.index, mensual, color=[viz.CALOR if m in pi["meses_temporada"] else viz.GRIS for m in mensual.index])
ax[0].set_xticks(range(1, 13)); ax[0].set_xticklabels(list(nombres))
ax[0].set_title(f"Focos MODIS por mes en la triple frontera, {anios[0]}–{anios[-1]}")
corr.plot.bar(ax=ax[1], color=[viz.AZUL_RIO, viz.GRIS], rot=0)
ax[1].axhline(0, color="k", lw=0.6)
ax[1].set_ylabel("correlación de Spearman")
ax[1].set_title("Focos de " + "".join(nombres[m - 1] for m in pi["meses_temporada"]) + " frente al índice de cada trimestre")
fig.tight_layout()
display(Markdown(f"Focos MODIS en la triple frontera ({anios[0]}–{anios[-1]}): **{len(reg[reg.index.year >= anios[0]]):,}**; ".replace(",", ".")
+ f"el {100 * mensual.loc[pi['meses_temporada']].sum() / mensual.sum():.0f} % ocurre en agosto–octubre."))
x = pronostico.media_meses(tna, pi["meses_predictor"])
X = x.reindex(y.index).to_frame("TNA")
ly = np.log(y.clip(lower=1))
ref, _ = pronostico.regresion_validada(ly, X.iloc[:, :0])
pred, coef = pronostico.regresion_validada(ly, X)
h = pronostico.habilidad(ly, pred, ref)
fig, ax = plt.subplots(1, 2, figsize=(12, 3.8))
ax[0].scatter(X["TNA"], y, color=viz.CALOR, s=28, zorder=3)
for a, r_ in X.join(y.rename("focos")).iterrows():
ax[0].annotate(str(a), (r_["TNA"], r_["focos"]), fontsize=6.5, xytext=(3, 2), textcoords="offset points")
xx = np.linspace(X["TNA"].min(), X["TNA"].max(), 50)
ax[0].plot(xx, np.exp(coef[0] + coef[1] * xx), color="k", lw=1)
ax[0].set_xlabel("TNA de marzo–mayo (°C)")
ax[0].set_ylabel("focos de agosto–octubre")
ax[0].set_title("Atlántico Norte tropical y temporada de incendios")
ax[1].plot(y.index, y, "o-", color=viz.CALOR, label="observado")
ax[1].plot(pred.index, np.exp(pred), "s--", color=viz.GRIS, label="pronóstico con validación cruzada")
ax[1].set_ylabel("focos de agosto–octubre")
ax[1].set_title("Pronóstico emitido en junio de cada año")
ax[1].legend()
fig.tight_layout()
texto = (f"Modelo: log(focos) = {coef[0]:.2f} + {coef[1]:.2f} × TNA. Cada 1 °C de anomalía del TNA en marzo–mayo multiplica los focos "
f"de agosto–octubre por **{np.exp(coef[1]):.1f}**. Validación cruzada dejando un año fuera ({h['n']} años): correlación "
f"**{h['r']:.2f}** y habilidad frente a la climatología **{h['habilidad']:.2f}** (0 = no mejora el promedio, 1 = perfecto).")
nuevo = [a for a in x.index if a > y.index.max()]
if nuevo:
a = max(nuevo)
c_, b_, s_ = pronostico.prediccion_con_intervalo(coef, [x.loc[a]], ly - pred)
texto += (f" Con el TNA de marzo–mayo de {a} ({x.loc[a]:+.2f} °C), el modelo estima para esa temporada **≈ {np.exp(c_):.0f} focos** "
f"(intervalo de 90 %: {np.exp(b_):.0f}–{np.exp(s_):.0f}; mediana {anios[0]}–{anios[-1]}: {y.median():.0f}); el archivo de FIRMS aún no "
"publica ese año completo.")
display(Markdown(texto))
Lectura. El Atlántico Norte tropical sí deja una huella en la temporada de incendios de la triple frontera: los años con más focos (2005, 2010 y 2024) tuvieron un TNA cálido en marzo–mayo, y la correlación con El Niño es menor. En 2025, con un TNA cercano a lo normal, el número de focos detectados fue el más bajo de la serie. Pero la habilidad del pronóstico es modesta: el índice explica una parte pequeña de la variación entre años, porque el número de focos depende también de la deforestación, de las quemas agrícolas y de las nubes durante el paso del satélite. En una extensión pequeña y con pocos focos, la señal climática pesa menos que la ignición humana: la TSM indica el riesgo de una temporada seca, no cuántos incendios habrá. Un TNA muy cálido en primavera es razón suficiente para preparar desde junio las campañas de prevención de quemas y los planes de contingencia por humo; la validación año a año muestra cuánto pesar esa señal.
3. Escenarios de uso del suelo alrededor de la ciudad¶
loss = fuentes.bosque_hansen("lossyear")
tc = fuentes.bosque_hansen("treecover2000")
b = urbana.to_crs(4326).buffer(0.05).intersection(mun.geometry.iloc[0]) # ~5 km alrededor del área urbana, lado colombiano
l = loss.rio.write_crs(4326).rio.clip(b.geometry, drop=True)
t = tc.rio.write_crs(4326).rio.clip(b.geometry, drop=True)
px_ha = (0.00025 * 111320) ** 2 * np.cos(np.radians(4.2)) / 1e4
bosque2000 = float(((t.values >= 50) & np.isfinite(t.values)).sum()) * px_ha
perdida = pd.Series(l.values.ravel()).value_counts().drop(0, errors="ignore").sort_index() * px_ha
perdida.index = perdida.index.astype(int) + 2000
tasa = perdida.loc[2015:].mean()
restante = bosque2000 - perdida.sum()
filas = [{"escenario": "Tendencia 2015–2024", "pérdida anual (ha)": tasa, "bosque en 2050 (ha)": restante - tasa * 26},
{"escenario": "Duplicación de la tasa", "pérdida anual (ha)": 2 * tasa, "bosque en 2050 (ha)": restante - 2 * tasa * 26},
{"escenario": "Deforestación cero desde 2030 (Pacto de Leticia)", "pérdida anual (ha)": tasa,
"bosque en 2050 (ha)": restante - tasa * 6}]
display(Markdown(f"Bosque (cobertura ≥ 50 %) en 2000 en un radio de ~5 km de la ciudad (lado colombiano): **{bosque2000:,.0f} ha**; perdido 2001–2024: "
f"{perdida.sum():,.0f} ha.".replace(",", ".")))
display(pd.DataFrame(filas).set_index("escenario").round(0))Lectura. Los escenarios de uso del suelo son simples extrapolaciones de la tasa reciente de pérdida de bosque. Su mensaje es de política: el Pacto de Leticia por la Amazonía (2019) y la Declaración de Belém (2023) comprometen a los países amazónicos con la reducción de la deforestación. Para Leticia, conservar el bosque periurbano reduce a la vez el riesgo de incendio, el calor urbano (3.4) y la presión sobre las fuentes de agua.
4. Coberturas del SINCHI alrededor de la ciudad, 2002–2024¶
zona = gpd.GeoSeries(b, crs=4326).to_crs(aoi.crs()).union_all()
filas = {}
for anio in pi["anios_coberturas_siatac"]:
g = fuentes.siatac("siatac_coberturas", anio=anio).to_crs(aoi.crs())
g = g.assign(geometry=g.geometry.intersection(zona))
filas[anio] = g.groupby("cob_agrup").apply(lambda x: x.area.sum() / 1e4)
cob_t = pd.DataFrame(filas).T.fillna(0)
cob_t = cob_t[cob_t.sum().sort_values(ascending=False).index]
colores = {"Bosques": viz.VERDE, "Vegetación secundaria": "#a6d96a", "Pastizales": "#fee08b", "Territorios artificializados": viz.ROJO,
"Superficies de agua": viz.AZUL_RIO, "Fragmentados": "#66bd63", "Herbazales": "#d9ef8b"}
fig, ax = plt.subplots(figsize=(10, 3.8))
ax.stackplot(cob_t.index, cob_t.T.values, labels=cob_t.columns, colors=[colores.get(c, "#cccccc") for c in cob_t.columns], alpha=0.9)
ax.set_ylabel("ha")
ax.set_title("Coberturas en un radio de ~5 km de la ciudad (lado colombiano), SINCHI 1:100.000")
ax.legend(fontsize=7, ncol=3, loc="lower left")
fig.tight_layout()
display(cob_t.round(0).astype(int))
a0, a1 = cob_t.index[0], pi["anio_coberturas_siatac"] # misma leyenda completa de 2002 a 2022
cambio = (cob_t.loc[a1] - cob_t.loc[a0]).round(0)
ritmo_bosque = -cambio.get("Bosques", 0) / (a1 - a0)
miles = lambda v: f"{v:+,.0f}".replace(",", ".")
display(Markdown(f"Cambio {a0}–{a1}: bosques **{miles(cambio.get('Bosques', 0))} ha**; vegetación secundaria {miles(cambio.get('Vegetación secundaria', 0))} ha; "
f"pastizales {miles(cambio.get('Pastizales', 0))} ha; territorios artificializados {miles(cambio.get('Territorios artificializados', 0))} ha. "
f"Pérdida neta de bosque según el SINCHI: **{ritmo_bosque:.0f} ha/año**, frente a {tasa:.0f} ha/año de pérdida bruta de cobertura "
f"arbórea según Hansen (2015–2024). La versión {cob_t.index[-1]} agrupa la leyenda y trata distinto el agua "
f"({cob_t.loc[a1, 'Superficies de agua']:.0f} ha en {a1}, {cob_t.loc[cob_t.index[-1], 'Superficies de agua']:.0f} ha en {cob_t.index[-1]}), "
"así que no se usa para la tasa."))
Lectura. Alrededor de la ciudad el paisaje cambió en dos direcciones: el bosque y los pastizales retroceden, y crecen la vegetación secundaria (potreros y chagras abandonados que se regeneran) y el área construida, que se triplicó entre 2002 y 2022. La pérdida neta de bosque del SINCHI es menor que la pérdida bruta de cobertura arbórea de Hansen, que cuenta también el desmonte de rastrojos y árboles aislados; ambas fuentes coinciden en una pérdida sostenida. Los escenarios de la sección 3 pueden usar cualquiera de las dos tasas como referencia.
- Flores, B. M., Montoya, E., Sakschewski, B., & others. (2024). Critical transitions in the Amazon forest system. Nature, 626, 555–564. 10.1038/s41586-023-06970-0
- Chen, Y., Randerson, J. T., Morton, D. C., DeFries, R. S., Collatz, G. J., Kasibhatla, P. S., & others. (2011). Forecasting fire season severity in South America using sea surface temperature anomalies. Science, 334(6057), 787–791. 10.1126/science.1209472
- Marengo, J. A., Espinoza, J. C., Fu, R., Muñoz, J. C. J., Alves, L. M., Rocha, H. R., & Schöngart, J. (2021). Chapter 22: Long-term variability, extremes and changes in temperature and hydro meteorology in the Amazon region [Amazon Assessment Report 2021]. Science Panel for the Amazon. 10.55161/ZGJG8060
- Fernandes, K., Baethgen, W., Bernardes, S., DeFries, R., DeWitt, D. G., Goddard, L., Lavado, W., Lee, D. E., Padoch, C., Pinedo-Vasquez, M., & Uriarte, M. (2011). North Tropical Atlantic influence on western Amazon fire season variability. Geophysical Research Letters, 38(12). 10.1029/2011GL047392