匯出至 BigQuery

總覽

Earth Engine 的運算架構經過最佳化,可快速且可擴充地進行圖像 (以像素為基礎) 運算。BigQuery 同樣可針對表格資料 (向量) 進行可調整的處理作業,且具備許多功能,可與 Earth Engine 互補。

工作流程範例包括:

  • 針對在 Earth Engine 中產生的資料執行大型 BigQuery 彙整作業
  • 使用影像衍生的統計資料標註向量資料,以便在 BigQuery 中進一步處理
  • 定期將資料從 Earth Engine 匯出至可附加的 BigQuery 資料表

如果您有其他絕佳用途,歡迎與我們分享

BigQuery 基本概念

Earth Engine 會寫入 BigQuery 資料表,所有資料表都包含在資料集內。如果 BigQuery 中沒有指定的資料集,匯出工作就會失敗。如需更多資訊,請參閱 BigQuery 資料集簡介

建立資料集

資料集有許多建立時選項,包括名稱、儲存區域和到期行為 (以及其他更進階的選項)。

建立資料集的方法有很多種,但最簡單的方式就是透過 Cloud 控制台:

  1. 前往 Cloud 控制台中的 BigQuery 頁面
  2. 按一下「啟用」來啟用 API (如果出現提示)。
  3. 在「SQL 工作區」分頁中,按一下專案旁邊的三點選單 ()。
  4. 選擇「建立資料集」選項。
  5. 按照設定指南操作。

如要瞭解建立及設定資料集的所有選項,請參閱 BigQuery 說明文件

權限

除了使用 Earth Engine 所需的標準角色和權限外,呼叫端也需要 Cloud 專案或資料集的正確 BigQuery 權限

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

下列任何預先定義身分與存取權管理 (IAM) 角色組合都包含必要權限:

  • bigquery.dataEditorbigquery.jobUser
  • bigquery.dataOwnerbigquery.jobUser
  • bigquery.user
  • bigquery.admin

定價

BigQuery 是付費的 Google Cloud 服務,因此您必須為使用 BigQuery 支付費用,包括儲存和分析您匯出至 BigQuery 的任何 Earth Engine 資料。

如要詳細瞭解 Earth Engine 的 BigQuery 匯出功能定價,請參閱下方的定價章節

匯出組態設定

語法

  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()

自動或手動結構定義

如果 BigQuery 中沒有任何資料表,Earth Engine 會嘗試使用集合中第一個 ee.Feature 的屬性,判斷結構定義。這是最佳的推測方式,您可以建立集合,其中第一個項目的結構定義與其他項目的結構定義不同。

如果您需要在 BigQuery 資料表上使用特定結構定義,請建立目標結構定義的空白資料表來進行設定。

屬性名稱

Earth Engine 地圖項目的屬性會對應至 BigQuery 中的資料欄。Earth Engine 會使用「geo」這個名稱,將 ee.Feature 幾何圖形 (「.geo」選取器) 寫入 BigQuery。

為避免重新命名,請確認 ee.Feature 物件具有有效的資料欄名稱屬性,且沒有任何名稱為「geo」的屬性 (因為這個名稱會用於地圖項目的幾何圖形,而 Earth Engine 中沒有這個名稱)。

資源名稱含有無效字元,導致匯出作業失敗,因為 BigQuery 資料欄名稱有限制

類型轉換

在可行情況下,Earth Engine (ee.Feature 屬性的值) 資料會轉換為等同的 BigQuery 類型。請注意,可為空值的屬性是由資料表結構定義而非類型控制。

Earth Engine 類型 BigQuery 類型 注意事項
ee.String STRING
ee.Number FLOATINTEGER
ee.Geometry GEOGRAPHY
ee.Date TIMESTAMP
ee.ByteString BYTES
ee.Array STRUCT<ARRAY<INT64>, ARRAY<INT64|FLOAT64>> 請參閱「陣列」一節
其他 ee.* 類型 不支援 請參閱 JSON 值相關章節

陣列

Earth Engine 會將任何多維 ee.Array 匯出至 STRUCT<ARRAY<INT64> dimensions, ARRAY<INT64|FLOAT64> values>,類似 BigQuery ML.DECODE_IMAGE 函式使用的格式。

結構體 dimensions 中的第一個陣列包含 Earth Engine 陣列的維度,即 d1dn

結構體中的第二個陣列 values 包含多維陣列中的所有值,並扁平化為單一 BigQuery 陣列。扁平化陣列中的值總數為 ni=1di,原始 Earth Engine 陣列中索引 (ii,,in) 的值會對應至扁平化陣列中下列索引的值:

nj=1(ijnk=j+1dk)

在一般情況下,values 陣列的索引運算式如下:

陣列大小 尺寸 索引運算式
1 維 d1 [i1]
2D d1, d2 [(i1 * d2) + i2]
3D d1, d2, d3 [(i1 * d2 * d3) + (i2 * d3) + i3]

舉例來說,請考量 2x3x4 Earth Engine 陣列:

    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]
      ]
    ]);

