Visualización de ImageCollection

Las imágenes que componen un ImageCollection se pueden visualizar como una animación o una serie de miniaturas, a las que se denomina “tira de película”. Estos métodos proporcionan una evaluación rápida del contenido de un ImageCollection y un medio eficaz para observar el cambio espaciotemporal (Figura 1).

En las siguientes secciones, se describe cómo preparar un ImageCollection para la visualización, se proporciona código de ejemplo para cada método de visualización de colecciones y se abordan varias técnicas de animación avanzadas.


Figura 1: Animación que muestra la progresión de tres días de los huracanes del Atlántico en septiembre de 2017.

Preparación de la colección

Filtra, combina, ordena y aplica diseño a las imágenes de una colección para mostrar solo aquellas que te interesan o enfatizar un fenómeno. Se puede proporcionar cualquier ImageCollection como entrada a las funciones de visualización, pero una colección seleccionada con consideración de los períodos interanuales y dentro de un año, el intervalo de observación, la extensión regional, la calidad y la representación puede lograr mejores resultados.

Filtros

Filtra una colección de imágenes para incluir solo los datos relevantes que respaldan el propósito de la visualización. Ten en cuenta las fechas, la extensión espacial, la calidad y otras propiedades específicas de un conjunto de datos determinado.

Por ejemplo, filtra una colección de reflectancia superficial de Sentinel-2 por lo siguiente:

un solo período

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterDate('2018-01-01', '2019-01-01');

un rango de días del año en serie,

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filter(ee.Filter.calendarRange(171, 242, 'day_of_year'));

una región de interés

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2));

o una propiedad de imagen.

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50));

Encadena varios filtros.

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterDate('2018-01-01', '2019-01-01')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2))
  .filter('CLOUDY_PIXEL_PERCENTAGE < 50');

Composiciones

Combina períodos intraanuales e interanuales para reducir la cantidad de imágenes en una colección y mejorar la calidad. Por ejemplo, supongamos que quieres crear una visualización del NDVI anual para África. Una opción es simplemente filtrar una recopilación de NDVI de 16 días de MODIS para incluir todas las observaciones de 2018.

var ndviCol = ee.ImageCollection('MODIS/006/MOD13A2')
  .filterDate('2018-01-01', '2019-01-01')
  .select('NDVI');

Compuesto interanual por filtro y reducción

La visualización de la colección anterior muestra un ruido considerable en las regiones boscosas donde la cobertura de nubes es alta (Figura 2a). Se puede lograr una mejor representación si se reducen los períodos de datos de serie por mediana en todos los años de la colección MODIS.

// Make a day-of-year sequence from 1 to 365 with a 16-day step.
var doyList = ee.List.sequence(1, 365, 16);

// Import a MODIS NDVI collection.
var ndviCol = ee.ImageCollection('MODIS/006/MOD13A2').select('NDVI');

// Map over the list of days to build a list of image composites.
var ndviCompList = doyList.map(function(startDoy) {
  // Ensure that startDoy is a number.
  startDoy = ee.Number(startDoy);

  // Filter images by date range; starting with the current startDate and
  // ending 15 days later. Reduce the resulting image collection by median.
  return ndviCol
    .filter(ee.Filter.calendarRange(startDoy, startDoy.add(15), 'day_of_year'))
    .reduce(ee.Reducer.median());
});

// Convert the image List to an ImageCollection.
var ndviCompCol = ee.ImageCollection.fromImages(ndviCompList);

La animación resultante de esta colección tiene menos ruido, ya que cada imagen representa la mediana de un compuesto de NDVI de 16 días durante más de 20 años de datos (Figura 1b). Consulta este instructivo para obtener más información sobre esta animación.

Figura 2a. NDVI anual sin compaginación interanual. Figura 2b. NDVI anual con compaginación interanual

Composición intraanual por filtro y reducción

En el ejemplo anterior, se aplica la composición interanual. También puede ser útil combinar una serie de observaciones intraanuales. Por ejemplo, los datos de Landsat se recopilan cada dieciséis días para una escena determinada por sensor, pero a menudo las nubes ocultan parte de las imágenes. Enmascarar las nubes y componer varias imágenes de la misma temporada puede producir una representación más libre de nubes. Considera el siguiente ejemplo en el que las imágenes de Landsat 5 de julio y agosto se combinan con la mediana de cada año desde 1985 hasta 2011.

