Алгоритмы, которые вы создаете в Earth Engine, выполняются в облаке Google, распределенном по множеству компьютеров. Отладка может быть сложной, поскольку ошибки могут возникать как в клиентском коде, так и при серверном выполнении закодированных инструкций, и быть результатом проблем масштабирования, а также синтаксических или логических ошибок. Части программы, которые выполняются в облаке, недоступны для проверки, если вы не запросите их. В этом документе представлены стратегии отладки, инструменты и решения, которые помогут вам устранить распространенные ошибки и отладить скрипты Earth Engine.
Ошибки в синтаксисе
Синтаксические ошибки возникают, когда код нарушает правила языка программирования (JavaScript или Python в Earth Engine). Они не позволяют коду выполняться и обычно обнаруживаются до выполнения. Если вы столкнулись с синтаксической ошибкой, внимательно изучите выделенную строку или сообщение об ошибке и обратитесь к таким ресурсам, как Справочное руководство по языку Python или Руководство по стилю Google JavaScript. Для выявления и устранения таких проблем можно использовать линтер.
Клиентские ошибки
Даже если синтаксис кода правильный, могут возникать ошибки, связанные с логикой скрипта. В примерах ниже показаны ошибки, возникающие при использовании несуществующих переменных и методов.
Ошибка – этот код не работает.
Редактор кода (JavaScript)
// Load a Sentinel-2 image. var image = ee.Image('NASA/NASADEM_HGT/001'); // 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('NASA/NASADEM_HGT/001') # 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()
(Стоит отметить, что здесь вы можете безопасно вызвать любой метод для Element, потому что Earth Engine считает, что это именно он).
Не смешивайте функции клиента и сервера
Вот менее очевидный пример:
Ошибка – код не выполняет нужные действия.
Редактор кода (JavaScript)
// Don't mix EE objects and JavaScript objects. var image = ee.Image('NASA/NASADEM_HGT/001'); 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('NASA/NASADEM_HGT/001') 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) ошибочно смешивается с клиентским оператором (+). Результаты могут быть неожиданными. В первом случае вывод nonsense в редакторе кода JavaScript выполнит запрошенную операцию (+), преобразовав 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. Два распространенных источника этой ошибки – циклы for и/или getInfo() в коде JavaScript в редакторе кода. Худший сценарий – getInfo() внутри цикла for. Циклы 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, неизменяемы. (Любой объект 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 (а также print и метод для Map и Chart в редакторе кода JavaScript), не будут работать в сопоставленных функциях. Пример:
Ошибка – этот код не работает.
Редактор кода (JavaScript)
var collection = ee.ImageCollection('MODIS/061/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/061/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/061/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/061/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/061/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/061/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/061/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/061/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);
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())
Этот глупый пример приведен только для демонстрации. Эта ошибка возникает, чтобы вы могли подтвердить, что действительно хотите уменьшить изображение на 80 300 348 117 (80 миллиардов) пикселей. Если нет, увеличьте значение scale (размер пикселя в метрах) или задайте для bestEffort значение true, чтобы автоматически пересчитать масштаб. Подробную информацию об этих параметрах можно найти на странице reduceRegion().
Превышено время ожидания вычислений.
Предположим, что вам нужны все эти пиксели в ваших вычислениях. Если это так, вы можете увеличить значение параметра maxPixels, чтобы вычисление было выполнено успешно. Однако Earth Engine потребуется некоторое время, чтобы завершить вычисления. В результате может возникнуть ошибка "computation timed out" (время ожидания вычисления истекло):
Плохо – так делать не нужно!
Редактор кода (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 ждал около пяти минут, прежде чем остановить вычисление. Экспорт позволяет Earth Engine выполнять вычисления в среде с более длительным допустимым временем работы (но не с большим объемом памяти). Поскольку возвращаемое значение reduceRegion() является словарем, вы можете использовать его для установки свойств объекта с нулевой геометрией:
Хорошо – используйте 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, например:
Эти строки служат уникальными идентификаторами, которые помогают команде Earth Engine выявлять конкретные проблемы. Добавьте эту строку в отчет об ошибке.
Методы отладки
Вы написали код для анализа, запустили его и получили ошибку. Что дальше? В этом разделе описаны общие методы отладки, позволяющие изолировать и устранить проблему.
Проверка переменных и слоев карты
Предположим, что вы выполняете очень сложный анализ, который приводит к ошибке. Если источник ошибки не очевиден, рекомендуется вывести на печать или визуализировать промежуточные объекты и проверить их, чтобы убедиться, что структура объекта соответствует логике скрипта. В частности, вы можете проверять значения пикселей слоев, добавленных на карту с помощью редактора кода или инструментов инспектора geemap. Если вы что-то печатаете, обязательно разверните свойства с помощью стрелок (▶). Вот что нужно проверить:
- Названия групп. Совпадают ли названия каналов изображения с вашим кодом?
- Значения пикселей. Правильно ли указан диапазон данных? Правильно ли она замаскирована?
- Null. Есть ли нулевые значения там, где их быть не должно?
- Размеры. Не равен ли размер нулю, когда это не так?
aside()
Помещать каждый промежуточный шаг анализа в переменную, чтобы его можно было распечатать и проверить, может быть обременительно. Чтобы напечатать промежуточные значения из длинной цепочки вызовов функций, можно использовать метод aside(). Пример:
Редактор кода (JavaScript)
var image = ee.Image(ee.ImageCollection('COPERNICUS/S2_HARMONIZED') .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()
Печать и визуализация полезны для отладки, когда они доступны, но когда вы отлаживаете функцию, сопоставленную с коллекцией, вы не можете печатать в функции, как описано в разделе сопоставленных функций. В этом случае полезно изолировать проблемные элементы в коллекции и протестировать сопоставленную функцию на отдельном элементе. При тестировании функции без сопоставления можно использовать операторы печати, чтобы понять, в чем проблема. Рассмотрим пример.
Ошибка – этот код не работает.
Редактор кода (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. Чтобы разобраться в ситуации, полезно немного провести рефакторинг кода. В частности, в функции не должно быть операторов печати, когда она сопоставляется с коллекцией, как описано в этом разделе. Цель отладки – изолировать проблемную функцию и запустить ее с несколькими операторами печати. Используя то же изображение и функции, что и ранее:
Редактор кода (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` для Python geemap). Проверьте напечатанный объект и убедитесь, что объект, возвращенный reduceRegion(), имеет нулевые значения для каждого диапазона. Вот почему деление не выполняется: нельзя разделить 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 и использовании памяти (для каждого алгоритма и объекта), полученную в результате вычислений, выполненных во время его работы. В верхней части списка будут показаны наиболее ресурсоемкие операции. Если скрипт выполняется долго или неэффективно, записи в верхней части профилировщика подскажут, на чем следует сосредоточить усилия по оптимизации. Важное примечание. Профайлер сам по себе влияет на производительность скрипта, поэтому его следует запускать только при необходимости.