偵錯指南

您在 Earth Engine 中建立的演算法會在 Google 雲端中執行,並分散至多部電腦。偵錯作業可能相當困難,因為錯誤可能發生在用戶端程式碼或伺服器端執行程式碼指令時,並因縮放問題以及語法或邏輯錯誤而導致。除非您要求,否則無法檢查在雲端某處執行的程式位元。本文將介紹偵錯策略、工具和解決方案,協助您解決常見錯誤並偵錯 Earth Engine 指令碼。

語法錯誤

當程式碼違反程式設計語言 (Earth Engine 中的 JavaScript 或 Python) 規則時,就會發生語法錯誤。這些錯誤會導致程式碼無法執行,通常會在執行前偵測到。如果發生語法錯誤,請仔細查看醒目顯示的程式碼行或錯誤訊息,並參閱 Python 語言參考資料 Google JavaScript 樣式指南等資源。程式碼檢查器也可以協助找出並修正這些問題。

用戶端錯誤

即使程式碼的語法正確,仍可能發生與指令碼一致性或邏輯相關的錯誤。以下範例說明使用不存在的變數和方法所導致的錯誤。

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

// Load a Sentinel-2 image.
var image = ee.Image('USGS/SRTMGL1_003');

// Error: "bandNames" is not defined in this scope.
var display = image.visualize({bands: bandNames, min: 0, max: 9000});

// Error: image.selfAnalyze is not a function
var silly = image.selfAnalyze();

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

# Load a Sentinel-2 image.
image = ee.Image('USGS/SRTMGL1_003')

# NameError: name 'band_names' is not defined.
display = image.visualize(bands=band_names, min=0, max=9000)

# AttributeError: 'Image' object has no attribute 'selfAnalyze'.
silly = image.selfAnalyze()

第一個錯誤會通知您,bandNames 變數並未在參照的範圍中定義。解決方法是設定變數,或為 bands 參數提供清單引數。第二個錯誤則說明呼叫不存在的 selfAnalyze() 函式時會發生什麼情況。由於這不是圖片上的實際方法,因此錯誤訊息會告知您這不是函式。無論是哪種情況,錯誤都會描述問題。

不明的物件類型轉換

...is not a function」錯誤可能是因為 Earth Engine 不知道變數的類型。這個問題的常見徵兆通常是因為:

  • first() 傳回的物件執行某些操作 (集合中的元素類型不明)。
  • get() 傳回的物件執行某些操作 (儲存在屬性中的元素類型不明)。
  • 在引數類型不明的情況下,對函式引數 (在函式中) 執行某些動作。

以下是前者範例:

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

var collection = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');

// Error: collection.first(...).area is not a function
var area = collection.first().area();

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

collection = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017')

# AttributeError: 'Element' object has no attribute 'area'.
area = collection.first().area()

在所有情況下,解決方案都是使用已知類型的建構函式,將不明類型的物件轉換為該類型。接續先前的範例,解決方案是將值轉換為 ee.Feature

解決方法:使用投放功能!

程式碼編輯器 (JavaScript)

var area = ee.Feature(collection.first()).area();

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

area = ee.Feature(collection.first()).area()

(值得一提的是,您可以放心在 Element 上呼叫任何方法,因為 Earth Engine 會將其視為安全)。

避免混用用戶端函式和伺服器函式

以下範例較不明顯:

錯誤:這個程式碼無法執行您想要的操作

程式碼編輯器 (JavaScript)

// Don't mix EE objects and JavaScript objects.
var image = ee.Image('USGS/SRTMGL1_003');
var nonsense = image + 2;

// You can print this, but it's not what you were hoping for.
print(nonsense);

// Error: g.eeObject.name is not a function
Map.addLayer(nonsense);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

# Don't mix EE objects and Python objects.
image = ee.Image('USGS/SRTMGL1_003')
nonsense = image + 2