// Assemble a collection of Landsat surface reflectance images for a given
// region and day-of-year range.
var lsCol = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
  .filterBounds(ee.Geometry.Point(-122.9, 43.6))
  .filter(ee.Filter.dayOfYear(182, 243))
  // Add the observation year as a property to each image.
  .map(function(img) {
    return img.set('year', ee.Image(img).date().get('year'));
  });


// Define a function to scale the data and mask unwanted pixels.
function maskL457sr(image) {
  // Bit 0 - Fill
  // Bit 1 - Dilated Cloud
  // Bit 2 - Unused
  // Bit 3 - Cloud
  // Bit 4 - Cloud Shadow
  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
  var saturationMask = image.select('QA_RADSAT').eq(0);

  // Apply the scaling factors to the appropriate bands.
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);

  // Replace the original bands with the scaled ones and apply the masks.
  return image.addBands(opticalBands, null, true)
      .addBands(thermalBand, null, true)
      .updateMask(qaMask)
      .updateMask(saturationMask);
}

// Define a list of unique observation years from the image collection.
var years = ee.List(lsCol.aggregate_array('year')).distinct().sort();

// Map over the list of years to build a list of annual image composites.
var lsCompList = years.map(function(year) {
  return lsCol
    // Filter image collection by year.
    .filterMetadata('year', 'equals', year)
    // Apply cloud mask.
    .map(maskL457sr)
    // Reduce image collection by median.
    .reduce(ee.Reducer.median())
    // Set composite year as an image property.
    .set('year', year);
});

// Convert the image List to an ImageCollection.
var lsCompCol = ee.ImageCollection.fromImages(lsCompList);

Compuesto intraanual por unión y reducción

Ten en cuenta que los dos métodos de composición anteriores se asignan a un List de días y años para definir de forma incremental fechas nuevas para filtrar y combinar. Aplicar una unión es otro método para lograr esta operación. En el siguiente fragmento, se define una colección de años única y, luego, se aplica una unión saveAll para identificar todas las imágenes que corresponden a un año determinado. Las imágenes que pertenecen a un año determinado se agrupan en un objeto List, que se almacena como una propiedad del representante del año correspondiente en la colección de años distintos. Los compuestos anuales se generan a partir de estas listas reduciendo ImageCollections que definen en una función asignada a la colección de años distinta.

// Assemble a collection of Landsat surface reflectance images for a given
// region and day-of-year range.
var lsCol = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
  .filterBounds(ee.Geometry.Point(-122.9, 43.6))
  .filter(ee.Filter.dayOfYear(182, 243))
  // Add the observation year as a property to each image.
  .map(function(img) {
    return img.set('year', ee.Image(img).date().get('year'));
  });

// Make a distinct year collection; one image representative per year.
var distinctYears = lsCol.distinct('year').sort('year');

// Define a join filter; one-to-many join on ‘year’ property.
var filter = ee.Filter.equals({leftField: 'year', rightField: 'year'});

// Define a join.
var join = ee.Join.saveAll('year_match');

// Apply the join; results in 'year_match' property being added to each distinct
// year representative image. The list includes all images in the collection
// belonging to the respective year.
var joinCol = join.apply(distinctYears, lsCol, filter);

// Define a function to scale the data and mask unwanted pixels.
function maskL457sr(image) {
  // Bit 0 - Fill
  // Bit 1 - Dilated Cloud
  // Bit 2 - Unused
  // Bit 3 - Cloud
  // Bit 4 - Cloud Shadow
  var qaMask = image.select('QA_PIXEL').bitwiseAnd(parseInt('11111', 2)).eq(0);
  var saturationMask = image.select('QA_RADSAT').eq(0);

  // Apply the scaling factors to the appropriate bands.
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);

  // Replace the original bands with the scaled ones and apply the masks.
  return image.addBands(opticalBands, null, true)
      .addBands(thermalBand, null, true)
      .updateMask(qaMask)
      .updateMask(saturationMask);
}

// Map over the distinct years collection to build a list of annual image
// composites.
var lsCompList = joinCol.map(function(img) {
  // Get the list of images belonging to the given year.
  return ee.ImageCollection.fromImages(img.get('year_match'))
    // Apply cloud mask.
    .map(maskL457sr)
    // Reduce image collection by median.
    .reduce(ee.Reducer.median())
    // Set composite year as an image property.
    .copyProperties(img, ['year']);
});

// Convert the image List to an ImageCollection.
var lsCompCol = ee.ImageCollection(lsCompList);

