25 Práctica 4 · Google Earth Engine: Sentinel-2, índices y severidad de incendios
Duración: 3 horas · Software: Google Earth Engine (Python + geemap) · Lectura previa: Capítulo 4, Capítulo 12 Cuota estimada: < 5 EECU-hora (cabe en el nivel Community)
25.1 Objetivos
- Filtrar y enmascarar nubes en una colección Sentinel-2.
- Calcular NDVI, NDWI y NBR.
- Estimar la severidad de un incendio con dNBR.
- Exportar resultados a GeoTIFF en un SRC chileno.
25.2 Caso de estudio
Incendio de Viña del Mar y Valparaíso (febrero de 2024). Ajusta las fechas y el área si trabajas otro evento de tu región.
25.3 Código
import ee, geemap
ee.Initialize(project="geomatica-chile-tunombre")
# Cubre Viña del Mar, Quilpué, Villa Alemana y el sector de Peñuelas; contrástalo con el perímetro oficial de CONAF
aoi = ee.Geometry.Rectangle([-71.62, -33.18, -71.33, -32.98])
def mascara_nubes(img):
scl = img.select("SCL")
ok = scl.neq(3).And(scl.neq(8)).And(scl.neq(9)).And(scl.neq(10))
return img.updateMask(ok).divide(10000).copyProperties(img, ["system:time_start"])
def coleccion(inicio, fin):
return (ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
.filterBounds(aoi).filterDate(inicio, fin)
.filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 30))
.map(mascara_nubes).median().clip(aoi))
pre = coleccion("2024-01-10", "2024-01-31")
post = coleccion("2024-02-10", "2024-03-05")
def nbr(img): return img.normalizedDifference(["B8", "B12"]).rename("NBR")
def ndvi(img): return img.normalizedDifference(["B8", "B4"]).rename("NDVI")
def ndwi(img): return img.normalizedDifference(["B3", "B8"]).rename("NDWI") # McFeeters
# Índices de la escena post-incendio
indices = ndvi(post).addBands(ndwi(post)).addBands(nbr(post))
dnbr = nbr(pre).subtract(nbr(post)).rename("dNBR")
# Clases según Key y Benson (2006): 0 = no quemado o rebrote, 1 = baja,
# 2 = moderada-baja, 3 = moderada-alta, 4 = alta
severidad = (dnbr.where(dnbr.lt(0.10), 0)
.where(dnbr.gte(0.10).And(dnbr.lt(0.27)), 1)
.where(dnbr.gte(0.27).And(dnbr.lt(0.44)), 2)
.where(dnbr.gte(0.44).And(dnbr.lt(0.66)), 3)
.where(dnbr.gte(0.66), 4)).toInt()
m = geemap.Map(center=[-33.05, -71.5], zoom=12)
m.addLayer(post, {"bands": ["B12", "B8", "B4"], "min": 0, "max": 0.35}, "Post (SWIR)")
m.addLayer(indices.select("NDVI"), {"min": -0.2, "max": 0.8,
"palette": ["#8B1E1E", "#FFE08A", "#1E7B3A"]}, "NDVI post")
m.addLayer(severidad, {"min": 0, "max": 4,
"palette": ["#EEF1F5", "#FFE08A", "#F5A35C", "#E8772A", "#8B1E1E"]}, "Severidad")
mSuperficie por clase de severidad
area = ee.Image.pixelArea().divide(10000).addBands(severidad)
stats = area.reduceRegion(
reducer=ee.Reducer.sum().group(groupField=1, groupName="clase"),
geometry=aoi, scale=20, maxPixels=1e9)
print(stats.getInfo())Exportar a Drive en UTM 19S (SIRGAS-Chile 2025)
ee.batch.Export.image.toDrive(
image=dnbr, description="dNBR_Valparaiso_2024",
region=aoi, scale=20, crs="EPSG:11476", maxPixels=1e9).start()
# Si Earth Engine rechaza EPSG:11476, usa "EPSG:5361" (SIRGAS-Chile 2002 / UTM 19S)
NotaSRC de exportación
EPSG:11476 es SIRGAS-Chile 2025 / UTM 19S. Earth Engine solo acepta los SRC presentes en su versión de la base EPSG; si el código aún no está disponible, exporta en EPSG:5361. A 20 m de resolución la diferencia entre épocas de SIRGAS-Chile es despreciable, pero documenta siempre el SRC usado.
25.4 Preguntas
- ¿Qué superficie (ha) tiene cada clase de severidad? ¿Cuánta corresponde a severidad alta (clase 4, dNBR ≥ 0,66)?
- ¿Por qué se usa la mediana y no una sola imagen?
- ¿Qué limitaciones tiene el dNBR en zonas urbanas?
25.5 Entregable
Mapa de severidad diseñado según Capítulo 17, tabla de superficies y el notebook ejecutado.