# TypeError: unsupported operand type(s) for +: 'Image' and 'int'.
display(nonsense)

# TypeError: unsupported operand type(s) for +: 'Image' and 'int'.
m = geemap.Map()
m.add_layer(nonsense)
m

假設這段程式碼的作者想將 2 新增至圖片中的每個像素,這並非正確做法。具體來說,這段程式碼將伺服器端物件 (image) 與用戶端運算子 (+) 混淆,因此結果可能會出乎意料。在第一種情況下,在 JavaScript 程式碼編輯器中列印 nonsense 時,系統會將 image2 轉換為字串,然後串連,執行要求的作業 (+)。產生的字串並非預期結果 (在 Python 中會擲回 TypeError)。在第二種情況下,將 nonsense 新增至地圖時,JavaScript 程式碼編輯器會顯示神秘的 g.eeObject.name is not a function 錯誤,這是因為新增至地圖的物件 nonsense 是字串,而非 EE 物件 (在 Python 中會擲回 TypeError)。為避免產生不預期的結果和無意義的錯誤,請勿將伺服器物件和函式與用戶端物件、基本元素或函式混用。本範例的解決方案是使用伺服器函式。

解決方法:使用伺服器函式!

程式碼編輯器 (JavaScript)

Map.addLayer(image.add(2));

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

m = geemap.Map()
m.add_layer(image.add(2))
m

詳情請參閱「用戶端與伺服器」頁面。

JavaScript 程式碼編輯器瀏覽器鎖定

當在用戶端執行的 JavaScript 耗時過長,或是等待 Earth Engine 執行某項作業時,瀏覽器就可能會凍結或鎖定。這類錯誤的兩個常見原因是 JavaScript 程式碼編輯器程式碼中的 for 迴圈和/或 getInfo(),最糟的情況是 for 迴圈中的 getInfo()。由於程式碼會在您的電腦上執行,因此 For 迴圈可能會導致瀏覽器鎖定。另一方面,getInfo() 會同步要求 Earth Engine 的運算結果,並在收到結果前保持封鎖狀態。如果運算時間過長,封鎖可能會導致瀏覽器鎖定。在程式碼編輯器中作業時,請避免使用 for 迴圈和 getInfo()。詳情請參閱「用戶端與伺服器」頁面。

伺服器端錯誤

即使用戶端程式碼具有邏輯一致性,仍可能出現只有在伺服器上執行時才會顯現的錯誤。以下範例說明嘗試取得不存在的頻帶時會發生什麼情況。

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

// Load a Sentinel-2 image.
var s2image = ee.Image(
    'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR');

// Error: Image.select: Pattern 'nonBand' did not match any bands.
print(s2image.select(['nonBand']));

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

# Load a Sentinel-2 image.
s2image = ee.Image(
    'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR'
)

# EEException: Image.select: Band pattern 'non_band' did not match any bands.
print(s2image.select(['non_band']).getInfo())

在這個範例中,錯誤訊息會告知您沒有名為 nonBand 的頻帶。可能的解決方法是指定確實存在的樂團名稱。您可以透過列印圖片並在控制台中檢查,或是列印 image.bandNames() 傳回的頻帶名稱清單,來找出頻帶名稱。

不變性

您在 Earth Engine 中建立的伺服器端物件是immutable。(任何 ee.Object 都是伺服器端 Object)。也就是說,如果您想變更物件,就必須將變更狀態儲存至新的變數。舉例來說,下列指令無法在 Sentinel-2 圖像上設定資源:

錯誤:這個程式碼無法執行您想要的動作!

程式碼編輯器 (JavaScript)

var s2image = ee.Image(
    'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR');
s2image.set('myProperty', 'This image is not assigned to a variable');

// This will not result in an error, but will not find 'myProperty'.
print(s2image.get('myProperty')); // null

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

s2image = ee.Image(
    'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR'
)
s2image.set('my_property', 'This image is not assigned to a variable')