Compuesto del mismo día por unión y reducción

Un caso adicional para la composición es crear mosaicos de imágenes contiguas espacialmente. Supongamos que tu región de interés abarca dos filas de Landsat dentro de la misma ruta y tu objetivo es mostrar un mosaico de imágenes de las dos imágenes para cada órbita de Landsat 8 en 2017 y 2018. Aquí, después de filtrar la colección por ruta y fila, se usa una operación de combinación para crear una imagen en mosaico de las imágenes de Landsat del mismo obit, definido por la fecha de adquisición.

var lsCol = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
  .filterDate('2017-01-01', '2019-01-01')
  .filter('WRS_PATH == 38 && (WRS_ROW == 28 || WRS_ROW == 29)')
  .map(function(img) {
    var date = img.date().format('YYYY-MM-dd');
    return img.set('date', date);
  });

var distinctDates = lsCol.distinct('date').sort('date');
var filter = ee.Filter.equals({leftField: 'date', rightField: 'date'});
var join = ee.Join.saveAll('date_match');
var joinCol = join.apply(distinctDates, lsCol, filter);

var lsColMos = ee.ImageCollection(joinCol.map(function(col) {
  return ee.ImageCollection.fromImages(col.get('date_match')).mosaic();
}));

Ordenar

Ordena una colección por tiempo para garantizar una secuencia cronológica adecuada o ordénala según una propiedad que elijas. De forma predeterminada, la serie de marcos de visualización se ordena en el orden natural de la colección. La disposición de las series se puede alterar con el método de recopilación sort, en el que se selecciona una propiedad Image para ordenarla de forma ascendente o descendente. Por ejemplo, para ordenar por hora de observación, usa la propiedad system:time_start omnipresente.

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2))
  .sort('system:time_start');

O tal vez el orden se deba definir según el aumento de la nubosidad, como en este caso de las imágenes de Sentinel-2.

var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-122.1, 37.2))
  .sort('CLOUDY_PIXEL_PERCENTAGE');

El orden también se puede definir mediante una propiedad derivada, como el NDVI regional promedio. Aquí, el NDVI regional se agrega como una propiedad a cada imagen en una función asignada, seguida de una ordenación en la propiedad nueva.

// Define an area of interest geometry.
var aoi = ee.Geometry.Point(-122.1, 37.2).buffer(1e4);

// Filter MODIS NDVI image collection by a date range.
var ndviCol = ee.ImageCollection('MODIS/061/MOD13A1')
  .filterDate('2018-01-01', '2019-01-01')
  .select('NDVI')
  // Map over the image collection to calculate regional mean NDVI and add
  // the result to each image as a property.
  .map(function(img) {
    var meanNdvi = img.reduceRegion({
      reducer: ee.Reducer.mean(), geometry: aoi, scale: 500});
    return img.set('meanNdvi', meanNdvi.get('NDVI'));
  })
  // Sort the collection by descending regional mean NDVI.
  .sort('meanNdvi', false);

Visualización de imágenes

La visualización de imágenes transforma los números en colores. Existen tres maneras de controlar cómo se representan los datos de imagen como color en los métodos de visualización de colecciones:

  1. Proporciona argumentos de visualización directamente a getVideoThumbURL y getFilmstripThumbURL.
  2. Asigna el método de imagen visualize a la colección de imágenes antes de aplicar getVideoThumbURL y getFilmstripThumbURL.
  3. Asigna el método de imagen sldStyle a la colección de imágenes antes de aplicar getVideoThumbURL y getFilmstripThumbURL. Consulta Descriptor de capa con diseño para obtener más información.

En los ejemplos de esta guía, se usan las opciones 1 y 2, en las que se logra la visualización asignando tres bandas de imagen de una imagen multibanda a los canales de color rojo, verde y azul, o bien graduando los valores de una sola banda de forma lineal a lo largo de una paleta de colores. Entre los parámetros de visualización, se incluyen los siguientes:

Parámetros de visualización
Parámetro Descripción Tipo
bandas Lista separada por comas de tres nombres de bandas que se asignarán a RGB list
min Valores que se asignarán a 0 número o lista de tres números, uno para cada banda
max Valores que se asignarán a 255 número o lista de tres números, uno para cada banda
ganar Valores por los que se debe multiplicar cada valor de píxel número o lista de tres números, uno para cada banda
sesgo Valores que se agregarán a cada DN número o lista de tres números, uno para cada banda
gamma Factores de corrección gamma número o lista de tres números, uno para cada banda
palette Lista de cadenas de colores de estilo CSS (solo imágenes de una sola banda) Lista de cadenas hexadecimales separadas por comas
opacidad Es la opacidad de la capa (0.0 es completamente transparente y 1.0 es completamente opaco). número

