SIG EDUCA YouTube Tutoriales

Composición sin nubes con Landsat 9 en Google Earth Engine

básicoLección 9 de 1810 minGoogle Earth Engine

Crea una composición de mediana sin nubes con Landsat 9 aplicando el factor de escala y la máscara QA_PIXEL, y expórtala a Google Drive.

Introducción #

Una sola imagen satelital casi nunca está limpia. En zonas tropicales las nubes aparecen en buena parte de las escenas, y eso complica cualquier análisis. La solución es combinar muchas imágenes de un mismo periodo en una composición de mediana.

En esta lección construiremos una composición sin nubes con Landsat 9. Crearemos funciones para aplicar el factor de escala y la máscara de nubes, compararemos el antes y el después, 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.

Ojo con el orden. La máscara usa la banda QA_PIXEL, que no debe escalarse. Por eso aplicamos primero mask_clouds y después scaler.

Paso 1: Crear las funciones #

Landsat Colección 2 guarda la reflectancia como números enteros. La función scaler los convierte en reflectancia real con los factores que publica el USGS, y mask_clouds lee los bits de la banda QA_PIXEL para ocultar cirros, nubes, sombras de nubes y nieve.

// Factor de escala: convierte los valores digitales en reflectancia
function scaler(img) {
  var optical = img.select('SR_B.').multiply(0.0000275).add(-0.2).toFloat();
  var thermal = img.select('ST_B.*').multiply(0.00341802).add(149).toFloat();
  return img.addBands(optical, null, true).addBands(thermal, null, true);
}

// Máscara de nubes con la banda QA_PIXEL
function mask_clouds(img) {
  var qa = img.select('QA_PIXEL');
  var cirrus = 1 << 2;       // bit 2, cirros
  var cloud = 1 << 3;        // bit 3, nubes
  var cloudShadow = 1 << 4;  // bit 4, sombra de nubes
  var snow = 1 << 5;         // bit 5, nieve
  var mask = qa.bitwiseAnd(cirrus).eq(0)
    .and(qa.bitwiseAnd(cloud).eq(0))
    .and(qa.bitwiseAnd(cloudShadow).eq(0))
    .and(qa.bitwiseAnd(snow).eq(0));
  return img.updateMask(mask);
}
Bit de QA_PIXELSignificado
1Nube dilatada
2Cirros
3Nube
4Sombra de nube
5Nieve
7Agua

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 Landsat 9 por fecha y región y calculamos una mediana sin ninguna corrección. Así vemos el punto de partida, todavía con restos de nubes y en valores digitales.

// Colección de Landsat 9 filtrada por fecha y región
var ls9 = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2")
  .filterDate('2023-01-01', '2023-06-30')
  .filterBounds(AOI);
print(ls9);

// Mediana sin máscara, todavía con nubes
var ls9_m = ls9.median().clip(AOI);
var visRaw = {bands: "SR_B4,SR_B3,SR_B2", min: 7200, max: 13800, gamma: 1};
Map.addLayer(ls9_m, visRaw, "Landsat 9 mediana con nubes");
Map.centerObject(AOI, 8);

Paso 3: Composición sin nubes #

Ahora aplicamos las dos funciones a cada imagen con map() y luego calculamos la mediana. La segunda versión añade un filtro por CLOUD_COVER_LAND para usar solo las escenas más despejadas, lo que suele dar colores más limpios.

// Mediana sin nubes: primero la máscara, luego el factor de escala
var ls9_cf = ls9.map(mask_clouds).map(scaler).median().clip(AOI);
var visSR = {bands: "SR_B4,SR_B3,SR_B2", min: 0.002, max: 0.182, gamma: 1};
Map.addLayer(ls9_cf, visSR, "Landsat 9 mediana sin nubes");