# This will not result in an error, but will not find 'my_property'.
display(s2image.get('my_property'))  # None

在這個範例中,s2image.set() 會傳回含有新屬性的圖片副本,但儲存在 s2image 變數中的圖片不會改變。您需要將 s2image.set() 傳回的圖片儲存在新變數中。例如:

解決方案:請在變數中擷取結果!

程式碼編輯器 (JavaScript)

s2image = s2image.set('myProperty', 'OK');
print(s2image.get('myProperty')); // OK

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

s2image = s2image.set('my_property', 'OK')
display(s2image.get('my_property'))  # OK

對應函式

另一個用戶端函式與伺服器函式不混用的情況,是位於對應函式中。具體來說,由對應函式指定的作業會在雲端執行,因此 getInfoExport 等用戶端函式 (以及 JavaScript 程式碼編輯器中的 MapChart 上的 print 和方法) 無法在對應函式中運作。例如:

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

var collection = ee.ImageCollection('MODIS/006/MOD44B');

// Error: A mapped function's arguments cannot be used in client-side operations
var badMap3 = collection.map(function(image) {
  print(image);
  return image;
});

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

collection = ee.ImageCollection('MODIS/006/MOD44B')

# Error: A mapped function's arguments cannot be used in client-side operations.
bad_map_3 = collection.map(lambda image: print(image.getInfo()))

這個有點難解的錯誤是由於 Earth Engine 使用這個程序,將程式碼轉換為可在 Google 伺服器上執行的一組指令。用戶端函式和控制結構無法用於對傳遞至對應函式的引數圖片進行操作。為避免發生這項錯誤,請避免在對應函式中使用用戶端函式。請參閱「用戶端與伺服器」頁面,進一步瞭解用戶端函式與伺服器函式的差異。

對應函式另有其他規定。舉例來說,已對應的函式必須傳回某些內容:

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

var collection = ee.ImageCollection('MODIS/006/MOD44B');

// Error: User-defined methods must return a value.
var badMap1 = collection.map(function(image) {
  // Do nothing.
});

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

collection = ee.ImageCollection('MODIS/006/MOD44B')

# Error: User-defined methods must return a value.
bad_map_1 = collection.map(lambda image: None)

最明顯的解決方法就是傳回某個值。但它無法傳回任何類型的東西。具體來說,對 ImageCollectionFeatureCollection 對應的函式必須傳回 ImageFeature。舉例來說,您無法從 ImageCollection 對應的函式傳回日期:

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

var collection = ee.ImageCollection('MODIS/006/MOD44B');

var badMap2 = collection.map(function(image) {
  return image.date();
});

// Error: Collection.map: A mapped algorithm must return a Feature or Image.
print(badMap2);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

collection = ee.ImageCollection('MODIS/006/MOD44B')

bad_map_2 = collection.map(lambda image: image.date())

# EEException: Collection.map:
# A mapped algorithm must return a Feature or Image.
print(bad_map_2.getInfo())

為避免這種情況,請使用新的屬性集合傳回輸入圖片。接著,如果您需要集合內圖片的日期清單,可以使用 aggregate_array()

解決方法:設定屬性!

程式碼編輯器 (JavaScript)

var collection = ee.ImageCollection('MODIS/006/MOD44B');

var okMap2 = collection.map(function(image) {
  return image.set('date', image.date());
});
print(okMap2);

// Get a list of the dates.
var datesList = okMap2.aggregate_array('date');
print(datesList);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

collection = ee.ImageCollection('MODIS/006/MOD44B')

ok_map_2 = collection.map(lambda image: image.set('date', image.date()))
print(ok_map_2.getInfo())

# Get a list of the dates.
dates_list = ok_map_2.aggregate_array('date')
print(dates_list.getInfo())

程序錯誤

模式套用至沒有頻帶的圖片