Usa el argumento bands para seleccionar las bandas que deseas visualizar. Proporciona una lista de uno o tres nombres de bandas. En el caso de las imágenes multibanda, las primeras tres bandas se seleccionan de forma predeterminada. El orden de los nombres de las bandas determina la asignación de colores. La primera, segunda y tercera banda enumeradas se asignan a rojo, verde y azul, respectivamente.

La escala del rango de datos es una consideración importante cuando se visualizan imágenes. De forma predeterminada, los valores de datos de punto flotante entre 0 y 1 (inclusive) se escalan entre 0 y 255 (inclusive). Los valores fuera de este rango se establecen de manera forzosa en 0 y 255, según si son menores que 0 o mayores que 1, respectivamente. En el caso de los datos de números enteros, la capacidad completa definida por su tipo se ajusta entre 0 y 255 (p.ej., los datos de 16 bits firmados tienen un rango de -32,768 a 32,767, que se ajusta a [0, 255] de forma predeterminada). A menudo, aceptar los valores predeterminados puede generar visualizaciones con poco o ningún contraste entre las características de la imagen. Usa min y max para mejorar el contraste y enfatizar un rango de datos en particular. Una buena regla general es establecer min y max en valores que representen el 2º y el 98º percentil de los datos dentro de tu área de interés. Consulta el siguiente ejemplo de cómo calcular estos valores para un modelo de elevación digital.

// Import SRTM global elevation model.
var demImg = ee.Image('USGS/SRTMGL1_003');

// Define a rectangular area of interest.
var aoi = ee.Geometry.Polygon(
  [[
    [-103.84153083119054, 49.083004219142886],
    [-103.84153083119054, 25.06838270664608],
    [-85.64817145619054, 25.06838270664608],
    [-85.64817145619054, 49.083004219142886]
  ]],
  null, false);

// Calculate the 2nd and 98th percentile elevation values from rescaled (to
// 500m) pixels intersecting the area of interest. A Dictionary is returned.
var percentClip = demImg.reduceRegion({
  reducer: ee.Reducer.percentile([2, 98]),
  geometry: aoi,
  scale: 500,
  maxPixels: 3e7
});

// Print the regional 2nd and 98th percentile elevation values. Get the
// dictionary keys and use them to get the values for each percentile summary.
var keys = percentClip.keys();
print('Set vis min to:', ee.Number(percentClip.get(keys.get(0))).round());
print('Set vis max to:', ee.Number(percentClip.get(keys.get(1))).round());

El parámetro palette define los colores que representan la imagen de visualización de 8 bits. Solo se aplica a las representaciones de una sola banda. Si lo especificas con una imagen de varias bandas, se produce un error. Si los datos son de una sola banda o deseas visualizar una sola banda de una imagen de varias bandas, establece el parámetro forceRgbOutput en true (no es necesario si se proporciona el argumento palette). Usa los parámetros min y max para definir el rango de valores que se escalarán de forma lineal entre 0 y 255.

A continuación, se muestra un ejemplo de cómo asignar una función de visualización a una colección de imágenes de una sola banda. Se importa una colección de NDVI de MODIS, se establecen los argumentos de visualización para el método visualization y se asigna una función que transforma los valores en representaciones de imágenes RGB sobre la colección de NDVI.

// Filter MODIS NDVI image collection by a date range.
var ndviCol = ee.ImageCollection('MODIS/061/MOD13A1')
  .filterDate('2018-01-01', '2019-01-01')
  .select('NDVI');

// Define visualization arguments.
var visArgs = {
  min: 0,
  max: 9000,
  palette: [
    'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901',
    '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01',
    '012E01', '011D01', '011301'
  ]
};

// Define a function to convert an image to an RGB visualization image and copy
// properties from the original image to the RGB image.
var visFun = function(img) {
  return img.visualize(visArgs).copyProperties(img, img.propertyNames());
};

// Map over the image collection to convert each image to an RGB visualization
// using the previously defined visualization function.
var ndviColVis = ndviCol.map(visFun);

Este es un ejemplo de cómo asignar una función de visualización a una colección de imágenes de varias bandas:

// Assemble a collection of Sentinel-2 surface reflectance images for a given
// region and date range.
var s2col = ee.ImageCollection('COPERNICUS/S2_SR')
  .filterBounds(ee.Geometry.Point(-96.9037, 48.0395))
  .filterDate('2019-06-01', '2019-10-01');

// Define visualization arguments.
var visArgs = {bands: ['B11', 'B8', 'B3'], min: 300, max: 3500};

// Define a function to convert an image to an RGB visualization image and copy
// properties from the original image to the RGB image.
var visFun = function(img) {
  return img.visualize(visArgs).copyProperties(img, img.propertyNames());
};

// Map over the image collection to convert each image to an RGB visualization
// using the previously defined visualization function.
var s2colVis = s2col.map(visFun);

En este caso, no se proporciona ningún argumento de paleta porque se proporcionan tres bandas, que definen la intensidad de cada capa RGB. Ten en cuenta que ambos ejemplos usan los parámetros min y max para controlar qué valores se extienden hasta los límites de los datos RGB de 8 bits.

Miniatura de video

La función getVideoThumbURL() genera una animación a partir de todas las imágenes de un ImageCollection, en el que cada imagen representa un fotograma. El flujo de trabajo general para producir una animación es el siguiente:

  1. Define un Geometry cuyos límites determinan el alcance regional de la animación.
  2. Define un ImageCollection.
  3. Considera la visualización de imágenes: asigna una función de visualización de imágenes a la colección o agrega argumentos de visualización de imágenes al conjunto de argumentos de animación.
  4. Define los argumentos de animación y llama al método getVideoThumbURL.

El resultado de getVideoThumbURL es una URL. Imprime la URL en la consola y haz clic en ella para iniciar los servidores de Earth Engine que generan la animación sobre la marcha en una nueva pestaña del navegador. Como alternativa, llama a la función ui.Thumbnail en la colección y sus argumentos de animación correspondientes para ver la animación en la consola del Editor de código. Después de la renderización, la animación estará disponible para descargarla. Para ello, haz clic con el botón derecho en ella y selecciona las opciones adecuadas en el menú contextual.

En el siguiente ejemplo, se muestra cómo generar una animación que representa las temperaturas globales durante 24 horas. Ten en cuenta que este ejemplo incluye argumentos de visualización junto con argumentos de animación, en lugar de asignar primero una función de visualización sobre ImageCollection. Cuando ejecutes esta secuencia de comandos, debería aparecer una animación similar a la figura 3 en la consola del editor de código.

// Define an area of interest geometry with a global non-polar extent.
var aoi = ee.Geometry.Polygon(
  [[[-179.0, 78.0], [-179.0, -58.0], [179.0, -58.0], [179.0, 78.0]]], null,
  false);

// Import hourly predicted temperature image collection for northern winter
// solstice. Note that predictions extend for 384 hours; limit the collection
// to the first 24 hours.
var tempCol = ee.ImageCollection('NOAA/GFS0P25')
  .filterDate('2018-12-22', '2018-12-23')
  .limit(24)
  .select('temperature_2m_above_ground');

// Define arguments for animation function parameters.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857',
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Print the animation to the console as a ui.Thumbnail using the above defined
// arguments. Note that ui.Thumbnail produces an animation when the first input
// is an ee.ImageCollection instead of an ee.Image.
print(ui.Thumbnail(tempCol, videoArgs));

// Alternatively, print a URL that will produce the animation when accessed.
print(tempCol.getVideoThumbURL(videoArgs));


Figura 3: Temperatura superficial por hora del solsticio de invierno del norte representada como una imagen GIF animada.

Tira de película

La función getFilmstripThumbUrl genera una sola imagen estática que representa la concatenación de todas las imágenes de un ImageCollection en una serie norte-sur. La secuencia de fotogramas de la película sigue el orden natural de la colección.

El resultado de getFilmstripThumbUrl es una URL. Imprime la URL en la consola y haz clic en ella para iniciar los servidores de Earth Engine que generan la imagen sobre la marcha en una nueva pestaña del navegador. Después de la renderización, la imagen está disponible para descargarla. Para ello, haz clic con el botón derecho en ella y selecciona las opciones adecuadas en el menú contextual.

En el siguiente fragmento de código, se usa la misma colección que en el ejemplo de miniatura de video anterior. Cuando ejecutes esta secuencia de comandos, debería aparecer una tira de película similar a la Figura 4 en la consola del editor de código.

