SIG EDUCA YouTube Tutoriales

Composición sin nubes con Sentinel-2 en Google Earth Engine

básicoLección 10 de 1810 minGoogle Earth Engine

Crea una composición de mediana sin nubes con Sentinel-2 usando la banda SCL, y expórtala a Google Drive a 10 m.

Introducción #

Sentinel-2 toma imágenes de todo el planeta cada cinco días con 10 m de resolución, así que en pocos meses acumula decenas de escenas de tu zona. Combinarlas en una composición de mediana permite obtener una imagen limpia de nubes y con mucho detalle.

En esta lección construiremos esa composición con una máscara de nubes basada en la banda SCL y exportaremos el resultado a Google Drive.

Objetivos #

Recursos #

¿Por qué la mediana? #

Las nubes son muy brillantes y las sombras muy oscuras, así que en una serie de imágenes aparecen como valores extremos. La mediana ignora esos extremos y se queda con el valor típico de cada píxel. Si además enmascaramos las nubes antes de calcularla, el resultado es una imagen continua y limpia aunque ninguna escena individual lo sea.

Paso 1: Crear la función de máscara #

La función maskS2clouds usa la banda SCL (clasificación de la escena) para ocultar sombras, nubes y cirros. Después se queda solo con las bandas espectrales y las divide entre 10 000 para pasarlas a reflectancia, de 0 a 1.

// 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);
}
Clase SCLSignificado
3Sombra de nube
8Nube, probabilidad media
9Nube, probabilidad alta
10Cirros
4Vegetación
6Agua

Paso 2: Filtrar la colección y ver la mediana con nubes #

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.

Filtramos la colección por fecha y región y calculamos una mediana sin máscara para ver el punto de partida. Los valores todavía están multiplicados por 10 000.

// Colección de Sentinel-2 filtrada por fecha y región
var s2 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
  .filterDate("2023-01-01", "2023-12-01")
  .filterBounds(AOI);
print(s2);

// Mediana sin máscara, todavía con nubes
var s2_m = s2.median().clip(AOI);
Map.addLayer(s2_m, {bands: "B4,B3,B2", min: 0, max: 3000, gamma: 1}, "Sentinel-2 mediana con nubes");
Map.centerObject(AOI, 8);

Paso 3: Composición sin nubes #

Aplicamos la máscara a cada imagen con map() y calculamos la mediana. En la segunda versión filtramos antes por CLOUDY_PIXEL_PERCENTAGE para usar solo las escenas más despejadas.

// Mediana sin nubes
var visSR = {bands: "B4,B3,B2", min: 0, max: 0.3, gamma: 1};
var s2_cf = s2.map(maskS2clouds).median().clip(AOI);
Map.addLayer(s2_cf, visSR, "Sentinel-2 mediana sin nubes");

// Mismo proceso, solo con escenas de hasta 20 % de píxeles nubosos
var s2_cc = s2.filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 20))
  .map(maskS2clouds)
  .median()
  .clip(AOI);
Map.addLayer(s2_cc, visSR, "Sentinel-2 mediana sin nubes y con filtro");

Consejo. Si quieres una composición de una temporada concreta, por ejemplo la época seca, agrega .filter(ee.Filter.calendarRange(10, 11, 'month')) a la colección para quedarte solo con las imágenes de octubre y noviembre.

Paso 4: Exportar la composición #

Exportamos las bandas azul, verde, roja, NIR y las dos SWIR a 10 m. Recuerda ejecutar la tarea desde la pestaña Tasks.

// Exportar las bandas principales a Google Drive
Export.image.toDrive({
  image: s2_cc.select(['B2', 'B3', 'B4', 'B8', 'B11', 'B12']),
  description: 'sentinel_2_cloud_free_composite',
  folder: "sentinel collections",
  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);
}

// Colección de Sentinel-2 filtrada por fecha y región
var s2 = ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
  .filterDate("2023-01-01", "2023-12-01")
  .filterBounds(AOI);
print(s2);

// Mediana sin máscara, todavía con nubes
var s2_m = s2.median().clip(AOI);
Map.addLayer(s2_m, {bands: "B4,B3,B2", min: 0, max: 3000, gamma: 1}, "Sentinel-2 mediana con nubes");
Map.centerObject(AOI, 8);

// Mediana sin nubes
var visSR = {bands: "B4,B3,B2", min: 0, max: 0.3, gamma: 1};
var s2_cf = s2.map(maskS2clouds).median().clip(AOI);
Map.addLayer(s2_cf, visSR, "Sentinel-2 mediana sin nubes");

// Mismo proceso, solo con escenas de hasta 20 % de píxeles nubosos
var s2_cc = s2.filter(ee.Filter.lte('CLOUDY_PIXEL_PERCENTAGE', 20))
  .map(maskS2clouds)
  .median()
  .clip(AOI);
Map.addLayer(s2_cc, visSR, "Sentinel-2 mediana sin nubes y con filtro");

// Exportar las bandas principales a Google Drive
Export.image.toDrive({
  image: s2_cc.select(['B2', 'B3', 'B4', 'B8', 'B11', 'B12']),
  description: 'sentinel_2_cloud_free_composite',
  folder: "sentinel collections",
  region: AOI,
  scale: 10,
  maxPixels: 1e13,
  fileFormat: "TIFF",
  formatOptions: {cloudOptimized: true}
});

Conclusiones #

Con unas pocas líneas tienes una imagen de Sentinel-2 sin nubes y a 10 m, lista para calcular índices o comparar fechas. Las siguientes lecciones parten de imágenes preparadas de esta forma.

© 2026 SIG EDUCA