"Pattern 'my_band' was applied to an Image with no bands" 錯誤表示有 ee.Image.select() 呼叫,且圖片的頻帶清單為空白。以下是解決方法:

  • 如果圖片是從含有中繼器的 ImageCollection 產生,或是使用 first()toBands() 呼叫產生,請確認來源集合不為空白。
  • 如果圖片是使用 ee.Dictionary().toImage() 產生的字典,請確認字典不為空白。
  • 如果圖片是獨立圖片,請確認圖片含有資料 (而非只有 ee.Image(0))。

縮放錯誤

雖然指令碼的語法可能正確、沒有邏輯錯誤,且代表伺服器的有效指令集,但在並行處理和執行運算時,產生的物件可能太大、太多,或需要花費太長的時間進行運算。在這種情況下,您會收到錯誤訊息,指出無法擴充演算法。這類錯誤通常最難診斷及解決。這類錯誤的例子包括:

  • 運算作業逾時
  • 並行匯總數量過多
  • 已超出使用者記憶體上限
  • 發生內部錯誤

改善程式碼的調整功能,可讓您更快取得結果,並改善所有使用者的運算資源可用性。我們將在後續章節討論各類錯誤,並簡要介紹 reduceRegion() 這個常用函式,因為它經常會導致各種縮放錯誤。

reduceRegion()

雖然 reduceRegion() 會貪婪地消耗足夠的像素,進而觸發各種錯誤,但也有用於控管運算的參數,可協助您克服錯誤。舉例來說,請考量下列不建議的縮減方式:

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

var absurdComputation = ee.Image(1).reduceRegion({
  reducer: 'count',
  geometry: ee.Geometry.Rectangle([-180, -90, 180, 90], null, false),
  scale: 100,
});

// Error: Image.reduceRegion: Too many pixels in the region.
//        Found 80300348117, but only 10000000 allowed.
print(absurdComputation);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

absurd_computation = ee.Image(1).reduceRegion(
    reducer='count',
    geometry=ee.Geometry.Rectangle([-180, -90, 180, 90], None, False),
    scale=100,
)

# EEException: Image.reduceRegion: Too many pixels in the region.
#        Found 80300348117, but only 10000000 allowed.
print(absurd_computation.getInfo())

這個愚蠢的例子只是用來做示範。這個錯誤的目的是要詢問您是否「真的」要減少 80300348117 (即 80 億) 個像素。如果沒有,請相應增加 scale (以公尺為單位的像素大小),或將 bestEffort 設為 true,自動重新計算較大的比例尺。如要進一步瞭解這些參數,請參閱 reduceRegion() 頁面。

運算作業逾時

假設您需要在運算中使用所有這些像素。如果是這樣,您可以增加 maxPixels 參數,讓運算成功。不過,Earth Engine 需要一段時間才能完成運算。因此,系統可能會擲回「計算超時」錯誤:

錯誤做法:請勿這麼做!

程式碼編輯器 (JavaScript)

var ridiculousComputation = ee.Image(1).reduceRegion({
  reducer: 'count',
  geometry: ee.Geometry.Rectangle([-180, -90, 180, 90], null, false),
  scale: 100,
  maxPixels: 1e11
});

// Error: Computation timed out.
print(ridiculousComputation);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

ridiculous_computation = ee.Image(1).reduceRegion(
    reducer='count',
    geometry=ee.Geometry.Rectangle([-180, -90, 180, 90], None, False),
    scale=100,
    maxPixels=int(1e11),
)

# Error: Computation timed out.
print(ridiculous_computation.getInfo())

這個錯誤的意思是,Earth Engine 會等待約五分鐘,然後才停止運算。匯出功能可讓 Earth Engine 在允許的較長執行時間 (但增加記憶體) 的環境中執行運算。由於 reduceRegion() 的傳回值是字典,因此您可以使用字典設定具有空值幾何圖形的要素屬性:

良好:使用 Export

程式碼編輯器 (JavaScript)

