Эта тема началась как поздравление с Новым 2021 годом, шутка между несколькими фанатами наблюдения за Землей (EO)¹. Но он привлек внимание большего числа людей, чем я думал, с более чем 2600 просмотров за 24 часа, поэтому я подумал, что стоит опубликовать код, который можно использовать напрямую, вместо того, чтобы «оставлять читателю в качестве упражнения».

Первоначальный пост был фрагментом 9-строчного кода ниндзя с использованием редактора кода JavaScript Google Earth Engine (GEE). Он строит максимально безоблачный набор мозаичных изображений в течение месяца из неограниченной последовательности изображений для статистики, классификации или ИИ. См. пример на заглавном изображении. Когда я начал EO в 1981 году, то же самое потребовало бы более 500 строк Fortran + библиотеки и часов выполнения. Теперь это 9 строк кода и результат мгновенный.

Для этого мы используем сочетание информатики и ЭО. Давайте начнем с приведенного ниже кода, который вы можете скопировать, вставить и запустить непосредственно в редакторе кода GEE. Подробные пояснения к учебнику приведены ниже. Если вы новичок, прочтите дополнительную информацию о том, как держаться за руки, в приложении² и приложении³.

Полностью функциональный код

Скопируйте и вставьте следующее в редактор кода GEE и нажмите «Выполнить».

Вы можете остановиться здесь, если вам не интересно воспроизвести шаблон в вашем собственном коде GEE.

Подробное объяснение кода

Вы можете сразу перейти к руководству по функции map ниже и вернуться к этому подробному объяснению позже.

Код подготовки

  • строка 9: установка центральной точки анализа, долгота-широта в градусах. Возможно, вы захотите изменить свое любимое место.
  • строка 13: устанавливаем месяц, по которому мы хотим рассчитать максимально безоблачное изображение за несколько лет, например, чтобы сделать статистику индекса вегетации, или обучить алгоритмы классификации или ИИ. Здесь мы используем апрель, который является концом сухого сезона в Тамил Наду. Вы можете перейти на ноябрь, который является одним из самых облачных месяцев.
  • строка 15: установить список лет обучения. Sentinel 2 имеет изображения только за полный год с 2017 года. Если вы используете MODIS, вы можете получить изображения с 2000 года.

Настоящий код ниндзя: 1 строка :-)

  • строка 16: первый и основной «код ниндзя»: он строит набор изображений, каждое из которых представляет собой мозаику из всех изображений месяца, используя как можно меньше облачных пикселей. Мы используем функцию JavaScript «map» вместо функции «makeBestImage». См. дополнительные пояснения ниже.

Иллюстрация использования результата

  • строки 17–22: иллюстративный пример использования результата, не являющийся непосредственно частью кода ниндзя. В качестве упражнения вы можете использовать функцию JavaScript reduce для вычисления среднего значения и стандартного отклонения на этих безоблачных изображениях?

Где все происходит…

  • строки 25–41: это функция makeBestImage, которая дается в качестве аргумента для вызова функции JavaScript «map» в строке 16. В строке 33 она возвращает анонимную функцию, которую мы сейчас увидим.
  • строки 33–40: анонимная функция, function(year). Используется функцией JavaScript «map» и получает в качестве уникального аргумента от «map» один элемент «year» списка, по которому выполняет итерацию функция «map».
  • строка 34: установить дату начала диапазона, на котором выполняется безоблачная мозаика.
  • строка 35: установить продолжительность диапазона. Вы можете попробовать изменить его с «1 месяц» на «4 недели» или «2 недели» и т. д. Если повторные визиты происходят достаточно часто, может быть, достаточно 2 недель?
  • строка 36: извлечь в filtered уменьшенный набор изображений, к которым можно применить мозаику
  • строка 37: еще один «код ниндзя», использующий «map» для вычисления NDVI каждого пикселя.
  • строка 38: здесь не так уж и «ниндзя», мы просто вызываем функцию GEE mosaic для создания мозаичного изображения из пикселей всех filtered изображений с лучшим индексом NDVI
  • строки 44–54: вспомогательная функция для вычисления индекса

Краткое руководство по функции «карта» в JavaScript

Займемся основами информатики :-)

