Earth Engine で作成したアルゴリズムは、Google Cloud で実行され、多くのコンピュータに分散されます。エラーはクライアントサイドのコードまたはコード化された命令のサーバーサイドの実行で発生し、スケーリングの問題や構文エラーや論理エラーが原因である可能性があるため、デバッグは困難な場合があります。クラウドのどこかで実行されているプログラムのビットは、リクエストしない限り検査できません。このドキュメントでは、一般的なエラーの解決と 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();
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
パラメータにリスト引数を指定します。2 番目のエラーは、存在しない 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();
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();
import ee import geemap.core as geemap
Colab(Python)
area = ee.Feature(collection.first()).area()
(ここでは、Earth Engine がそう認識しているため、Element
で任意のメソッドを安全に呼び出すことができます)。
クライアントとサーバーの機能を混在させない
次の例はわかりにくいかもしれません。
エラー - このコードは望みどおりの処理を実行しません
コードエディタ(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);
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
を印刷すると、image
と 2
の両方を文字列に変換してから連結することで、リクエストされたオペレーション(+
)が実行されます。結果の文字列は意図したものではありません(Python では TypeError がスローされます)。2 番目のケースでは、地図に nonsense
を追加すると、JavaScript コードエディタに不可解な g.eeObject.name is not a function
エラーが表示されます。これは、地図に追加されるオブジェクト nonsense
が EE オブジェクトではなく文字列であるためです(Python では TypeError がスローされます)。意図しない結果や有用でないエラーを回避するには、サーバー オブジェクトと関数をクライアント オブジェクト、プリミティブ、関数と混同しないでください。この例では、サーバー関数を使用するのが解決策です。
解決策 - サーバー関数を使用する
コードエディタ(JavaScript)
Map.addLayer(image.add(2));
import ee import geemap.core as geemap
Colab(Python)
m = geemap.Map() m.add_layer(image.add(2)) m
詳細については、クライアントとサーバーのページをご覧ください。
JavaScript コードエディタのブラウザロック
ブラウザがフリーズまたはロックされる原因として、クライアントで実行される JavaScript の実行に時間がかかりすぎることや、Earth Engine からのデータを待機していることがあります。このエラーの一般的な原因は、JavaScript Code Editor のコード内の for ループや getInfo()
の 2 つです。最悪のシナリオは、for ループ内の getInfo()
です。コードがマシンで実行されるため、For ループによってブラウザがロックされる可能性があります。一方、getInfo()
は Earth Engine から計算結果を同期的にリクエストし、結果が受信されるまでブロックします。計算に時間がかかると、ブロックによりブラウザがロックされる可能性があります。Code Editor で作業する際は、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']));
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
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
import ee import geemap.core as geemap
Colab(Python)
s2image = s2image.set('my_property', 'OK') display(s2image.get('my_property')) # OK
マッピングされた関数
クライアント関数とサーバー関数が混在しない別のコンテキストは、マッピングされた関数です。具体的には、マッピングされた関数で指定されたオペレーションはクラウドで実行されるため、getInfo
や Export
などのクライアント関数(JavaScript コードエディタの Map
と Chart
の 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; });
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. });
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)
最も簡単な解決策は、返品することです。ただし、任意の型のオブジェクトを返すことはできません。具体的には、ImageCollection
または FeatureCollection
にマッピングされた関数は、Image
または Feature
を返す必要があります。たとえば、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);
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);
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()
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);
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(800 億)ピクセルを本当に削減するかどうかを確認するためのものです。そうでない場合は、scale
(ピクセルサイズ(メートル単位))を適宜増やすか、bestEffort
を true に設定して、より大きなスケールを自動的に再計算します。これらのパラメータの詳細については、reduceRegion()
のページをご覧ください。
計算がタイムアウトしました
計算にこれらのピクセルすべてが必要だとします。その場合は、maxPixels
パラメータを増やして計算を成功させることができます。ただし、計算が完了するまでに時間がかかります。その結果、「計算がタイムアウトしました」というエラーがスローされることがあります。
NG - このようなことは行わないでください。
コードエディタ(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);
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 が計算を停止する前に約 5 分間待機したことを意味します。エクスポートすると、Earth Engine は、許容される実行時間が長い環境で計算を実行できます(メモリは増加しません)。reduceRegion()
からの戻り値は辞書であるため、この辞書を使用して、null ジオメトリを持つ対象物のプロパティを設定できます。
良い - Export
を使用してください。
コードエディタ(JavaScript)
Export.table.toDrive({ collection: ee.FeatureCollection([ ee.Feature(null, ridiculousComputation) ]), description: 'ridiculousComputation', fileFormat: 'CSV' });
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()
同時実行される集計が多すぎる
このエラーの「集計」部分は、複数のマシンに分散されるオペレーション(複数のタイルにまたがる減算など)を指します。Earth Engine には、このような集計が同時に実行されすぎないようにする制限が設けられています。この例では、マップ内の減算によって「同時実行される集計が多すぎます」というエラーが発生しています。
NG - このようなことは行わないでください。
コードエディタ(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);
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' });
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 でアルゴリズムを並列化する方法の一つは、入力をタイルに分割し、各タイルで同じ計算を個別に実行してから結果を結合することです。そのため、出力タイルの計算に必要なすべての入力をメモリに収める必要があります。たとえば、入力が多くのバンドを含む画像の場合、すべてのバンドが計算で使用されると、メモリを大量に消費する可能性があります。これを示すために、この例では、画像コレクション全体を(不必要に)タイルに強制的に配置することで、過剰なメモリを使用しています。
NG - このようなことは行わないでください。
コードエディタ(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);
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())
この非常に悪いコードは、本当に必要な場合を除き、配列を使用しないことの理由の 1 つを示しています(「型を不必要に変換しない」セクションも参照)。そのコレクションが巨大な配列に変換されると、配列は一度にメモリに読み込まれる必要があります。長い時系列の画像であるため、配列が大きくなり、メモリに収まらない。
解決策の 1 つは、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);
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);
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 コード エディタ コンソールに表示される [エラーを報告] リンクをクリックします。[ヘルプ] ボタンからフィードバックを送信することもできます。このエラーは、実行時にのみ明らかになるスクリプトの論理エラーや、Earth Engine の内部動作に関する問題が原因で発生することがあります。どちらの場合も、エラーは有益ではなく、修正できるように報告する必要があります。
内部エラーには、次のような request
ID が含まれます。
これらの文字列は、Earth Engine チームが特定の問題を特定するのに役立つ一意の識別子として機能します。この文字列をバグレポートに含めます。
デバッグ方法
分析をコード化し、実行したところ、エラーが発生しました。次に求めるのは、このセクションでは、問題を特定して修正するための一般的なデバッグ手法について説明します。
変数と地図レイヤを検査する
エラーが発生する非常に複雑な分析があるとします。エラーの発生元が不明な場合は、まず中間オブジェクトを印刷または可視化して検査し、オブジェクトの構造がスクリプトのロジックと一致していることを確認します。具体的には、Code Editor または geemap インスペクタ ツールを使用して、地図に追加されたレイヤのピクセル値を検査できます。何かを印刷する場合は、必ずジッパー(▶)でプロパティを開いて、次のことを確認してください。
- バンド名。画像のバンド名はコードと一致していますか?
- ピクセル値。データの範囲は適切ですか?適切にマスキングされていますか?
- NULL: null であるべきではない値が null になっているか
- サイズ。サイズが 0 になっているのに、0 でないこと
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());
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');
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()
で関数を実行する
印刷と可視化は、利用可能な場合はデバッグに役立ちますが、コレクションにマッピングされた関数をデバッグする場合は、マッピングされた関数のセクションで説明されているように、関数内で印刷することはできません。この場合は、コレクション内の問題のある要素を分離し、個々の要素でマッピングされた関数をテストすると便利です。マッピングせずに関数をテストする場合は、print ステートメントを使用して問題を把握できます。次に例を示します。
エラー - このコードはご利用いただけません。
コードエディタ(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);
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
の機能に問題があることを通知します。詳しく調査するには、コードを少しリファクタリングすると便利です。具体的には、このセクションで説明するように、関数がコレクションにマッピングされている場合、関数内に print ステートメントを含めることはできません。デバッグの目的は、問題のある機能を分離し、いくつかの print ステートメントを含む関数を実行することです。前回使用した同じ画像と特徴を使用します。
コードエディタ(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');
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
関数は 1 つの特徴でのみ実行されるため、内部に print(Python geemap の場合は「display」)呼び出しを追加できます。出力されたオブジェクトを調べると、reduceRegion()
によって返されたオブジェクトにすべてのバンドに null があることがわかります。これが、除算が失敗する理由です。null を null で除算することはできません。そもそも null なのはなぜですか?
調査するには、入力画像と不適切な対象物を地図に追加し、不適切な対象物を中央に配置します。これにより、問題はポイントが画像の境界外にあることが原因であることがわかります。この検出結果に基づいて、デバッグされたコードは次のようになります。
コードエディタ(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);
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 時間とメモリ使用量(アルゴリズムとアセットごと)に関する情報を提供します。プロファイラの上部にあるエントリで、リソース使用量が最も多いオペレーションを確認します。長時間実行または非効率なスクリプトの場合、プロファイラの上部にあるエントリは、スクリプトを最適化するために重点的に取り組むべき場所を示します。重要な注意事項: プロファイラ自体がスクリプトのパフォーマンスに影響するため、必要な場合にのみ実行してください。