// Define an area of interest geometry with a global non-polar extent.
var aoi = ee.Geometry.Polygon(
  [[[-179.0, 78.0], [-179.0, -58.0], [179.0, -58.0], [179.0, 78.0]]], null,
  false);

// Import hourly predicted temperature image collection for northern winter
// solstice. Note that predictions extend for 384 hours; limit the collection
// to the first 24 hours.
var tempCol = ee.ImageCollection('NOAA/GFS0P25')
  .filterDate('2018-12-22', '2018-12-23')
  .limit(24)
  .select('temperature_2m_above_ground');

// Define arguments for the getFilmstripThumbURL function parameters.
var filmArgs = {
  dimensions: 128,
  region: aoi,
  crs: 'EPSG:3857',
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Print a URL that will produce the filmstrip when accessed.
print(tempCol.getFilmstripThumbURL(filmArgs));


Figura 4: Temperatura superficial por hora del solsticio de invierno del norte representada como una imagen PNG de tira de película. Ten en cuenta que la tira de película se dividió en cuatro secciones para su visualización. El resultado de getFilmstripThumbURL es una sola serie de imágenes de la colección unidas de norte a sur.

Técnicas avanzadas

En las siguientes secciones, se describe cómo usar el recorte, la opacidad y la composición de capas para mejorar las visualizaciones agregando bordes de polígonos, enfatizando regiones de interés y comparando imágenes dentro de una colección.

Ten en cuenta que todos los siguientes ejemplos de esta sección usan la misma base ImageCollection definida aquí:

// Import hourly predicted temperature image collection for northern winter
// solstice. Note that predictions extend for 384 hours; limit the collection
// to the first 24 hours.
var tempCol = ee.ImageCollection('NOAA/GFS0P25')
  .filterDate('2018-12-22', '2018-12-23')
  .limit(24)
  .select('temperature_2m_above_ground');

// Define visualization arguments to control the stretch and color gradient
// of the data.
var visArgs = {
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Convert each image to an RGB visualization image by mapping the visualize
// function over the image collection using the arguments defined previously.
var tempColVis = tempCol.map(function(img) {
  return img.visualize(visArgs);
});

// Import country features and filter to South American countries.
var southAmCol = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017')
  .filterMetadata('wld_rgn', 'equals', 'South America');

// Define animation region (South America with buffer).
var southAmAoi = ee.Geometry.Rectangle({
  coords: [-103.6, -58.8, -18.4, 17.4], geodesic: false});

Superposiciones

Se pueden superponer varias imágenes con el método Image de blend, en el que se combinan los píxeles superpuestos de dos imágenes según sus máscaras (opacidad).

Superposición de vectores

Agregar polígonos de límites administrativos y otras geometrías a una imagen puede proporcionar un contexto espacial valioso. Considera la animación de la temperatura superficial mundial diaria anterior (Figura 3). Los límites entre la tierra y el océano se pueden distinguir, pero se pueden hacer explícitos si se agrega una superposición de polígonos de países.

Los datos vectoriales (Features) se dibujan en las imágenes aplicando el método paint. Los componentes se pueden pintar en una imagen existente, pero la práctica recomendada es pintarlos en una imagen en blanco, aplicarles un estilo y, luego, combinar el resultado con otras capas de imagen con estilo. Tratar cada capa de una pila de visualización de forma independiente permite tener más control sobre el diseño.

En el siguiente ejemplo, se muestra cómo pintar los bordes de los países de Sudamérica en un Image en blanco y combinar el resultado con cada Image de la colección global de temperaturas diarias (Figura 5). Los límites de países superpuestos distinguen la tierra del agua y proporcionan contexto a los patrones de temperatura.

// Define an empty image to paint features to.
var empty = ee.Image().byte();

// Paint country feature edges to the empty image.
var southAmOutline = empty
  .paint({featureCollection: southAmCol, color: 1, width: 1})
  // Convert to an RGB visualization image; set line color to black.
  .visualize({palette: '000000'});

// Map a blend operation over the temperature collection to overlay the country
// border outline image on all collection images.
var tempColOutline = tempColVis.map(function(img) {
  return img.blend(southAmOutline);
});

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: southAmAoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(tempColOutline, videoArgs));


Figura 5: Agrega superposiciones de vectores a las imágenes de una colección para proporcionar contexto espacial.

Superposición de imágenes

Se pueden superponer varias imágenes para lograr el estilo deseado. Supongamos que quieres enfatizar una región de interés. Puedes crear una copia atenuada de una visualización de imagen como capa base y, luego, superponer una versión recortada de la visualización original. En función del ejemplo anterior, la siguiente secuencia de comandos genera la Figura 6.

// Define an empty image to paint features to.
var empty = ee.Image().byte();

// Paint country feature edges to the empty image.
var southAmOutline = empty
  .paint({featureCollection: southAmCol, color: 1, width: 1})
  // Convert to an RGB visualization image; set line color to black.
  .visualize({palette: '000000'});

// Map a blend operation over the temperature collection to overlay the country
// border outline image on all collection images.
var tempColOutline = tempColVis.map(function(img) {
  return img.blend(southAmOutline);
});

// Define a partially opaque grey RGB image to dull the underlying image when
// blended as an overlay.
var dullLayer = ee.Image.constant(175).visualize({
  opacity: 0.6, min: 0, max: 255, forceRgbOutput: true});

// Map a two-part blending operation over the country outline temperature
// collection for final styling.
var finalVisCol = tempColOutline.map(function(img) {
  return img
    // Blend the dulling layer with the given country outline temperature image.
    .blend(dullLayer)
    // Blend a clipped copy of the country outline temperature image with the
    // dulled background image.
    .blend(img.clipToCollection(southAmCol));
});

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: southAmAoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(finalVisCol, videoArgs));