Есть 3 встроенные функции JavaScript, которые особенно полезны в GEE, потому что вы можете заставить GEE массово распараллелить свой код, то есть распределить его по потенциально десяткам виртуальных машин, выполняющихся параллельно. Это map, reduce и filter. Смотрите подробные объяснения здесь. Короче говоря, они представляют собой значительное улучшение по сравнению со старой доброй петлей for наших дедов.

  • В цикле for вы просите компилятор указать ЦП многократно выполнять некоторые инструкции, например, вызывать функцию для всех элементов массива.
  • С помощью функции map вы просите сервер GEE⁵ сгенерировать столько копий функции, сколько требуется, и сопоставить их с каждым элементом массива.

Когда вы выполняете цикл for, вы говорите GEE: «пожалуйста, делайте в точности то, что я вам говорю, итерацию за итерацией, я могу сделать что-то более сложное, чем просто итерация по массиву». В этом случае GEE воздерживается от распараллеливания.

Наоборот, при работе с GEE, облачной службой с виртуальными машинами, если вы используете вызов map, вы на самом деле говорите GEE: «Эй, эта функция работает с массивом, не стесняйтесь распараллеливать ее настолько, насколько как сможете».

Обратите внимание на 3 вещи:

  • map — это встроенная функция класса array⁴: вы вызываете map, используя запись через точку, array.map(function).
  • переменная первого класса: вы даете map аргумент, который является функцией, и его обратный вызов makeBestImage даже возвращает функцию. Функции рассматриваются как первоклассные объекты в JavaScript.
  • функции обратного вызова: функция обратного вызова, такая как makeBestImage, может выполняться в другой области. Это позволяет использовать в более широком контексте такие функции, как map, которые имеют только один аргумент.

Продолжим об обратном вызове. В программировании EO и блокчейне вы будете очень часто использовать обратные вызовы.

Передача функций в качестве аргументов или возвратов и обратных вызовов

Здесь нужно решить следующую проблему: когда map работает с массивом, она знает только элемент массива, над которым работает, и ничего больше, так как же мы можем предоставить больше информации функции, которую map связывает с массивом? элемент?

Например, вы можете вызвать map для расчета не только NDVI изображений (NIR и Red), но и повторно использовать его для расчета любого нормализованного дифференциального индекса, такого как NDWI-Gao (NIR, Short Wave IR) или NDWI-Mc Feeters ( NIR, зеленый) и т. д. Вместо жесткого кодирования спектральных полос в функции вы даете ей 2 аргумента, помимо map, как показано ниже (сравните со строкой 37 выше):

imageArray_withNDVI = imageArray.map(addIndex(NIR, Red));

когда map вызывает addIndex(), обратный вызов, последний ничего не делает, кроме как возвращает анонимную функцию. Эта функция является настоящим итератором: она вычисляет нормализованную разницу двух полос в контексте вызывающей стороны и добавляет результирующую полосу к аргументу image. Сравните со строками 51–54 выше.

return function (image) { var ndvi = image.normalizedDifference ([NIR, RED]).rename (‘NDVI’); return image.addBands (ndvi); }}

Следующая картинка может быть проще для понимания.

  • В левом столбце мы позволяем map напрямую применять функцию ко всем элементам массива и создавать другой массив с результатами. Эта функция может иметь только один аргумент — элемент массива.
  • В правом столбце мы разрешаем map применять функцию, которая сама вызывает функцию, возвращающую результат map. Первая функция — это обратный вызов, который может иметь столько аргументов, сколько необходимо.

В нашем примере кода ниндзя мы используем эту настройку дважды, один раз для отображения makeBestImage и один раз для отображения addNDVI. Овладейте им, и вы будете использовать его все время в GEE. Теперь вы умеете читать и понимать приведенный выше код.

Мы также будем использовать обратные вызовы при программировании блокчейна, но оставим эту тему для другой статьи. Быть в курсе. На самом деле программирование ЭО и блокчейна не так уж сильно отличается.

[1] https://www.linkedin.com/posts/kvutien_earthobservation-tech4good-blockchain4good-activity-6753627504562450432-1GKV

[2] У вас нет учетной записи GEE для запуска кода? Вот как получить плату за счет GEE.

[3] Вы новичок в Наблюдении за Землей? Вот практическое интерактивное упражнение для изучения вегетативных индексов (NDVI, NDWI, LAI и др.).

[4] на самом деле map, reduce и filter работают с массивами, списками и коллекциями. Не только на массивах.

[5] поскольку map является серверной функцией, вы можете столкнуться с неожиданностями или сомнительными сообщениями об ошибках от GEE, когда вы вставляете клиентские функции в область «mapped», например, если вы используете print().