ייצוא נתונים ל-BigQuery

סקירה כללית

הארכיטקטורה החישובית של Earth Engine מותאמת לביצוע חישובים של תמונות (מבוססי פיקסלים) במהירות ובקלות התאמה. BigQuery מותאם באופן דומה לעיבוד נתונים בטבלאות (ווקטורים) שניתן להתאים לעומס, ויש לו תכונות רבות שמאפשרות לשלב אותו היטב עם Earth Engine.

תהליכי עבודה לדוגמה:

  • ביצוע צירופי BigQuery גדולים על נתונים שנוצרו ב-Earth Engine
  • הוספת הערות לנתוני וקטורים באמצעות נתונים סטטיסטיים שמתקבלים מתמונות לצורך עיבוד נוסף ב-BigQuery
  • ייצוא נתונים מדי פעם מ-Earth Engine לטבלת BigQuery שניתן להוסיף לה עוד נתונים

אם יש לכם תרחישי שימוש אחרים שתרצו לשתף, נשמח לשמוע עליהם.

יסודות BigQuery

מערכת Earth Engine כותבת לטבלאות של BigQuery, וכל הטבלאות נכללות במערכי נתונים. משימות הייצוא נכשלות אם מערך הנתונים שצוין לא נמצא ב-BigQuery. מידע נוסף זמין במאמר מבוא למערכי נתונים ב-BigQuery.

יצירת מערך נתונים

למערכי נתונים יש כמה אפשרויות בזמן היצירה, כולל השם, אזור האחסון והתנהגות התפוגה (לצד כמה אפשרויות מתקדמות יותר).

יש מגוון מנגנונים ליצירת מערכי נתונים, אבל דרך פשוטה להתחיל היא דרך מסוף Cloud:

  1. עוברים אל הדף של BigQuery במסוף Cloud.
  2. לוחצים על 'Enable' (הפעלה) כדי להפעיל את ה-API, אם מופיעה בקשה לעשות זאת.
  3. בכרטיסייה SQL Workspace, לוחצים על סמל התפריט של שלוש הנקודות () לצד הפרויקט.
  4. בוחרים באפשרות 'יצירת מערך נתונים'.
  5. פועלים לפי מדריך ההגדרה.

כל האפשרויות ליצירה ולהגדרה של מערך נתונים מפורטות במסמכי העזרה של BigQuery.

הרשאות

בנוסף לתפקידים וההרשאות הרגילים שנדרשים לשימוש ב-Earth Engine, למבצעי הקריאה החוזרת צריכות להיות גם הרשאות BigQuery מתאימות בפרויקט או במערך הנתונים ב-Cloud.

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

כל אחד מהשילבים הבאים של תפקידים מוגדרים מראש לניהול זהויות והרשאות גישה (IAM) כולל את ההרשאות הנדרשות:

  • bigquery.dataEditor יחד עם bigquery.jobUser
  • bigquery.dataOwner יחד עם bigquery.jobUser
  • bigquery.user
  • bigquery.admin

תמחור

BigQuery הוא שירות בתשלום ב-Google Cloud, כך שתצטברו חיובים על השימוש ב-BigQuery, כולל אחסון וניתוח של כל הנתונים מ-Earth Engine שיוצאו ל-BigQuery.

פרטים על התמחור של תכונת הייצוא ל-BigQuery ב-Earth Engine מופיעים בסעיף התמחור בהמשך.

ייצא תצורה

תחביר

  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 FLOAT או INTEGER
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>, בדומה לפורמט שבו נעשה שימוש בפונקציה ML.DECODE_IMAGE של BigQuery.

המערך הראשון ב-struct, ‏ dimensions, מכיל את המאפיינים של מערך Earth Engine, ‏ d1 עד dn.

המערך השני ב-struct, ‏ values, מכיל את כל הערכים במערך המימדי, שמתואמים למערך יחיד ב-BigQuery. המספר הכולל של הערכים במערך השטוח הוא ni=1di, והערך באינדיקטור (ii,,in) במערך המקורי ב-Earth Engine תואם לערך באינדיקטור הבא במערך השטוח:

nj=1(ijnk=j+1dk)

במקרים נפוצים, הביטוי להוספה לאינדקס של מערך values הוא:

