Exportar a BigQuery

Descripción general

La arquitectura de procesamiento de Earth Engine está optimizada para que el procesamiento de imágenes (basado en píxeles) sea rápido y escalable. BigQuery también está optimizado para el procesamiento escalable de datos tabulares (vectores) y tiene muchas funciones que lo convierten en un buen complemento de Earth Engine.

Estos son algunos ejemplos de flujos de trabajo:

  • Cómo realizar grandes combinaciones de BigQuery en datos generados en Earth Engine
  • Anotación de datos vectoriales con estadísticas derivadas de imágenes para un procesamiento adicional en BigQuery
  • Exporta datos de Earth Engine de forma periódica a una tabla de BigQuery que se pueda agregar

Si tienes otros casos de uso interesantes, nos encantaría conocerlos.

Conceptos básicos de BigQuery

Earth Engine escribe en tablas de BigQuery, y todas las tablas se contienen en conjuntos de datos. Las tareas de exportación fallan si el conjunto de datos especificado no está presente en BigQuery. Obtén más información en la introducción al conjunto de datos de BigQuery.

Creación de conjuntos de datos

Los conjuntos de datos tienen varias opciones de tiempo de creación, como el nombre, la región de almacenamiento y el comportamiento de vencimiento (junto con varias otras opciones más avanzadas).

Existen varios mecanismos para crear conjuntos de datos, pero una manera simple de comenzar es a través de la consola de Cloud:

  1. Navega a la página de BigQuery en la consola de Cloud.
  2. Haz clic en “Habilitar” para habilitar la API, si se te solicita.
  3. En la pestaña "Espacio de trabajo de SQL", haz clic en el menú de tres puntos () junto al proyecto.
  4. Elige la opción “Crear conjunto de datos”.
  5. Sigue la guía de configuración.

Para ver todas las opciones para crear y configurar un conjunto de datos, consulta la documentación de BigQuery.

Permisos

Además de los roles y permisos estándar necesarios para usar Earth Engine, los llamadores también necesitan los permisos de BigQuery correctos en el proyecto o el conjunto de datos de Cloud.

  • bigquery.tables.get
  • bigquery.tables.create
  • bigquery.tables.updateData
  • bigquery.tables.delete
  • bigquery.jobs.create

Cualquiera de las siguientes combinaciones de roles predefinidos de Identity and Access Management (IAM) incluye los permisos necesarios:

  • bigquery.dataEditor más bigquery.jobUser
  • bigquery.dataOwner más bigquery.jobUser
  • bigquery.user
  • bigquery.admin

Precios

BigQuery es un servicio pagado de Google Cloud, por lo que se generarán cargos por el uso de BigQuery, incluido el almacenamiento y el análisis de los datos de Earth Engine que exportes a BigQuery.

Para obtener detalles sobre los precios de la función de exportación a BigQuery de Earth Engine, consulta la sección de precios a continuación.

Exportar configuración

Sintaxis

  Export.table.toBigQuery({
    'collection': myFeatureCollection,
    'table': 'myproject.mydataset.mytable',
    'description': 'put_my_data_in_bigquery',
    'append': true,
    'overwrite': false
  });
  task = ee.batch.Export.table.toBigQuery(
      collection=myFeatureCollection,
      table='myproject.mydataset.mytable',
      description='put_my_data_in_bigquery',
      append=True,
      overwrite=False)
  task.start()

Especificación del esquema automática o manual

Si no hay ninguna tabla en BigQuery, Earth Engine intenta determinar un esquema con las propiedades del primer ee.Feature de la colección. Esta es una suposición razonable, y es posible construir una colección en la que el esquema de la primera función sea diferente del esquema de otras funciones.

Si necesitas un esquema específico en tu tabla de BigQuery, configúralo creando una tabla vacía con el esquema de destino.

Nombres de las propiedades

Las propiedades de los atributos de Earth Engine corresponden a columnas de BigQuery. Earth Engine usa el nombre "geo" para escribir la geometría ee.Feature (el selector ".geo") en BigQuery.

Para evitar que se cambie el nombre, asegúrate de que tus objetos ee.Feature tengan propiedades que sean nombres de columna válidos y que ninguno se llame "geo" (ya que este nombre se usa para la geometría del componente, que no tiene nombre en Earth Engine).

Los caracteres no válidos en los nombres de las propiedades hacen que la exportación falle debido a las restricciones en los nombres de las columnas de BigQuery.