Export.table.toDrive({
  collection: ee.FeatureCollection([
    ee.Feature(null, ridiculousComputation)
  ]),
  description: 'ridiculousComputation',
  fileFormat: 'CSV'
});

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

task = ee.batch.Export.table.toDrive(
    collection=ee.FeatureCollection([ee.Feature(None, ridiculous_computation)]),
    description='ridiculous_computation',
    fileFormat='CSV',
)
# task.start()

並行匯總數量過多

這項錯誤的「aggregations」部分是指分散在多台機器上的作業 (例如跨多個圖塊的減法)。Earth Engine 設有限制,可避免同時執行過多這類匯總作業。在本例中,地圖內的減法觸發了「同時匯總數量過多」錯誤:

錯誤做法:請勿這麼做!

程式碼編輯器 (JavaScript)

var collection = ee.ImageCollection('LANDSAT/LT05/C02/T1')
    .filterBounds(ee.Geometry.Point([-123, 43]));

var terribleAggregations = collection.map(function(image) {
  return image.set(image.reduceRegion({
    reducer: 'mean',
    geometry: image.geometry(),
    scale: 30,
    maxPixels: 1e9
  }));
});

// Error: Quota exceeded: Too many concurrent aggregations.
print(terribleAggregations);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

collection = ee.ImageCollection('LANDSAT/LT05/C02/T1').filterBounds(
    ee.Geometry.Point([-123, 43])
)


def apply_mean_aggregation(image):
  return image.set(
      image.reduceRegion(
          reducer='mean',
          geometry=image.geometry(),
          scale=30,
          maxPixels=int(1e9),
      )
  )


terrible_aggregations = collection.map(apply_mean_aggregation)

# EEException: Computation timed out.
print(terrible_aggregations.getInfo())

假設這段程式碼的目的是要取得每張圖片的圖片統計資料,其中一個可能的解決方案是 Export 結果。舉例來說,利用 ImageCollection 也是 FeatureCollection 的事實,可以將與圖片相關聯的中繼資料匯出為表格:

良好:使用 Export

程式碼編輯器 (JavaScript)

Export.table.toDrive({
  collection: terribleAggregations,
  description: 'terribleAggregations',
  fileFormat: 'CSV'
});

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

task = ee.batch.Export.table.toDrive(
    collection=terrible_aggregations,
    description='terrible_aggregations',
    fileFormat='CSV',
)
# task.start()

已超出使用者記憶體上限

在 Earth Engine 中並行執行演算法的其中一種方法,就是將輸入內容分割成多個圖塊,在每個圖塊上分別執行相同的運算,然後再將結果合併。因此,計算輸出資訊方塊所需的所有輸入內容都必須符合記憶體容量。舉例來說,如果輸入內容是包含多個頻帶的圖片,如果在運算中使用所有頻帶,可能會耗用大量記憶體。為說明這一點,這個範例會將整個圖片集合 (不必要) 強制放入單一資訊方塊,導致使用過多記憶體:

錯誤做法:請勿這麼做!

程式碼編輯器 (JavaScript)

var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'];
var memoryHog = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands)
  .toArray()
  .arrayReduce(ee.Reducer.mean(), [0])
  .arrayProject([1])
  .arrayFlatten([bands])
  .reduceRegion({
    reducer: 'mean',
    geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000),
    scale: 1,
    bestEffort: true,
  });

// Error: User memory limit exceeded.
print(memoryHog);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']
memory_hog = (
    ee.ImageCollection('LANDSAT/LT05/C02/T1')
    .select(bands)
    .toArray()
    .arrayReduce(ee.Reducer.mean(), [0])
    .arrayProject([1])
    .arrayFlatten([bands])
    .reduceRegion(
        reducer=ee.Reducer.mean(),
        geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000),
        scale=1,
        bestEffort=True,
    )
)

# EEException: User memory limit exceeded.
print(memory_hog.getInfo())

