Tutoriales / Imágenes
Cómo hacer un mosaico Landsat sin nubes en Google Earth Engine
Un mosaico sin nubes combina varias imágenes Landsat del mismo lugar, después de quitar de cada una las nubes y sus sombras. Aquí verás cómo hacerlo en Google Earth Engine con la imagen más despejada de cada mes. También, cómo elegir la escena por su path y row, usar las bandas QA_PIXEL y QA_RADSAT y la escala de la Colección 2, y exportar en la proyección de la escena. El mismo flujo en el Code Editor (JavaScript) y en Colab (Python).

Qué es un mosaico sin nubes
Un mosaico sin nubes es una imagen hecha con varias fotos del mismo lugar tomadas en fechas distintas. En cada una se quitan los píxeles con nubes o con sombra de nube, y para cada píxel se combina lo que queda. El resultado es una vista limpia de la zona en ese año.
Hace falta porque una sola imagen rara vez llega despejada, sobre todo en zonas tropicales. Landsat pasa por el mismo lugar cada 16 días por satélite, y casi siempre alguna parte de la escena viene cubierta.
Es lo que hace Landsat Explorer, nuestra aplicación de mosaicos de Venezuela: eliges la escena, el año y las imágenes, y genera el mosaico. Este tutorial explica cada paso y da un script que, además, elige solo las imágenes: la más despejada de cada mes.
Escenas de Landsat: path y row
Landsat organiza sus imágenes en una cuadrícula fija, el sistema WRS-2. Cada escena tiene un path (la órbita) y un row (la fila a lo largo de ella), y siempre cubre la misma zona. En Earth Engine se filtran con las propiedades WRS_PATH y WRS_ROW.
El nombre de cada imagen lo dice todo. Por ejemplo, LC08_006052_20250218:
| Parte | Significa |
|---|---|
LC08 | Landsat 8 (en Landsat 9, LC09; en Landsat 5, LT05; en Landsat 7, LE07) |
006052 | Path 6, row 52 |
20250218 | Fecha de adquisición: 18 de febrero de 2025 |
Bandas y escala de la Colección 2
Landsat 8 y 9 numeran sus bandas de forma distinta a Landsat 4, 5 y 7. Para combinar sensores o reutilizar código, conviene renombrarlas, como hace el script:
| Banda | Landsat 4, 5 y 7 | Landsat 8 y 9 |
|---|---|---|
| Azul | SR_B1 | SR_B2 |
| Verde | SR_B2 | SR_B3 |
| Rojo | SR_B3 | SR_B4 |
| Infrarrojo cercano | SR_B4 | SR_B5 |
| Infrarrojo de onda corta 1 | SR_B5 | SR_B6 |
| Infrarrojo de onda corta 2 | SR_B7 | SR_B7 |
La reflectancia de la Colección 2 se guarda como números enteros. Para convertirla hay que multiplicar por 0,0000275 y restar 0,2. El desplazamiento no se cancela en los índices ni en la mediana: sin él, los valores salen mal aunque el mapa se vea bien (ejemplo con el NDVI).
Cómo quitar nubes y sombras con QA_PIXEL
Hay dos filtros distintos y hacen falta los dos. CLOUD_COVER es el porcentaje de nubes de toda la escena, según sus metadatos: sirve para elegir imágenes. La banda QA_PIXEL marca cada píxel: sirve para quitar justo las nubes que quedan en las imágenes elegidas. El script descarta un píxel si cualquiera de estos bits está activo:
| Bit de QA_PIXEL | Marca | Sensores |
|---|---|---|
| 0 | Relleno: fuera de la imagen, sin datos | Todos |
| 1 | Nube dilatada (el borde de la nube) | Todos |
| 2 | Cirros | Solo Landsat 8 y 9; en Landsat 4, 5 y 7 no se usa |
| 3 | Nube | Todos |
| 4 | Sombra de nube | Todos |
| 5 | Nieve | Todos |
Las sombras importan tanto como las nubes: oscurecen el suelo y la vegetación, y en una mediana con pocas imágenes pueden quedar como manchas. La nieve se quita porque el mosaico busca el suelo; en zonas donde la nieve es lo que quieres ver, quita el bit 5 de la máscara.
La segunda máscara usa la banda QA_RADSAT. En Landsat 8 y 9 marca los píxeles con alguna banda saturada (bits 0 a 6 y 8) y los que el relieve tapa al sensor (bit 11). El script descarta el píxel si tiene cualquiera de esas marcas, también en bandas que luego no usa: es la opción más prudente.
La imagen más despejada de cada mes y la mediana
La mediana toma, para cada píxel, el valor del medio de las fechas disponibles. Un resto de nube o de sombra que se escapó a la máscara es un valor extremo, y la mediana lo deja fuera; el promedio, en cambio, lo mezcla con el resto.
El script no usa todas las imágenes del año, sino la de menor CLOUD_COVER de cada mes. Así, cada mes pesa lo mismo en la mediana: un mes con muchas pasadas despejadas no domina el resultado. Por eso el filtro inicial es permisivo (70 %): en los meses lluviosos, una imagen con nubes parciales aporta lo que la máscara deja limpio. Los meses sin ninguna imagen se saltan.
Dos matices. CLOUD_COVER mide toda la escena, así que la imagen «más despejada» puede tener justo nubes sobre tu zona; la capa de observaciones del script lo muestra. Y un mosaico de todo el año mezcla la época seca con la de lluvias. Si quieres el paisaje de una sola estación, limita los meses (por ejemplo, con ee.Filter.calendarRange) o elige las imágenes a mano, como en Landsat Explorer.
Si adaptas el script a Landsat 5 y 7, ten en cuenta que el corrector de líneas de Landsat 7 falló el 31 de mayo de 2003. Desde entonces cada escena pierde cerca del 22 % de sus píxeles en franjas, según el USGS. Varias fechas en la mediana rellenan esas franjas; la capa de observaciones muestra dónde no lo consiguen.
La proyección al exportar
Cada escena Landsat viene en la proyección UTM de su zona. El mosaico, en cambio, no: según la guía de proyecciones de Earth Engine, una imagen hecha con median() a partir de una colección queda con WGS84 a 1° como proyección por defecto. Y una exportación sin crs toma el CRS de la primera banda de la imagen.
Sin hacer nada, el archivo saldría en EPSG:4326 y no en UTM. El script copia la proyección de la escena al mosaico con setDefaultProjection, así que exporta en su UTM sin escribir el código EPSG a mano: sirve para cualquier path y row.
El script
Busca las imágenes de Landsat 8 y 9 de la escena y el año, y elige la más despejada de cada mes. De cada una quita las nubes, las sombras, la nieve y los píxeles saturados; luego calcula la mediana y exporta el mosaico y una capa de observaciones. El ejemplo es la escena 002/052, la de la isla de Margarita (Nueva Esparta, Venezuela), en 2025.
Pega esto en el Code Editor y cambia el path, el row y el año por los de tu zona. Las funciones están al final del script: JavaScript permite usarlas antes de declararlas.
/**
* Automatic monthly Landsat mosaic
* Collection 2, Level 2
*
* WRS-2:
* Path: 2
* Row: 52
*
* The script automatically selects the clearest Landsat
* scene available for each month and creates an annual
* cloud-free median mosaic.
*
* Satellites:
* - Landsat 8
* - Landsat 9
*/
/**
* Configuration
*/
var path = 2;
var row = 52;
var year = 2025;
// Initial scene-level cloud filter.
// A relatively permissive threshold is useful because the
// best scene will later be selected independently for each month.
var max_cloud_cover = 70;
var collections = [
'LANDSAT/LC08/C02/T1_L2',
'LANDSAT/LC09/C02/T1_L2',
];
var input_bands = [
'SR_B2',
'SR_B3',
'SR_B4',
'SR_B5',
'SR_B6',
'SR_B7',
];
var output_bands = [
'blue',
'green',
'red',
'nir',
'swir1',
'swir2',
];
/**
* Dates
*/
var start_date = ee.Date.fromYMD(year, 1, 1);
var end_date = start_date.advance(1, 'year'); // filterDate excludes the end date
/**
* Candidate Landsat images
*/
var raw_collection = get_collection(collections, start_date, end_date);
print(
'Available image count',
raw_collection.size()
);
print(
'Available images',
raw_collection.aggregate_array(
'LANDSAT_PRODUCT_ID'
)
);
print(
'Available CLOUD_COVER',
raw_collection.aggregate_array(
'CLOUD_COVER'
)
);
/**
* Automatic monthly selection
*/
var selected_raw = select_best_monthly_images(
raw_collection
);
print(
'Automatically selected image count',
selected_raw.size()
);
print(
'Automatically selected images',
selected_raw.aggregate_array(
'LANDSAT_PRODUCT_ID'
)
);
print(
'Selected months',
selected_raw.aggregate_array('selection_month')
);
print(
'Selected CLOUD_COVER',
selected_raw.aggregate_array('CLOUD_COVER')
);
/**
* Apply pixel-level QA masking
*
* Only the automatically selected monthly scenes are processed.
*/
var selection = selected_raw
.map(prepare_image)
.sort('system:time_start');
/**
* Scene geometry and projection
*
* A median composite has no native projection: Earth Engine
* gives it WGS84 at 1 degree. The UTM projection of the scene
* is kept so the export does not fall back to EPSG:4326.
*/
var first_image = ee.Image(raw_collection.first());
var zone = first_image.geometry();
var projection = first_image
.select('SR_B4')
.projection();
/**
* Annual median mosaic
*/
var mosaic = selection
.median()
.setDefaultProjection(projection)
.clip(zone);
/**
* Cloud-free observation count
*
* Number of valid observations contributing to each
* pixel of the annual mosaic (0 where none is left).
*/
var observations = selection
.select('red')
.count()
.unmask(0)
.rename('observations')
.setDefaultProjection(projection)
.clip(zone);
/**
* Pixels without observations
*/
var no_observations = observations
.eq(0)
.selfMask()
.rename('no_observations');
/**
* Low-observation pixels
*
* Areas supported by only one valid observation
* during the year.
*/
var low_observations = observations
.eq(1)
.selfMask()
.rename('low_observations');
/**
* Visualization
*/
var vis_true_color = {
bands: ['red', 'green', 'blue'],
min: 0,
max: 0.25,
};
var vis_false_color = {
bands: ['swir1', 'nir', 'red'],
min: [0, 0, 0],
max: [0.35, 0.45, 0.20],
};
var vis_observations = {
min: 1,
max: 12,
palette: [
'd73027',
'fc8d59',
'fee08b',
'ffffbf',
'd9ef8b',
'91cf60',
'1a9850',
'006837',
],
};
var vis_no_observations = {
palette: ['ff0000'],
};
var vis_low_observations = {
palette: ['ff9900'],
};
/**
* Map
*/
Map.setOptions('SATELLITE');
Map.centerObject(zone, 9);
Map.addLayer(
mosaic,
vis_false_color,
'False color (SWIR1, NIR, red)',
true
);
Map.addLayer(
mosaic,
vis_true_color,
'True color',
false
);
Map.addLayer(
observations,
vis_observations,
'Cloud-free observations',
false
);
Map.addLayer(
low_observations,
vis_low_observations,
'Only one valid observation',
false
);
Map.addLayer(
no_observations,
vis_no_observations,
'Pixels without observations',
false
);
/**
* Export
*
* Surface reflectance is multiplied by 10000 and
* exported as signed 16-bit integers.
*
* No CRS is hardcoded: the export takes the CRS of the first
* band, which is the scene's UTM projection set above.
*/
Export.image.toDrive({
image: mosaic
.multiply(10000)
.round()
.toInt16(),
description: 'landsat_monthly_mosaic_' + path + '_' + row + '_' + year,
folder: 'earthengine',
region: zone,
scale: 30,
maxPixels: 1e10,
});
/**
* Export observation count
*
* Optional quality layer showing how many cloud-free
* observations support each pixel.
*/
Export.image.toDrive({
image: observations.toUint8(),
description: 'landsat_observations_' + path + '_' + row + '_' + year,
folder: 'earthengine',
region: zone,
scale: 30,
maxPixels: 1e10,
});
/**
* Prepare Landsat image
*
* Applies Collection 2 Level 2 surface reflectance scaling
* and masks invalid pixels using QA_PIXEL and QA_RADSAT.
*
* Masked conditions:
* - fill
* - dilated cloud
* - cirrus
* - cloud
* - cloud shadow
* - snow
* - radiometric saturation or terrain occlusion
*
* @param {ee.Image} image Landsat Collection 2 Level 2 image.
* @returns {ee.Image} Cloud-free surface reflectance image.
*/
function prepare_image(image) {
var qa = image.select('QA_PIXEL');
var clear_mask = qa
.bitwiseAnd(1 << 0)
.eq(0)
.and(qa.bitwiseAnd(1 << 1).eq(0))
.and(qa.bitwiseAnd(1 << 2).eq(0))
.and(qa.bitwiseAnd(1 << 3).eq(0))
.and(qa.bitwiseAnd(1 << 4).eq(0))
.and(qa.bitwiseAnd(1 << 5).eq(0));
var saturation_mask = image
.select('QA_RADSAT')
.eq(0);
var reflectance = image
.select(input_bands, output_bands)
.multiply(0.0000275)
.add(-0.2)
.updateMask(clear_mask)
.updateMask(saturation_mask);
// copyProperties returns an ee.Element: cast it back to ee.Image
return ee.Image(
reflectance.copyProperties(
image,
[
'system:time_start',
'CLOUD_COVER',
'CLOUD_COVER_LAND',
'WRS_PATH',
'WRS_ROW',
'SPACECRAFT_ID',
'LANDSAT_PRODUCT_ID',
]
)
).set('image_id', image.id());
}
/**
* Load Landsat collections
*
* Loads all candidate Landsat scenes for the selected
* WRS-2 path/row and year.
*
* @param {Array<string>} collection_ids Landsat collection IDs.
* @param {ee.Date} start_date Start date.
* @param {ee.Date} end_date End date.
* @returns {ee.ImageCollection} Combined raw Landsat collection.
*/
function get_collection(collection_ids, start_date, end_date) {
var collection = ee.ImageCollection([]);
collection_ids.forEach(function (collection_id) {
var landsat = ee.ImageCollection(collection_id)
.filter(ee.Filter.eq('WRS_PATH', path))
.filter(ee.Filter.eq('WRS_ROW', row))
.filterDate(start_date, end_date)
.filter(
ee.Filter.lte('CLOUD_COVER', max_cloud_cover)
);
collection = collection.merge(landsat);
});
return collection
.sort('system:time_start');
}
/**
* Select best image of each month
*
* For each month, selects the candidate scene with the
* lowest CLOUD_COVER metadata value.
*
* Only months with images are visited, so months without
* candidates are skipped without conditionals or nulls.
*
* Selection happens before pixel-level QA masking to keep
* computation lightweight.
*
* @param {ee.ImageCollection} collection Raw Landsat collection.
* @returns {ee.ImageCollection} Best scene from each available month.
*/
function select_best_monthly_images(collection) {
var with_month = collection.map(function (image) {
return image.set(
'selection_month',
ee.Date(image.get('system:time_start')).get('month')
);
});
var months = with_month
.aggregate_array('selection_month')
.distinct();
var images = months.map(function (month) {
return with_month
.filter(ee.Filter.eq('selection_month', month))
.sort('CLOUD_COVER')
.first();
});
return ee.ImageCollection
.fromImages(images)
.sort('system:time_start');
}La consola muestra las imágenes disponibles, las elegidas y su mes, cada una con su CLOUD_COVER. En el mapa, sobre la vista satelital, se cargan el mosaico en falso color (visible) y en color real, y el número de observaciones sin nubes por píxel. Dos capas de control marcan los píxeles con una sola observación válida, en naranja, y los que no tienen ninguna, en rojo. En Tasks quedan dos exportaciones: el mosaico, con la reflectancia multiplicada por 10 000, y la capa de observaciones.
La lógica es idéntica, pero las funciones van antes de usarse, como exige Python. Los operadores lógicos van con mayúscula (.And()) porque and es palabra reservada. Las listas de la consola se traen con getInfo(), que aquí es seguro porque son unos pocos valores. Las dos exportaciones arrancan solas con .start().
"""
Automatic monthly Landsat mosaic
Collection 2, Level 2
WRS-2:
Path: 2
Row: 52
The script automatically selects the clearest Landsat
scene available for each month and creates an annual
cloud-free median mosaic.
Satellites:
- Landsat 8
- Landsat 9
"""
import ee
import geemap
ee.Authenticate()
ee.Initialize(project='tu-proyecto-gcp')
# Configuration
path = 2
row = 52
year = 2025
# Initial scene-level cloud filter.
# A relatively permissive threshold is useful because the
# best scene will later be selected independently for each month.
max_cloud_cover = 70
collections = [
'LANDSAT/LC08/C02/T1_L2',
'LANDSAT/LC09/C02/T1_L2',
]
input_bands = ['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7']
output_bands = ['blue', 'green', 'red', 'nir', 'swir1', 'swir2']
def prepare_image(image):
"""
Applies Collection 2 Level 2 surface reflectance scaling
and masks invalid pixels using QA_PIXEL and QA_RADSAT.
Masked conditions: fill, dilated cloud, cirrus, cloud,
cloud shadow, snow, radiometric saturation or terrain occlusion.
"""
qa = image.select('QA_PIXEL')
clear_mask = (
qa.bitwiseAnd(1 << 0).eq(0)
.And(qa.bitwiseAnd(1 << 1).eq(0))
.And(qa.bitwiseAnd(1 << 2).eq(0))
.And(qa.bitwiseAnd(1 << 3).eq(0))
.And(qa.bitwiseAnd(1 << 4).eq(0))
.And(qa.bitwiseAnd(1 << 5).eq(0))
)
saturation_mask = image.select('QA_RADSAT').eq(0)
reflectance = (
image.select(input_bands, output_bands)
.multiply(0.0000275)
.add(-0.2)
.updateMask(clear_mask)
.updateMask(saturation_mask)
)
# copyProperties returns an ee.Element: cast it back to ee.Image
return ee.Image(
reflectance.copyProperties(
image,
[
'system:time_start',
'CLOUD_COVER',
'CLOUD_COVER_LAND',
'WRS_PATH',
'WRS_ROW',
'SPACECRAFT_ID',
'LANDSAT_PRODUCT_ID',
],
)
).set('image_id', image.id())
def get_collection(collection_ids, start_date, end_date):
"""Loads all candidate Landsat scenes for the WRS-2 path/row and year."""
collection = ee.ImageCollection([])
for collection_id in collection_ids:
landsat = (
ee.ImageCollection(collection_id)
.filter(ee.Filter.eq('WRS_PATH', path))
.filter(ee.Filter.eq('WRS_ROW', row))
.filterDate(start_date, end_date)
.filter(ee.Filter.lte('CLOUD_COVER', max_cloud_cover))
)
collection = collection.merge(landsat)
return collection.sort('system:time_start')
def select_best_monthly_images(collection):
"""
For each month, selects the candidate scene with the lowest
CLOUD_COVER. Only months with images are visited.
"""
with_month = collection.map(
lambda image: image.set(
'selection_month',
ee.Date(image.get('system:time_start')).get('month'),
)
)
months = with_month.aggregate_array('selection_month').distinct()
images = months.map(
lambda month: with_month
.filter(ee.Filter.eq('selection_month', month))
.sort('CLOUD_COVER')
.first()
)
return ee.ImageCollection.fromImages(images).sort('system:time_start')
# Dates
start_date = ee.Date.fromYMD(year, 1, 1)
end_date = start_date.advance(1, 'year') # filterDate excludes the end date
# Candidate Landsat images
raw_collection = get_collection(collections, start_date, end_date)
print('Available image count:', raw_collection.size().getInfo())
print('Available images:', raw_collection.aggregate_array('LANDSAT_PRODUCT_ID').getInfo())
print('Available CLOUD_COVER:', raw_collection.aggregate_array('CLOUD_COVER').getInfo())
# Automatic monthly selection
selected_raw = select_best_monthly_images(raw_collection)
print('Automatically selected image count:', selected_raw.size().getInfo())
print('Automatically selected images:', selected_raw.aggregate_array('LANDSAT_PRODUCT_ID').getInfo())
print('Selected months:', selected_raw.aggregate_array('selection_month').getInfo())
print('Selected CLOUD_COVER:', selected_raw.aggregate_array('CLOUD_COVER').getInfo())
# Pixel-level QA masking, only on the selected monthly scenes
selection = selected_raw.map(prepare_image).sort('system:time_start')
# Scene geometry and projection.
# A median composite has no native projection: Earth Engine gives it
# WGS84 at 1 degree. The UTM projection of the scene is kept so the
# export does not fall back to EPSG:4326.
first_image = ee.Image(raw_collection.first())
zone = first_image.geometry()
projection = first_image.select('SR_B4').projection()
# Annual median mosaic
mosaic = selection.median().setDefaultProjection(projection).clip(zone)
# Cloud-free observations per pixel (0 where none is left)
observations = (
selection.select('red')
.count()
.unmask(0)
.rename('observations')
.setDefaultProjection(projection)
.clip(zone)
)
no_observations = observations.eq(0).selfMask().rename('no_observations')
low_observations = observations.eq(1).selfMask().rename('low_observations')
# Export: reflectance x 10000 as signed 16-bit integers. No CRS is
# hardcoded: the export takes the CRS of the first band (the scene's UTM)
task_mosaic = ee.batch.Export.image.toDrive(
image=mosaic.multiply(10000).round().toInt16(),
description=f'landsat_monthly_mosaic_{path}_{row}_{year}',
folder='earthengine',
region=zone,
scale=30,
maxPixels=1e10,
)
task_mosaic.start()
# Optional quality layer: cloud-free observations per pixel
task_observations = ee.batch.Export.image.toDrive(
image=observations.toUint8(),
description=f'landsat_observations_{path}_{row}_{year}',
folder='earthengine',
region=zone,
scale=30,
maxPixels=1e10,
)
task_observations.start()
# Map
vis_true_color = {'bands': ['red', 'green', 'blue'], 'min': 0, 'max': 0.25}
vis_false_color = {'bands': ['swir1', 'nir', 'red'], 'min': [0, 0, 0], 'max': [0.35, 0.45, 0.20]}
vis_observations = {
'min': 1,
'max': 12,
'palette': ['d73027', 'fc8d59', 'fee08b', 'ffffbf', 'd9ef8b', '91cf60', '1a9850', '006837'],
}
mapa = geemap.Map()
mapa.add_basemap('SATELLITE')
mapa.centerObject(zone, 9)
mapa.addLayer(mosaic, vis_false_color, 'False color (SWIR1, NIR, red)', True)
mapa.addLayer(mosaic, vis_true_color, 'True color', False)
mapa.addLayer(observations, vis_observations, 'Cloud-free observations', False)
mapa.addLayer(low_observations, {'palette': ['ff9900']}, 'Only one valid observation', False)
mapa.addLayer(no_observations, {'palette': ['ff0000']}, 'Pixels without observations', False)
mapaUn mosaico de Margarita con 12 de las 41 imágenes de 2025
En 2025 hubo 41 imágenes de Landsat 8 y 9 de la escena 002/052 con un 70 % de nubes o menos. Todos los meses tuvieron al menos una, así que el script eligió 12: la más despejada de cada mes. El mosaico cubre toda la isla de Margarita sin huecos visibles. En falso color, el suelo seco de las tierras bajas y de la península de Macanao sale en rosado, y la vegetación de los cerros, en verde. También salen en verde los manglares de la laguna de La Restinga, en el istmo que une las dos partes de la isla.