Conversión de tipos

Los datos de Earth Engine (los valores de las propiedades ee.Feature) se convierten en un tipo de BigQuery equivalente cuando es posible. Ten en cuenta que el esquema de la tabla controla la nulabilidad, no el tipo.

Tipo de Earth Engine Tipo de BigQuery Notas
ee.String STRING
ee.Number FLOAT o bien INTEGER
ee.Geometry GEOGRAPHY
ee.Date TIMESTAMP
ee.ByteString BYTES
ee.Array STRUCT<ARRAY<INT64>, ARRAY<INT64|FLOAT64>> Consulta la sección sobre arrays.
Otros tipos de ee.* not supported Consulta la sección sobre valores JSON.

Arrays

Earth Engine exporta cualquier ee.Array multidimesional a STRUCT<ARRAY<INT64> dimensions, ARRAY<INT64|FLOAT64> values>, similar al formato que usa la función ML.DECODE_IMAGE de BigQuery.

El primer array de la estructura, dimensions, contiene las dimensiones del array de Earth Engine, de d1 a dn.

El segundo array en la struct, values, contiene todos los valores del array multidimensiona, aplanados en un solo array de BigQuery. La cantidad total de valores en el array aplanado es ni=1di, y el valor en el índice (ii,,in) en el array original de Earth Engine corresponde al valor en el siguiente índice en el array aplanado:

nj=1(ijnk=j+1dk)

En los casos comunes, la expresión de indexación para el array values es la siguiente:

Tamaño del array Dimensiones Expresión de indexación
1 dimensión d1 [i1]
2 dimensiones d1, d2 [(i1 * d2) + i2]
Tridimensional d1, d2, d3 [(i1 * d2 * d3) + (i2 * d3) + i3]

Por ejemplo, considera un array de Earth Engine 2x3x4:

    ee.Array([
      [
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12]
      ],
      [
        [13, 14, 15, 16],
        [17, 18, 19, 20],
        [21, 22, 23, 24]
      ]
    ]);

Este array se traduce a un STRUCT de BigQuery cuyo elemento dimensions es el array [2, 3, 4] y cuyo elemento values es el array aplanado [1, 2, 3, 4, 5, 6, 7, 8, ..., 21, 22, 23, 24]. Los índices del array aplanado se pueden calcular como [(i1 * 12) + (i2 * 4) + i3].

Valores JSON

Para admitir datos más estructurados dentro de una celda, es posible codificar los valores de Earth Engine como objetos JSON. BigQuery admite operaciones de SQL en datos codificados en JSON, lo que permite realizar consultas que “busquen dentro” de los valores JSON codificados que generas en Earth Engine.

var states = ee.FeatureCollection('TIGER/2018/States');
var mod11a1 = ee.ImageCollection('MODIS/061/MOD11A1');

// Find the max day and night temperatures per pixel for a given time.
var maxTemp = mod11a1
    .select(['LST_Day_1km', 'LST_Night_1km'])
    .filterDate('2023-05-15', '2023-05-25')
    .max();

// Annotate each state with its max day/night temperatures.
var annotatedStates = states.map(function (e) {
  var dict = maxTemp.reduceRegion({
    reducer: ee.Reducer.max(),
    geometry: e.geometry(),
    scale: 10 * 1000,  // 10 km
  });
  // Convert the dictionary to JSON and add it as a property.
  return e.set('maxTemp', ee.String.encodeJSON(dict));
});

Export.table.toBigQuery(annotatedStates);

Consulta la página Entorno de Python para obtener información sobre la API de Python y el uso de geemap para el desarrollo interactivo.

import ee
import geemap.core as geemap
states = ee.FeatureCollection('TIGER/2018/States')
mod11a1 = ee.ImageCollection('MODIS/061/MOD11A1')

# Find the max day and night temperatures per pixel for a given time.
max_temp = (
    mod11a1.select(['LST_Day_1km', 'LST_Night_1km'])
    .filterDate('2023-05-15', '2023-05-25')
    .max()
)


def get_max_temp_for_state(e):
  max_temp_dict = max_temp.reduceRegion(
      reducer=ee.Reducer.max(),
      geometry=e.geometry(),
      scale=10 * 1000,  # 10 km
  )
  # Convert the dictionary to JSON and add it as a property.
  return e.set('maxTemp', ee.String.encodeJSON(max_temp_dict))