Figura 6. Para enfatizar un área de interés, recorta la imagen y superpónla en una copia atenuada.

También puedes combinar los datos de imagen con una capa base de sombreado de colinas para indicar el terreno y darle profundidad a la visualización (Figura 7).

// Define a hillshade layer from SRTM digital elevation model.
var hillshade = ee.Terrain.hillshade(ee.Image('USGS/SRTMGL1_003')
  // Exaggerate the elevation to increase contrast in hillshade.
  .multiply(100))
  // Clip the DEM by South American boundary to clean boundary between
  // land and ocean.
  .clipToCollection(southAmCol);

// Map a blend operation over the temperature collection to overlay a partially
// opaque temperature layer on the hillshade layer.
var finalVisCol = tempColVis.map(function(img) {
  return hillshade
    .blend(img.clipToCollection(southAmCol).visualize({opacity: 0.6}));
});

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: southAmAoi,
  framesPerSecond: 7,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(finalVisCol, videoArgs));


Figura 7. Superpone datos de imágenes parcialmente transparentes en una capa de sombreado de colinas para mostrar el terreno.

Transiciones

Personaliza una colección de imágenes para producir animaciones que revelen las diferencias entre dos imágenes de una colección con transiciones de atenuación, parpadeo y deslizamiento. Cada uno de los siguientes ejemplos usa la misma visualización básica que genera la siguiente secuencia de comandos:

// Define an area of interest geometry with a global non-polar extent.
var aoi = ee.Geometry.Polygon(
  [[[-179.0, 78.0], [-179.0, -58.0], [179.0, -58.0], [179.0, 78.0]]], null,
  false);

// Import hourly predicted temperature image collection.
var temp = ee.ImageCollection('NOAA/GFS0P25')

// Define a northern summer solstice temperature image.
var summerSolTemp = temp
  .filterDate('2018-06-21', '2018-06-22')
  .filterMetadata('forecast_hours', 'equals', 12)
  .first()
  .select('temperature_2m_above_ground');

// Define a northern winter solstice temperature image.
var winterSolTemp = temp
  .filterDate('2018-12-22', '2018-12-23')
  .filterMetadata('forecast_hours', 'equals', 12)
  .first()
  .select('temperature_2m_above_ground');

// Combine the solstice images into a collection.
var tempCol = ee.ImageCollection([
  summerSolTemp.set('season', 'summer'),
  winterSolTemp.set('season', 'winter')
]);

// Import international boundaries feature collection.
var countries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Define visualization arguments.
var visArgs = {
  min: -40.0,
  max: 35.0,
  palette: ['blue', 'purple', 'cyan', 'green', 'yellow', 'red']
};

// Convert the image data to RGB visualization images.
// The clip and unmask combination sets ocean pixels to black.
var tempColVis = tempCol.map(function(img) {
  return img
    .visualize(visArgs)
    .clipToCollection(countries)
    .unmask(0)
    .copyProperties(img, img.propertyNames());
});

Flicker