這段程式碼非常糟糕,它說明瞭為何不使用陣列,除非您確實需要 (另請參閱「避免不必要的型別轉換」一節)。當該集合轉換為巨型陣列時,陣列必須一次全部載入記憶體。由於這是長時間序列的圖片,因此陣列太大,無法放入記憶體中。

一個可能的解決方法是將 tileScale 參數設為較高的值。較高的 tileScale 值會使圖塊縮小 tileScale^2 倍。舉例來說,以下程式碼可讓運算成功:

程式碼編輯器 (JavaScript)

var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'];
var smallerHog = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands)
  .toArray()
  .arrayReduce(ee.Reducer.mean(), [0])
  .arrayProject([1])
  .arrayFlatten([bands])
  .reduceRegion({
    reducer: 'mean',
    geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000),
    scale: 1,
    bestEffort: true,
    tileScale: 16
  });

print(smallerHog);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']
smaller_hog = (
    ee.ImageCollection('LANDSAT/LT05/C02/T1')
    .select(bands)
    .toArray()
    .arrayReduce(ee.Reducer.mean(), [0])
    .arrayProject([1])
    .arrayFlatten([bands])
    .reduceRegion(
        reducer=ee.Reducer.mean(),
        geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000),
        scale=1,
        bestEffort=True,
        tileScale=16,
    )
)

print(smaller_hog.getInfo())

不過,最理想的解決方案是不要不必要地使用陣列,這樣您就完全不需要調整 tileScale

良好:請避免使用陣列!

程式碼編輯器 (JavaScript)

var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'];
var okMemory = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands)
  .mean()
  .reduceRegion({
    reducer: 'mean',
    geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000),
    scale: 1,
    bestEffort: true,
  });

print(okMemory);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']
ok_memory = (
    ee.ImageCollection('LANDSAT/LT05/C02/T1')
    .select(bands)
    .mean()
    .reduceRegion(
        reducer=ee.Reducer.mean(),
        geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000),
        scale=1,
        bestEffort=True,
    )
)

print(ok_memory.getInfo())

除非需要解決記憶體錯誤,否則不應設定 tileScale,因為較小的圖塊也會導致較大的平行處理負擔。

內部錯誤

您可能會遇到類似以下的錯誤:

如果您收到這項錯誤,請按一下 JavaScript Code Editor 控制台中的「Report error」(回報錯誤) 連結。您也可以點選「說明」按鈕的「提供意見」。這個錯誤可能是因為指令碼中只有在執行期間才會明顯出現的邏輯錯誤,或是 Earth Engine 內部運作問題。無論是哪種情況,錯誤都沒有提供任何資訊,因此應回報錯誤,以便修正。

內部錯誤會包含 request ID,如下所示:

這些字串可做為專屬 ID,協助 Earth Engine 團隊找出特定問題。請在錯誤報告中加入這個字串。

偵錯方法

您編寫了分析程式碼並執行,但收到錯誤訊息。還得本節將說明一般偵錯技巧,協助您找出問題並加以修正。

檢查變數和地圖圖層

假設您有一個非常複雜的分析作業,但會產生錯誤。如果無法明確指出錯誤來源,建議您先採用適當的初始策略,也就是列印或視覺化中繼物件,並檢查這些物件,確保物件結構與指令碼中的邏輯一致。具體來說,您可以使用程式碼編輯器或 geemap 檢查器工具,檢查新增至地圖的圖層的像素值。如果您要列印內容,請務必使用 zippies (▶) 展開其屬性。您可以檢查的項目包括:

  • 樂團名稱。圖片的頻帶名稱是否與代碼相符?
  • 像素值。資料是否有正確的範圍?是否適當遮蓋?
  • 空值。是否有任何不應為空值的項目?
  • 尺寸。大小是否為零,但不應為零?

aside()

將分析中的每個中繼步驟都放入變數,以便列印及檢查,可能會造成負擔。如要從長串函式呼叫鏈結中列印中間值,您可以使用 aside() 方法。例如:

程式碼編輯器 (JavaScript)

var image = ee.Image(ee.ImageCollection('COPERNICUS/S2')
    .filterBounds(ee.Geometry.Point([-12.29, 168.83]))
    .aside(print)
    .filterDate('2011-01-01', '2016-12-31')
    .first());

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

image = ee.Image(
    ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
    .filterBounds(ee.Geometry.Point([-12.29, 168.83]))
    .aside(display)
    .filterDate('2011-01-01', '2016-12-31')
    .first()
)

請注意,aside(print) (JavaScript 程式碼編輯器) 和 aside(display) (Python geemap) 會呼叫用戶端函式,因此仍會在對應函式中失敗。您也可以將 aside 與使用者定義函式搭配使用。例如:

程式碼編輯器 (JavaScript)

var composite = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')
    .filterBounds(ee.Geometry.Point([106.91, 47.91]))
    .map(function(image) {
      return image.addBands(image.normalizedDifference(['B5', 'B4']));
    })
    .aside(Map.addLayer, {bands: ['B4', 'B3', 'B2'], max: 0.3}, 'collection')
    .qualityMosaic('nd');

Map.setCenter(106.91, 47.91, 11);
Map.addLayer(composite, {bands: ['B4', 'B3', 'B2'], max: 0.3}, 'composite');

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

m = geemap.Map()
m.set_center(106.91, 47.91, 11)

composite = (
    ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')
    .filterBounds(ee.Geometry.Point([106.91, 47.91]))
    .map(lambda image: image.addBands(image.normalizedDifference(['B5', 'B4'])))
    .aside(m.add_layer, {'bands': ['B4', 'B3', 'B2'], 'max': 0.3}, 'collection')
    .qualityMosaic('nd')
)

m.add_layer(composite, {'bands': ['B4', 'B3', 'B2'], 'max': 0.3}, 'composite')
m

first() 上執行函式

在可用時,列印和視覺化功能可用於偵錯,但如果您要偵錯在集合上對應的函式,則無法在函式中列印,如對應函式章節所述。在這種情況下,建議您在集合中隔離有問題的元素,並針對個別元素測試對應的函式。在未對函式進行對應的情況下測試函式時,您可以使用輸出陳述式來瞭解問題。請看以下範例。

錯誤:這個程式碼無法運作!

程式碼編輯器 (JavaScript)

var image = ee.Image(
    'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU');

var someFeatures = ee.FeatureCollection([
  ee.Feature(ee.Geometry.Point([-2.02, 48.43])),
  ee.Feature(ee.Geometry.Point([-2.80, 48.37])),
  ee.Feature(ee.Geometry.Point([-1.22, 48.29])),
  ee.Feature(ee.Geometry.Point([-1.73, 48.65])),
]);

var problem = someFeatures.map(function(feature) {

  var dictionary = image.reduceRegion({
    reducer: 'first',
    geometry: feature.geometry(),
    scale: 10,
  });

  return feature.set({
    result: ee.Number(dictionary.get('B5'))
                .divide(dictionary.get('B4'))
  });
});

// Error in map(ID=2):
//  Number.divide: Parameter 'left' is required.
print(problem);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

image = ee.Image(
    'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU'
)

some_features = ee.FeatureCollection([
    ee.Feature(ee.Geometry.Point([-2.02, 48.43])),
    ee.Feature(ee.Geometry.Point([-2.80, 48.37])),
    ee.Feature(ee.Geometry.Point([-1.22, 48.29])),
    ee.Feature(ee.Geometry.Point([-1.73, 48.65])),
])


# Define a function to be mapped over the collection.
def function_to_map(feature):
  dictionary = image.reduceRegion(
      reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10
  )

  return feature.set(
      {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))}
  )


problem = some_features.map(function_to_map)

# EEException: Error in map(ID=2):
#  Number.divide: Parameter 'left' is required.
print(problem.getInfo())