# Annotate each state with its max day/night temperatures.
annotated_states = states.map(get_max_temp_for_state)

task = ee.batch.Export.table.toBigQuery(
    collection=annotated_states, table='myproject.mydataset.mytable'
)
task.start()

Conversión de geometría

BigQuery tiene compatibilidad limitada con diferentes proyecciones, por lo que todas las geometrías de Earth Engine se transforman en EPSG:4326 geodésico con un margen de error de 1 metro.

Para tener un control más preciso sobre este proceso de transformación, puedes asignar manualmente los componentes y transformar sus geometrías, p.ej.:

var transformedCollection = originalCollection.map(function transformGeo(e) {
  var myErrorMargin = 10 * 1000;  // meters
  return e.setGeometry(e.geometry(myErrorMargin, 'EPSG:4326', true));
});

Consulta la página Entorno de Python para obtener información sobre la API de Python y el uso de geemap para el desarrollo interactivo.

import ee
import geemap.core as geemap
def transform_geo(e):
  my_error_margin = 10 * 1000  # meters
  return e.setGeometry(e.geometry(my_error_margin, 'EPSG:4326', True))


transformed_collection = original_collection.map(transform_geo)

Rendimiento

Rendimiento de Earth Engine

El procesamiento de Earth Engine suele ser el cuello de botella de las operaciones de Export. Para ello, es importante organizar el procesamiento para obtener el máximo paralelismo. Cualquier cálculo que se compile en el procesamiento en serie (por ejemplo, ee.FeatureCollection.iterate()) puede hacer que la exportación se ejecute con lentitud o falle.

Rendimiento en BigQuery

La mejor manera de garantizar que las consultas se puedan realizar de manera eficiente en BigQuery es estructurar y agrupar los datos correctamente. Si no hay una tabla presente en BigQuery, las tablas exportadas de Earth Engine se agrupan en la geometría de los componentes (si está presente). El agrupamiento por el campo de ubicación geográfica es muy común para los datos geoespaciales. Mejora el rendimiento y reduce el costo de las consultas que usan filtros espaciales, lo que suele ocurrir en operaciones de BigQuery como las siguientes:

WHERE ST_DWithin(<table_column>, <constant_geography>, <distance>)
WHERE ST_Intersects(<table_column>, <constant_geography>)

Por lo general, agregar el agrupamiento a una tabla no agrupada tampoco perjudica nada, aunque podría aumentar ligeramente el tiempo de carga de datos en la tabla. Para obtener más información sobre la optimización de consultas, consulta la documentación de BigQuery.

Ten en cuenta que la configuración de agrupamiento solo afecta a los datos nuevos que se escriben en la tabla.

Demostración: Cómo usar reduceRegions

En algunos casos, es posible usar reduceRegions para obtener el mayor paralelismo posible de la infraestructura de procesamiento de Earth Engine. En este ejemplo, se muestra cómo usar una menor cantidad de llamadas a reduceRegions (cientos) en lugar de decenas de miles de llamadas a reduceRegion (el enfoque típico para asignar una función a una colección).

var lucas = ee.FeatureCollection('JRC/LUCAS_HARMO/COPERNICUS_POLYGONS/V1/2018');
var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');

// Fetch the unique date values from the dataset.
var dates = lucas.aggregate_array('survey_date')
    .distinct()
    .map(function (date) {
      return ee.Date.parse('dd/MM/yy', date);
    });

// For each date, annotate the LUCAS samples with the Sentinel-2 band values for
// a two-week window.
function getLucasSamplesForDate(date) {
  date = ee.Date(date);
  var imageForDate = s2
    .filterDate(
      date.advance(-1, 'week'),
      date.advance(1, 'week'))
    .select('B.*');
  var median = imageForDate.median();
  var lucasForDate = lucas.filter(
    ee.Filter.equals('survey_date', date.format('dd/MM/yy')));
  var sample = median.reduceRegions({
    collection: lucasForDate,
    reducer: ee.Reducer.mean(),
    scale: 10,
    tileScale: 8,
  });
  return sample;
}

// Flatten the collection.
var withSamples =
    ee.FeatureCollection(dates.map(getLucasSamplesForDate))
      .flatten();

Export.table.toBigQuery({
  collection: withSamples,
  description: 'lucas_s2_annotated'
});