Con solo dos imágenes en una colección, como es el caso aquí, el parpadeo es la representación predeterminada en la animación de la colección. Ajusta el argumento de animación framesPerSecond para acelerar o ralentizar la velocidad de parpadeo. Los siguientes argumentos de visualización aplicados a la colección anterior producen la Figura 8.

// Define arguments for animation function parameters.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 2,
  crs: 'EPSG:3857'
};

// Display animation to the Code Editor console.
print(ui.Thumbnail(tempColVis, videoArgs));


Figura 8. Ejemplo de parpadeo entre la temperatura superficial a las 12 p.m. (GMT) para el solsticio de invierno y el norte.

Atenuar

Para lograr una transición de atenuación entre dos capas, se disminuye simultáneamente la opacidad de una capa y se aumenta la opacidad de la otra en una secuencia de incrementos de opacidad de cerca de 0 a 1 (Figura 9).

// Define a sequence of decreasing opacity increments. Note that opacity cannot
// be 0, so near 1 and 0 are used. Near 1 is needed because a compliment is
// calculated in a following step that can result in 0 if 1 is an element of the
// list.
var opacityList = ee.List.sequence({start: 0.99999, end: 0.00001, count: 20});

// Filter the summer and winter solstice images from the collection and set as
// image objects.
var summerImg = tempColVis.filter(ee.Filter.eq('season', 'summer')).first();
var winterImg = tempColVis.filter(ee.Filter.eq('season', 'winter')).first();

// Map over the list of opacity increments to iteratively adjust the opacity of
// the two solstice images. Returns a list of images.
var imgList = opacityList.map(function(opacity) {
  var opacityCompliment = ee.Number(1).subtract(ee.Number(opacity));
  var winterImgFade = winterImg.visualize({opacity: opacity});
  var summerImgFade = summerImg.visualize({opacity: opacityCompliment});
  return summerImgFade.blend(winterImgFade).set('opacity', opacity);
});

// Convert the image list to an image collection; the forward phase.
var fadeForward = ee.ImageCollection.fromImages(imgList);

// Make a copy of the collection that is sorted by ascending opacity to
// represent the reverse phase.
var fadeBackward = fadeForward.sort({property: 'opacity'});

// Merge the forward and reverse phase frame collections.
var fadeCol = fadeForward.merge(fadeBackward);

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 25,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(fadeCol, videoArgs));


Figura 9. Ejemplo de atenuación entre la temperatura superficial a las 12 p.m. (GMT) del solsticio de verano y de invierno.

Control deslizante

Una transición de control deslizante muestra y oculta de forma progresiva la capa de imagen subyacente. Esto se logra ajustando iterativamente la opacidad de la imagen superpuesta en un rango de longitudes (Figura 10).

// Define a sequence of longitude increments. Start and end are defined by the
// min and max longitude of the feature to be provided to the region parameter
// of the animation arguments dictionary.
var lonSeq = ee.List.sequence({start: -179, end: 179, count: 20});

// Define a longitude image.
var longitude = ee.Image.pixelLonLat().select('longitude');

// Filter the summer and winter solstice images from the collection and set as
// image objects.
var summerImg = tempColVis.filter(ee.Filter.eq('season', 'summer')).first();
var winterImg = tempColVis.filter(ee.Filter.eq('season', 'winter')).first();

// Map over the list of longitude increments to iteratively adjust the mask
// (opacity) of the overlying image layer. Returns a list of images.
var imgList = lonSeq.map(function(lon) {
  lon = ee.Number(lon);
  var mask = longitude.gt(lon);
  return summerImg.blend(winterImg.updateMask(mask)).set('lon', lon);
});

// Convert the image list to an image collection; concealing phase.
var sliderColForward = ee.ImageCollection.fromImages(imgList);

// Make a copy of the collection that is sorted by descending longitude to
// represent the revealing phase.
var sliderColbackward = sliderColForward
  .sort({property: 'lon', ascending: false});

// Merge the forward and backward phase frame collections.
var sliderCol = sliderColForward.merge(sliderColbackward);

// Define animation arguments.
var videoArgs = {
  dimensions: 768,
  region: aoi,
  framesPerSecond: 25,
  crs: 'EPSG:3857'
};

// Display the animation.
print(ui.Thumbnail(sliderCol, videoArgs));


Figura 10. Ejemplo de una transición deslizante entre la temperatura superficial a las 12 p.m. (GMT) del solsticio de verano y de invierno.