גודל המערך מאפיינים הוספת ביטוי לאינדקס
חד-מימדי d1 [i1]
דו-מימדי d1, d2 [(i1 * d2) + i2]
תלת-ממדי 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]
      ]
    ]);

המערך הזה מתורגם ל-STRUCT ב-BigQuery, שבו הרכיב 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 יש תמיכה בפעולות SQL על נתונים בקידוד JSON, שמאפשרות להריץ שאילתות ש"נכנסות פנימה" לערכים של JSON המקודדים שאתם יוצרים ב-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);

בדף סביבת 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 עוברות טרנספורמציה ל-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 בו-זמנית בכמה משימות. זהו מנגנון לכתיבה של נתונים עם תפוקה גבוהה יותר, אבל הוא כרוך בעלות של מורכבות מוגברת (ניהול של תור המשימות, ניסיונות חוזרים וכו').

הבדלים בביצועים בין הפרמטרים append ו-overwrite

חשוב לזכור שכתיבה מחדש איטית יותר מאשר הוספה, כי BigQuery צריך לעבד את הנתונים החדשים לפני שהוא יכול להחליף את הנתונים הישנים. הגדרת הפרמטר {overwrite: true} במהלך הייצוא לטבלה קיימת ב-BigQuery מפעילה תהליך כתיבה בטוח:

  1. טבלה זמנית: הנתונים מיוצאים לטבלה זמנית חדשה במערך הנתונים של היעד.
  2. מחיקה אטומית: תוכן הטבלה הזמנית מועתק לטבלת היעד הסופית, ומחליף את הנתונים הקיימים בעסקה אטומית אחת.
  3. ניקוי: הטבלה הזמנית נמחקת.

כך תוכלו לוודא ששגיאות במהלך הייצוא לא יפגעו בנתונים הקיימים. בטבלאות קטנות, העיכוב הוא בדרך כלל כמה דקות.

חלופות בעלות ביצועים גבוהים

בתהליכי עבודה שדורשים תפוקה גבוהה מאוד, מומלץ להשתמש ב-GeoBeam כדי להעביר נתונים מ-Earth Engine ל-BigQuery. לשם כך נדרשות הגדרות ותשתית נוספות, לכן מומלץ להתחיל עם הפונקציונליות המובנית של Earth Engine.

תמחור

ייצוא ל-BigQuery הוא תהליך באצווה שצורך זמן EECU באצווה. אם אתם משתמשים ב-Earth Engine למטרות מסחריות או תפעוליות, הייצוא של נתונים ל-BigQuery יתבצע בחיוב לפי זמן ה-EECU שבו השתמשו המשימות. אפשר לעקוב אחרי כל השימוש באמצעות אותם כלים למעקב שעובדים בשאר Earth Engine.

חשבונות לחיוב ב-Cloud

כדי לכתוב נתונים ב-BigQuery, צריך להפעיל חשבון לחיוב בפרויקט המשויך ב-Cloud. מידע נוסף על הגדרת חשבון לחיוב זמין במסמכי העזרה בנושא חשבון לחיוב ב-Cloud.

תעבורת נתונים יוצאת (egress)

כל העלויות של תעבורת נתונים נכנסת ויוצאת מחוייבות כתנועה רגילה ברשת.

Earth Engine מתארח רק בארה"ב, אבל מערכי נתונים של BigQuery יכולים להתארח במספר אזורים אחרים. בהתאם לאזורים ולנפחי הנתונים, כתיבת נתונים מ-Earth Engine ל-BigQuery עשויה ליצור נפח תעבורה משמעותי ברשת.

בעיות מוכרות

כיוון לפוליגונים גדולים

פונקציית הייצוא של BigQuery הופכת פוליגונים גדולים יותר מחצי כדור על ידי היפוך הכיוון שלהם (שינוי הפוליגון למשלימים הגיאומטריים שלו). במקרים נדירים, יכול להיות שפוליגונים גדולים יותר מחצי כדור לא ייטענו.

אם צריך, אפשר לתקן ב-BigQuery פוליגונים שהופכים, על ידי הפיכת הפוליגונים שוב באמצעות הביטוי ST_Difference(ST_GeogFromText('fullglobe'), geo) ב-BigQuery.

מידע נוסף זמין כאן.