SIG EDUCA YouTube Tutoriales

Índices de vegetación con Sentinel-2 en Google Earth Engine

básicoLección 12 de 1815 minGoogle Earth Engine

Calcula, visualiza y exporta siete índices de vegetación con Sentinel-2 en Google Earth Engine.

Introducción #

Las plantas sanas absorben casi toda la luz roja para hacer fotosíntesis y reflejan con fuerza el infrarrojo cercano. Los índices espectrales de vegetación aprovechan ese contraste para resumir en un solo número qué tan vigorosa, densa o húmeda está la cobertura de cada píxel.

En esta lección calcularemos siete índices con Sentinel-2. Gracias a sus 10 m de resolución podremos ver diferencias entre lotes, parques o bordes de bosque que en Landsat se pierden.

Objetivos #

Recursos #

Los índices #

ÍndiceFórmulaPara qué sirve
NDVI(NIR − R) / (NIR + R)Vigor y densidad de la vegetación
EVI2,5 · (NIR − R) / (NIR + 6R − 7,5B + 1)Vegetación densa, corrige efectos de la atmósfera
SAVI(NIR − R) / (NIR + R + 0,5) · 1,5Zonas con suelo expuesto
MSAVI(2NIR + 1 − √((2NIR + 1)² − 8(NIR − R))) / 2Vegetación escasa sin fijar el factor L
NDMI(NIR − SWIR1) / (NIR + SWIR1)Contenido de humedad de la vegetación
NBR(NIR − SWIR2) / (NIR + SWIR2)Áreas quemadas
NBR2(SWIR1 − SWIR2) / (SWIR1 + SWIR2)Severidad de quemas y humedad del suelo

De dónde viene cada índice #

Cada índice nació para resolver un problema concreto. Conocer esa historia ayuda a elegir el adecuado para tu caso.

BandaSentinel-2Resolución
Azul (B)B210 m
Rojo (R)B410 m
NIRB810 m
SWIR1B1120 m
SWIR2B1220 m

Paso 1: Preparar la imagen #

Aplicamos la máscara de nubes, que además convierte los valores en reflectancia, y tomamos la primera escena con poca nubosidad sobre el AOI.

El script usa una geometría llamada AOI. Dibújala con las herramientas de geometría del mapa en el Code Editor y renómbrala como AOI en el panel de importaciones.

// Máscara de nubes con la banda SCL y paso a reflectancia (0 a 1)
function maskS2clouds(img) {
  var scl = img.select('SCL');
  var mask = scl.neq(3)    // sombra de nubes
    .and(scl.neq(8))       // nubes, probabilidad media
    .and(scl.neq(9))       // nubes, probabilidad alta
    .and(scl.neq(10));     // cirros
  return img.updateMask(mask).select('B.*').divide(10000);
}
// Primera imagen de Sentinel-2 con poca nubosidad sobre el AOI
var s2_image = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
  .filterDate("2023-01-01", "2023-12-01")
  .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(AOI)
  .map(maskS2clouds)
  .first()
  .clip(AOI);
print(s2_image);
// Imagen en color verdadero
Map.addLayer(s2_image, {bands: "B4,B3,B2", min: 0, max: 0.3, gamma: 1}, "Sentinel-2");
Map.centerObject(AOI, 12);

Paso 2: Calcular los índices #

Para los índices que son una diferencia normalizada entre dos bandas, normalizedDifference() es lo más corto. Para fórmulas con constantes o raíces usamos expression(), que asigna un nombre a cada banda dentro de la ecuación.

// Índices con las bandas de Sentinel-2 (B2 azul, B4 rojo, B8 NIR, B11 SWIR1, B12 SWIR2)
var NDVI = s2_image.normalizedDifference(['B8', 'B4']).rename('NDVI');
var EVI = s2_image.expression('2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
  'NIR': s2_image.select('B8'), 'RED': s2_image.select('B4'), 'BLUE': s2_image.select('B2')
}).rename('EVI');
var SAVI = s2_image.expression('((NIR - RED) / (NIR + RED + 0.5)) * 1.5', {
  'NIR': s2_image.select('B8'), 'RED': s2_image.select('B4')
}).rename('SAVI');
var MSAVI = s2_image.expression('(2 * NIR + 1 - sqrt(pow((2 * NIR + 1), 2) - 8 * (NIR - RED))) / 2', {
  'NIR': s2_image.select('B8'), 'RED': s2_image.select('B4')
}).rename('MSAVI');
var NDMI = s2_image.normalizedDifference(['B8', 'B11']).rename('NDMI');
var NBR = s2_image.normalizedDifference(['B8', 'B12']).rename('NBR');
var NBR2 = s2_image.normalizedDifference(['B11', 'B12']).rename('NBR2');

Paso 3: Visualizar los índices #

Los índices de vegetación van de 0 (sin vegetación) a 1 (vegetación muy densa), así que comparten una paleta que pasa del café al verde oscuro. NDMI y NBR tienen paletas propias.

// Simbologías
var vegPalette = {min: 0, max: 1, palette: ['0000ff', 'DF923D', 'F1B555', 'FCD163', '99B718',
  '74A901', '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01', '012E01',
  '011D01', '011301']};