// Mismo proceso, pero solo con escenas de menos del 10 % de nubes en tierra
var ls9_cc = ls9.filter(ee.Filter.lt("CLOUD_COVER_LAND", 10))
  .map(mask_clouds).map(scaler).median().clip(AOI);
Map.addLayer(ls9_cc, visSR, "Landsat 9 mediana sin nubes y con filtro");

Paso 4: Exportar la composición #

Exportamos solo las bandas ópticas SR_B1 a SR_B7 a 30 m. La tarea aparece en la pestaña Tasks, donde debes ejecutarla.

// Exportar las bandas ópticas a Google Drive
Export.image.toDrive({
  image: ls9_cc.select(["SR_B[1-7]"]),
  description: "landsat_9_cloud_free_composite",
  folder: "landsat composicion sin nubes",
  region: AOI,
  scale: 30,
  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.

// Factor de escala: convierte los valores digitales en reflectancia
function scaler(img) {
  var optical = img.select('SR_B.').multiply(0.0000275).add(-0.2).toFloat();
  var thermal = img.select('ST_B.*').multiply(0.00341802).add(149).toFloat();
  return img.addBands(optical, null, true).addBands(thermal, null, true);
}

// Máscara de nubes con la banda QA_PIXEL
function mask_clouds(img) {
  var qa = img.select('QA_PIXEL');
  var cirrus = 1 << 2;       // bit 2, cirros
  var cloud = 1 << 3;        // bit 3, nubes
  var cloudShadow = 1 << 4;  // bit 4, sombra de nubes
  var snow = 1 << 5;         // bit 5, nieve
  var mask = qa.bitwiseAnd(cirrus).eq(0)
    .and(qa.bitwiseAnd(cloud).eq(0))
    .and(qa.bitwiseAnd(cloudShadow).eq(0))
    .and(qa.bitwiseAnd(snow).eq(0));
  return img.updateMask(mask);
}

// Colección de Landsat 9 filtrada por fecha y región
var ls9 = ee.ImageCollection("LANDSAT/LC09/C02/T1_L2")
  .filterDate('2023-01-01', '2023-06-30')
  .filterBounds(AOI);
print(ls9);

// Mediana sin máscara, todavía con nubes
var ls9_m = ls9.median().clip(AOI);
var visRaw = {bands: "SR_B4,SR_B3,SR_B2", min: 7200, max: 13800, gamma: 1};
Map.addLayer(ls9_m, visRaw, "Landsat 9 mediana con nubes");
Map.centerObject(AOI, 8);

// Mediana sin nubes: primero la máscara, luego el factor de escala
var ls9_cf = ls9.map(mask_clouds).map(scaler).median().clip(AOI);
var visSR = {bands: "SR_B4,SR_B3,SR_B2", min: 0.002, max: 0.182, gamma: 1};
Map.addLayer(ls9_cf, visSR, "Landsat 9 mediana sin nubes");

// Mismo proceso, pero solo con escenas de menos del 10 % de nubes en tierra
var ls9_cc = ls9.filter(ee.Filter.lt("CLOUD_COVER_LAND", 10))
  .map(mask_clouds).map(scaler).median().clip(AOI);
Map.addLayer(ls9_cc, visSR, "Landsat 9 mediana sin nubes y con filtro");

// Exportar las bandas ópticas a Google Drive
Export.image.toDrive({
  image: ls9_cc.select(["SR_B[1-7]"]),
  description: "landsat_9_cloud_free_composite",
  folder: "landsat composicion sin nubes",
  region: AOI,
  scale: 30,
  maxPixels: 1e13,
  fileFormat: "TIFF",
  formatOptions: {cloudOptimized: true}
});

Conclusiones #

La composición de mediana es la base de casi todo lo que viene en el curso. Con una imagen limpia de tu zona ya puedes calcular índices espectrales, detectar agua o comparar fechas sin que las nubes arruinen el resultado. En la siguiente lección haremos lo mismo con Sentinel-2.

© 2026 SIG EDUCA