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
매개변수에 목록 인수를 제공합니다. 두 번째 오류는 존재하지 않는 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
를 인식하는 방식이기 때문에 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가 발생함).
두 번째 경우, 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()
이며, 최악의 경우 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']));
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 코드 편집기의 print
및 Map
및 Chart
의 메서드)는 매핑된 함수에서 작동하지 않습니다. 예를 들면 다음과 같습니다.
오류: 이 코드는 작동하지 않습니다.
코드 편집기 (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()
호출이 있음을 의미합니다. 이 문제를 해결하려면 다음 단계를 따르세요.
- 이미지가 reducer가 있는 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);
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 0억) 픽셀을 정말로 줄이시겠는지 묻는 것입니다. 그렇지 않은 경우 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);
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에는 이러한 집계가 너무 많이 동시에 실행되지 않도록 제한이 적용되어 있습니다. 이 예에서는 맵 내의 감소로 인해 '동시 집계가 너무 많음' 오류가 트리거됩니다.
나쁨: 이렇게 하지 마세요.
코드 편집기 (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에서 알고리즘을 병렬화하는 한 가지 방법은 입력을 티글로 분할하고 각 티글에서 동일한 계산을 별도로 실행한 후 결과를 결합하는 것입니다. 따라서 출력 타일을 계산하는 데 필요한 모든 입력은 메모리에 들어맞아야 합니다. 예를 들어 입력이 여러 밴드가 있는 이미지인 경우 계산에 모든 밴드가 사용되면 메모리가 많이 소모될 수 있습니다. 이를 보여주기 위해 이 예에서는 전체 이미지 컬렉션을 불필요하게 카드로 강제하여 너무 많은 메모리를 사용합니다.
나쁨: 이렇게 하지 마세요.
코드 편집기 (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())
이 매우 나쁜 코드는 정말로 필요한 경우가 아니라면 배열을 사용하지 않는 한 가지 이유를 보여줍니다('불필요하게 유형 변환 피하기' 섹션 참고). 이 컬렉션이 거대한 배열로 변환되면 배열을 한 번에 메모리에 로드해야 합니다. 이미지의 시계열이 길기 때문에 배열이 커서 메모리에 맞지 않습니다.
한 가지 해결 방법은 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
이제 함수가 하나의 지형지물에서만 실행되므로 내부에 print (`display` for
Python geemap) 호출을 넣을 수 있습니다. 인쇄된 객체를 검사하여 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)
프로파일러
프로파일러는 사용 설정되어 있는 동안 실행된 계산으로 인해 발생한 EECU 시간 및 메모리 사용량 (알고리즘 및 애셋별)에 관한 정보를 제공합니다. 프로파일러 상단의 항목에서 가장 리소스 집약적인 작업에 관한 정보를 찾습니다. 장기 실행 또는 비효율적인 스크립트의 경우 프로파일러 상단의 항목은 스크립트를 최적화하기 위해 노력을 집중해야 할 위치에 관한 단서를 제공합니다. 중요사항: 프로파일러 자체가 스크립트의 성능에 영향을 미치므로 필요한 경우에만 실행해야 합니다.