這個陣列會轉譯為 BigQuery STRUCT,其中 dimensions 元素是陣列 [2, 3, 4],而 values 元素是扁平化陣列 [1, 2, 3, 4, 5, 6, 7, 8, ..., 21, 22, 23, 24]。扁平化陣列中的索引可計算為 [(i1 * 12) + (i2 * 4) + i3]

JSON 值

為了在單一儲存格中支援更豐富的結構化資料,可以將 Earth Engine 值編碼為 JSON 物件。BigQuery 支援以 JSON 編碼資料執行 SQL 作業,可讓查詢「查看」您在 Earth Engine 中產生的已編碼 JSON 值。

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);

請參閱「 Python 環境」頁面,瞭解 Python API 和如何使用 geemap 進行互動式開發。

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()

幾何轉換

BigQuery 僅支援有限的不同投影方式,因此所有 Earth Engine 幾何圖形都會使用 1 公尺的誤差邊界轉換為地理座標 EPSG:4326

如要進一步控管這個轉換程序,您可以手動對地圖項目進行對應,並轉換地圖項目的幾何圖形,例如:

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

請參閱「 Python 環境」頁面,瞭解 Python API 和如何使用 geemap 進行互動式開發。

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)

成效

Earth Engine 效能

Earth Engine 運算通常是 Export 作業的瓶頸。為此,您必須妥善安排處理程序,以便盡可能並行處理。任何在序列處理中執行的計算作業 (例如 ee.FeatureCollection.iterate()) 都可能導致匯出作業執行速度變慢或失敗。

BigQuery 效能

正確設定資料結構並分群資料,是確保 BigQuery 能有效執行查詢的最佳方式。如果 BigQuery 中尚未有資料表,從 Earth Engine 匯出的資料表會根據地形 (如有) 進行叢集。以地理位置欄位為依據進行叢集是地理空間資料的常見做法。這項功能可改善使用空間篩選器的查詢效能,並降低成本,最常用於 BigQuery 作業,例如:

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

一般來說,在未分叢集的資料表中加入叢集處理功能不會造成任何損害,但可能會稍微增加資料載入資料表的時間。如要進一步瞭解如何最佳化查詢,請參閱 BigQuery 說明文件

請注意,叢集設定只會影響寫入資料表的資料。

示範:使用 reduceRegions

在某些情況下,您可以使用 reduceRegions,盡可能從 Earth Engine 處理基礎架構中取得平行作業。本例說明如何使用較少的 reduceRegions 呼叫 (幾百個),而非數萬個 reduceRegion 呼叫 (在集合上對應函式時的一般做法)。

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'
});

請參閱「 Python 環境」頁面,瞭解 Python API 和如何使用 geemap 進行互動式開發。

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()

工作平行處理

使用 {append: true} 選項時,多個工作可以同時將資料寫入 BigQuery 資料表。這是一種用來以更高吞吐量寫入資料的機制,但代價是增加複雜度 (管理工作佇列、重試等)。

appendoverwrite 參數之間的效能差異

請注意,覆寫的速度比附加慢,因為 BigQuery 必須先處理新資料,才能覆寫舊資料。將資料匯出至現有 BigQuery 資料表時,設定 {overwrite: true} 參數會觸發安全覆寫程序:

  1. 臨時資料表:資料會匯出至目的資料集中的新臨時資料表。
  2. 原子覆寫:將暫時資料表的內容複製到最終目的地資料表,在單一原子交易中取代現有資料。
  3. 清理:刪除暫存資料表。

這可確保匯出過程中的錯誤不會損壞現有資料。對於小型資料表,延遲時間通常為幾分鐘。

高效能替代方案

如果工作流程需要非常高的吞吐量,建議使用 GeoBeam 將資料從 Earth Engine 移至 BigQuery。這需要更多設定和基礎架構,因此建議您先從內建的 Earth Engine 功能著手。

定價

匯出至 BigQuery 是耗用批次 EECU 時間的批次程序。如果您是為了商業或營運用途而使用 Earth Engine,匯出資料至 BigQuery 時,系統會向您收取工作使用的 EECU 時間費用。您可以使用與 Earth Engine 其他部分相同的監控工具,監控所有使用情形。

Cloud Billing 帳戶

如要將資料寫入 BigQuery,相關聯的 Cloud 專案必須啟用帳單帳戶。如要進一步瞭解如何設定帳單帳戶,請參閱 Cloud Billing 帳戶說明文件

輸出

所有輸入和輸出費用都會以標準網路流量計費。

Earth Engine 僅託管於美國,但 BigQuery 資料集可託管於多個其他地區。根據所涉及的區域和資料量,從 Earth Engine 寫入 BigQuery 的資料可能會產生大量網路流量。

已知問題

大型多邊形的方向

BigQuery 匯出函式會將大於半球的多邊形反轉,方法是反轉其方向 (將多邊形變更為其幾何學補數)。在極少數情況下,大於半球的多邊形可能無法載入。

如有需要,您可以使用 BigQuery 運算式 ST_Difference(ST_GeogFromText('fullglobe'), geo),再次反轉已反轉的多邊形,藉此在 BigQuery 中修正多邊形。

詳情請參閱這篇文章