| Escena | Path 2, row 52 (isla de Margarita) |
|---|---|
| Periodo | 1 ene – 31 dic 2025 |
| Nubes por escena | Hasta 70 % (filtro inicial) |
| Imágenes disponibles | 41, de Landsat 8 y 9 |
| Imágenes en el mosaico | 12, la más despejada de cada mes |
Cómo leer el mosaico en falso color
La combinación infrarrojo de onda corta 1 (SWIR1), infrarrojo cercano (NIR) y rojo es la misma de Landsat Explorer. Separa coberturas que en color real se confunden:
| Color | Suele ser |
|---|---|
| Verde intenso | Vegetación densa y activa |
| Rosado o magenta, más claro cuanto más seco | Vegetación seca o escasa, suelo desnudo y arena |
| Azul oscuro o negro | Agua profunda o clara |
| Azul claro | Agua poco profunda o con sedimentos |
Los tonos exactos dependen del contraste (los valores min y max de cada capa). Lo que se mantiene es el reparto: qué coberturas se separan y cuáles se parecen.
Si necesitas mosaicos de varios años o de un territorio completo, listos para tu SIG, mira el servicio de mapas satelitales.
Lo que la IA suele hacer mal
Si le pides este script a un asistente de IA, estos fallos aparecen a menudo:
- Copia la máscara de nubes de la Colección 1. Los códigos viejos usan los bits 3 y 5 de
pixel_qa. En la Colección 2, el bit 3 deQA_PIXELes nube y el 5 es nieve: las sombras, que están en el bit 4, se quedan en el mosaico. - Escala la reflectancia sin el desplazamiento. Multiplica por 0,0000275 pero no resta 0,2. Los valores quedan desplazados y los índices calculados después salen mal.
- Pide el año con
filterDate(año-01-01, año-12-31). La fecha final no se incluye, así que las imágenes del 31 de diciembre quedan fuera. El fin correcto es el 1 de enero del año siguiente. - Exporta el mosaico sin
crsy da por hecho que sale en UTM. Un mosaico hecho conmedian()tiene WGS84 a 1° como proyección por defecto, y la exportación toma el CRS de la primera banda: el archivo sale en EPSG:4326. Hay que indicar elcrso copiar la proyección de la escena consetDefaultProjection. - Combina Landsat 5 y 7 de después de 2003 sin revisar las franjas. Con pocas fechas, las líneas sin datos de Landsat 7 aparecen en el mosaico. La capa de observaciones las muestra.
Preguntas frecuentes
¿Qué son el path y el row de Landsat?
Son las coordenadas de la cuadrícula WRS-2 con la que Landsat organiza sus escenas: el path es la órbita y el row, la fila a lo largo de ella. Cada combinación cubre siempre la misma zona, por ejemplo la 006/052 en el norte de Falcón, Venezuela.
¿Cómo se quitan las nubes en Landsat Colección 2?
Con la banda QA_PIXEL: se descartan los píxeles con los bits 1 (nube dilatada), 2 (cirros, solo en Landsat 8 y 9), 3 (nube) o 4 (sombra de nube) activos. El bit 0 marca el relleno sin datos y el 5, la nieve. Para elegir las imágenes se usa el porcentaje de nubes de la escena, CLOUD_COVER.
¿Se pueden combinar Landsat 8 y Landsat 9 en un mismo mosaico?
Sí. Usan los mismos nombres de bandas, la misma escala de reflectancia y la misma banda de calidad en la Colección 2, así que basta con unir las dos colecciones antes de calcular la mediana.
¿Por qué usar la mediana y no el promedio?
Porque la mediana ignora los valores extremos, como un resto de nube o de sombra que la máscara no detectó. El promedio los mezcla con el resto de las fechas y deja manchas en el mosaico.
Fuentes
- USGS Landsat 8 Level 2, Collection 2, Tier 1Google Earth Engine, catálogo de datosBandas, escala de la reflectancia y significado de cada bit de
QA_PIXELyQA_RADSAT. - USGS Landsat 9 Level 2, Collection 2, Tier 1Google Earth Engine, catálogo de datosLas mismas bandas y escala que Landsat 8.
- USGS Landsat 5 Level 2, Collection 2, Tier 1Google Earth Engine, catálogo de datosBandas de Landsat 4, 5 y 7, y el bit 2 sin uso.
- Landsat 7U.S. Geological Survey (USGS)Fallo del corrector de líneas el 31 de mayo de 2003 y los datos que se pierden.
- ee.ImageCollection.filterDateGoogle Earth Engine, referencia de la APILa fecha final del filtro no se incluye.
- Landsat AlgorithmsGoogle Earth Engine, documentaciónCómo trabaja Earth Engine con las colecciones Landsat.
- ProjectionsGoogle Earth Engine, documentaciónProyección por defecto de un mosaico (WGS84 a 1°) y
setDefaultProjection. - Exporting ImagesGoogle Earth Engine, documentaciónSin
crs, la exportación toma el CRS de la primera banda.
Sigue aprendiendo
Vegetación
Qué es el NDVI: cómo se calcula y cómo interpretar sus valoresVegetación · NDVI
Serie temporal de NDVI y fenología de cultivos en Google Earth EngineErrores · Optimización
User memory limit exceeded en Google Earth Engine
Mira esta técnica aplicada en Landsat Explorer, la aplicación de mosaicos Landsat de Venezuela. ¿Lo necesitas para tu zona? Mira el servicio Aplicaciones web para explorar resultados.
¿Necesitas mosaicos de varios años o un proceso que los genere solo?
Escribe a contacto@ventuari.dev con la zona y el periodo, y recibirás una propuesta con alcance y precio.