var ndmiParams = {min: -0.5, max: 0.5, palette: ["ff0000", "ffa500", "ffff00", "00ff00", "187a07"]};
var nbrParams = {min: 0, max: 0.5, palette: ["e47f44", "ffe483", "5d61ed", "2902ff"]};

Map.addLayer(NDVI, vegPalette, 'NDVI');
Map.addLayer(EVI, vegPalette, 'EVI');
Map.addLayer(SAVI, vegPalette, 'SAVI');
Map.addLayer(MSAVI, vegPalette, 'MSAVI');
Map.addLayer(NDMI, ndmiParams, 'NDMI');
Map.addLayer(NBR, nbrParams, 'NBR');
Map.addLayer(NBR2, nbrParams, 'NBR2');

Paso 4: Exportar los índices #

Unimos los siete índices en una sola imagen y la exportamos a 10 m.

var index = NDVI.addBands([EVI, SAVI, MSAVI, NDMI, NBR, NBR2]).toFloat();

Export.image.toDrive({
  image: index,
  description: "sentinel_2_vegetation_index",
  folder: "index",
  region: AOI,
  scale: 10,
  maxPixels: 1e13,
  fileFormat: "TIFF",
  formatOptions: {cloudOptimized: true}
});

Código completo #

Este es el script completo. Cópialo, pégalo en el Code Editor, dibuja tu AOI y presiona Run.

// Máscara de nubes con la banda SCL y paso a reflectancia (0 a 1)
function maskS2clouds(img) {
  var scl = img.select('SCL');
  var mask = scl.neq(3)    // sombra de nubes
    .and(scl.neq(8))       // nubes, probabilidad media
    .and(scl.neq(9))       // nubes, probabilidad alta
    .and(scl.neq(10));     // cirros
  return img.updateMask(mask).select('B.*').divide(10000);
}

// Primera imagen de Sentinel-2 con poca nubosidad sobre el AOI
var s2_image = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
  .filterDate("2023-01-01", "2023-12-01")
  .filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 10))
  .filterBounds(AOI)
  .map(maskS2clouds)
  .first()
  .clip(AOI);
print(s2_image);

// Imagen en color verdadero
Map.addLayer(s2_image, {bands: "B4,B3,B2", min: 0, max: 0.3, gamma: 1}, "Sentinel-2");
Map.centerObject(AOI, 12);

// Índices con las bandas de Sentinel-2 (B2 azul, B4 rojo, B8 NIR, B11 SWIR1, B12 SWIR2)
var NDVI = s2_image.normalizedDifference(['B8', 'B4']).rename('NDVI');
var EVI = s2_image.expression('2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', {
  'NIR': s2_image.select('B8'), 'RED': s2_image.select('B4'), 'BLUE': s2_image.select('B2')
}).rename('EVI');
var SAVI = s2_image.expression('((NIR - RED) / (NIR + RED + 0.5)) * 1.5', {
  'NIR': s2_image.select('B8'), 'RED': s2_image.select('B4')
}).rename('SAVI');
var MSAVI = s2_image.expression('(2 * NIR + 1 - sqrt(pow((2 * NIR + 1), 2) - 8 * (NIR - RED))) / 2', {
  'NIR': s2_image.select('B8'), 'RED': s2_image.select('B4')
}).rename('MSAVI');
var NDMI = s2_image.normalizedDifference(['B8', 'B11']).rename('NDMI');
var NBR = s2_image.normalizedDifference(['B8', 'B12']).rename('NBR');
var NBR2 = s2_image.normalizedDifference(['B11', 'B12']).rename('NBR2');

// Simbologías
var vegPalette = {min: 0, max: 1, palette: ['0000ff', 'DF923D', 'F1B555', 'FCD163', '99B718',
  '74A901', '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01', '012E01',
  '011D01', '011301']};
var ndmiParams = {min: -0.5, max: 0.5, palette: ["ff0000", "ffa500", "ffff00", "00ff00", "187a07"]};
var nbrParams = {min: 0, max: 0.5, palette: ["e47f44", "ffe483", "5d61ed", "2902ff"]};

Map.addLayer(NDVI, vegPalette, 'NDVI');
Map.addLayer(EVI, vegPalette, 'EVI');
Map.addLayer(SAVI, vegPalette, 'SAVI');
Map.addLayer(MSAVI, vegPalette, 'MSAVI');
Map.addLayer(NDMI, ndmiParams, 'NDMI');
Map.addLayer(NBR, nbrParams, 'NBR');
Map.addLayer(NBR2, nbrParams, 'NBR2');

var index = NDVI.addBands([EVI, SAVI, MSAVI, NDMI, NBR, NBR2]).toFloat();

Export.image.toDrive({
  image: index,
  description: "sentinel_2_vegetation_index",
  folder: "index",
  region: AOI,
  scale: 10,
  maxPixels: 1e13,
  fileFormat: "TIFF",
  formatOptions: {cloudOptimized: true}
});

Conclusiones #

Sentinel-2 es ideal para seguir la vegetación a escala de finca o de ciudad. Recuerda que NDMI, NBR y NBR2 usan bandas SWIR de 20 m, así que en esos índices el detalle es algo menor que en NDVI.

Referencias #

© 2026 SIG EDUCA