Saltar al contenido principal

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

  1. Filtrar y enmascarar nubes en una colección Sentinel-2.
  2. Calcular NDVI, NDWI y NBR.
  3. Estimar la severidad de un incendio con dNBR.
  4. 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")
m

Superficie 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

  1. ¿Qué superficie (ha) tiene cada clase de severidad? ¿Cuánta corresponde a severidad alta (clase 4, dNBR ≥ 0,66)?
  2. ¿Por qué se usa la mediana y no una sola imagen?
  3. ¿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.