Consulta la página Entorno de Python para obtener información sobre la API de Python y el uso de geemap para el desarrollo interactivo.

import ee
import geemap.core as geemap
lucas = ee.FeatureCollection('JRC/LUCAS_HARMO/COPERNICUS_POLYGONS/V1/2018')
s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED')

# Fetch the unique date values from the dataset.
dates = (
    lucas.aggregate_array('survey_date')
    .distinct()
    .map(lambda date: ee.Date.parse('dd/MM/yy', date))
)


# For each date, annotate the LUCAS samples with the Sentinel-2 band values for
# a two-week window.
def get_lucas_samples_for_date(date):
  date = ee.Date(date)
  image_for_date = s2.filterDate(
      date.advance(-1, 'week'), date.advance(1, 'week')
  ).select('B.*')
  median = image_for_date.median()
  lucas_for_date = lucas.filter(
      ee.Filter.equals('survey_date', date.format('dd/MM/yy'))
  )
  sample = median.reduceRegions(
      collection=lucas_for_date,
      reducer=ee.Reducer.mean(),
      scale=10,
      tileScale=8,
  )
  return sample


# Flatten the collection.
with_samples = ee.FeatureCollection(
    dates.map(get_lucas_samples_for_date)
).flatten()

task = ee.batch.Export.table.toBigQuery(
    collection=with_samples,
    table='myproject.mydataset.mytable',
    description='lucas_s2_annotated',
)
task.start()

Paralelización de tareas

Con la opción {append: true}, es posible que varias tareas escriban datos en una tabla de BigQuery de forma simultánea. Este es un mecanismo para escribir datos con una mayor productividad, pero a costa de una mayor complejidad (administrar la cola de tareas, reintentarlo, etcétera).

Diferencias de rendimiento entre los parámetros append y overwrite

Ten en cuenta que la anulación es más lenta que la adición, ya que BigQuery debe procesar los datos nuevos antes de anular los anteriores. Si configuras el parámetro {overwrite: true} cuando exportas a una tabla de BigQuery existente, se activa un proceso de reemplazo seguro:

  1. Tabla temporal: Los datos se exportan a una tabla nueva y temporal dentro del conjunto de datos de destino.
  2. Reemplazo atómico: El contenido de la tabla temporal se copia en la tabla de destino final y reemplaza los datos existentes en una sola transacción atómica.
  3. Limpieza: Se borra la tabla temporal.

Esto garantiza que los errores durante la exportación no dañen tus datos existentes. En el caso de las tablas pequeñas, la demora suele ser de unos minutos.

Alternativas de alto rendimiento

Para los flujos de trabajo que requieren una capacidad de procesamiento muy alta, considera usar GeoBeam para mover datos de Earth Engine a BigQuery. Esto requiere más configuración e infraestructura, por lo que te sugerimos que comiences con la funcionalidad integrada de Earth Engine.

Precios

La exportación a BigQuery es un proceso por lotes que consume tiempo de EECU por lotes. Si usas Earth Engine de forma comercial o operativa, la exportación de datos a BigQuery te cobra el tiempo de EECU que usan las tareas. Todo el uso se puede supervisar con las mismas herramientas de supervisión que funcionan para el resto de Earth Engine.

Cuentas de facturación de Cloud

Para escribir datos en BigQuery, el proyecto de Cloud asociado debe tener habilitada una cuenta de facturación. Para obtener más información sobre la configuración de la cuenta de facturación, consulta la documentación de la cuenta de facturación de Cloud.

Salida

Todos los costos de entrada y salida se cobran como tráfico de red estándar.

Earth Engine solo se aloja en EE.UU., pero los conjuntos de datos de BigQuery se pueden alojar en varias otras regiones. Según las regiones y los volúmenes de datos involucrados, escribir datos de Earth Engine a BigQuery puede generar un tráfico de red considerable.

Problemas conocidos

Orientación para polígonos grandes

La función de exportación de BigQuery invierte los polígonos que son más grandes que un hemisferio revirtiendo su orientación (cambiando el polígono a su complemento geométrico). En casos excepcionales, es posible que no se carguen los polígonos más grandes que un hemisferio.

Si es necesario, los polígonos invertidos se pueden corregir en BigQuery. Para ello, vuelve a invertirlos con la expresión ST_Difference(ST_GeogFromText('fullglobe'), geo) de BigQuery.

Para obtener más información, consulta este artículo.