如要對此進行偵錯,建議您檢查錯誤。幸好,這個實用的錯誤訊息會通知您 ID=2 的功能有問題。如要進一步調查,不妨稍微重構程式碼。具體來說,當函式對集合進行對應時,您無法在函式中加入印出陳述式,如本節所述。偵錯目標是隔離有問題的功能,並執行包含一些輸出陳述式的函式。使用先前使用的相同圖片和功能:

程式碼編輯器 (JavaScript)

// Define a function to be mapped over the collection.
var functionToMap = function(feature) {

  var dictionary = image.reduceRegion({
    reducer: 'first',
    geometry: feature.geometry(),
    scale: 10,
  });

  // Debug:
  print(dictionary);

  return feature.set({
    result: ee.Number(dictionary.get('B5'))
                .divide(dictionary.get('B4'))
  });
};

// Isolate the feature that's creating problems.
var badFeature = ee.Feature(someFeatures
    .filter(ee.Filter.eq('system:index', '2'))
    .first());

// Test the function with print statements added.
functionToMap(badFeature);

// Inspect the bad feature in relation to the image.
Map.centerObject(badFeature, 11);
Map.addLayer(badFeature, {}, 'bad feature');
Map.addLayer(image, {bands: ['B4', 'B3', 'B2'], max: 3000}, 'image');

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

# Define a function to be mapped over the collection.
def function_to_map(feature):
  dictionary = image.reduceRegion(
      reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10
  )

  # Debug:
  display(dictionary)

  return feature.set(
      {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))}
  )


# Isolate the feature that's creating problems.
bad_feature = ee.Feature(
    some_features.filter(ee.Filter.eq('system:index', '2')).first()
)

# Test the function with print statements added.
function_to_map(bad_feature)

# Inspect the bad feature in relation to the image.
m = geemap.Map()
m.center_object(bad_feature, 11)
m.add_layer(bad_feature, {}, 'bad feature')
m.add_layer(image, {'bands': ['B4', 'B3', 'B2'], 'max': 3000}, 'image')
m

由於函式只會在一個地圖項目上執行,因此您可以將輸出 (在 Python 中為 `display`) 呼叫置於其中。檢查已列印的物件,發現 (啊哈!) reduceRegion() 傳回的物件在每個頻帶都有空值。這就是為何除法會失敗的原因:因為您無法將空值除以空值。為什麼一開始會是空值?如要進行調查,請將輸入圖片和錯誤地圖項目新增至地圖,並將焦點放在錯誤地圖項目上。您會發現問題是因為點位於圖片邊界之外。根據這項發現,偵錯程式碼如下:

程式碼編輯器 (JavaScript)

var functionToMap = function(feature) {
  var dictionary = image.reduceRegion({
    reducer: 'first',
    geometry: feature.geometry(),
    scale: 10,
  });
  return feature.set({
    result: ee.Number(dictionary.get('B5'))
                .divide(dictionary.get('B4'))
  });
};

var noProblem = someFeatures
    .filterBounds(image.geometry())
    .map(functionToMap);

print(noProblem);

Python 設定

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

import ee
import geemap.core as geemap

Colab (Python)

def function_to_map(feature):
  dictionary = image.reduceRegion(
      reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10
  )

  return feature.set(
      {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))}
  )


no_problem = some_features.filterBounds(image.geometry()).map(function_to_map)

display(no_problem)

Profiler

分析器會提供 EECU 時間和記憶體用量 (依演算法和資產) 的相關資訊,這些資訊是透過啟用時執行的運算產生。請查看剖析工具頂端的項目,瞭解最耗用資源的作業。如果是執行時間過長或效率不佳的指令碼,剖析器頂端的項目會提供線索,讓您知道要從何處著手,以便對指令碼進行最佳化。重要事項:分析器本身會影響指令碼的效能,因此請只在必要時執行。