Введение в функциональное программирование
Earth Engine использует систему параллельной обработки для выполнения вычислений на большом количестве машин. Чтобы обеспечить такую обработку, Earth Engine использует стандартные методы, обычно используемые функциональными языками, такие как ссылочная прозрачность и ленивые вычисления, для значительной оптимизации и повышения эффективности.
Основная концепция, которая отличает функциональное программирование от процедурного, – это отсутствие побочных эффектов. Это означает, что функции, которые вы пишете, не зависят от данных, находящихся за пределами функции, и не обновляют их. Как показано в примерах ниже, вы можете перестроить задачу так, чтобы ее можно было решить с помощью функций без побочных эффектов, которые гораздо лучше подходят для параллельного выполнения.
Циклы for
В Earth Engine не рекомендуется использовать циклы for. Того же результата можно достичь с помощью операции map(), в которой вы указываете функцию, применяемую к каждому элементу по отдельности. Это позволяет системе распределять обработку между разными компьютерами.
В примере ниже показано, как взять список чисел и создать другой список с квадратами каждого числа, используя map():
Редактор кода (JavaScript)
// This generates a list of numbers from 1 to 10. var myList = ee.List.sequence(1, 10); // The map() operation takes a function that works on each element independently // and returns a value. You define a function that can be applied to the input. var computeSquares = function(number) { // We define the operation using the EE API. return ee.Number(number).pow(2); }; // Apply your function to each item in the list by using the map() function. var squares = myList.map(computeSquares); print(squares); // [1, 4, 9, 16, 25, 36, 49, 64, 81]
Условия "если/иначе"
Ещё одна распространенная проблема, с которой сталкиваются новые пользователи, привыкшие к процедурной парадигме программирования, – правильное использование условных операторов if/else в Earth Engine. Хотя API предоставляет алгоритм ee.Algorithms.If(), мы настоятельно рекомендуем использовать более функциональный подход с map() и фильтрами.
В Earth Engine используется
отложенное выполнение. Это значит, что выражение вычисляется только тогда, когда его значение действительно требуется. В некоторых случаях такая модель выполнения оценивает как истинные, так и ложные альтернативы оператора ee.Algorithms.If(). Это может привести к дополнительным вычислениям и использованию памяти в зависимости от выражений и ресурсов, необходимых для их выполнения.
Предположим, вы хотите решить вариант приведенного выше примера, где задача состоит в вычислении квадратов только нечетных чисел. Ниже показан функциональный подход к решению этой проблемы без условий if/else:
Редактор кода (JavaScript)
// The following function determines if a number is even or odd. The mod(2) // function returns 0 if the number is even and 1 if it is odd (the remainder // after dividing by 2). The input is multiplied by this remainder so even // numbers get set to 0 and odd numbers are left unchanged. var getOddNumbers = function(number) { number = ee.Number(number); // Cast the input to a Number so we can use mod. var remainder = number.mod(2); return number.multiply(remainder); }; var newList = myList.map(getOddNumbers); // Remove the 0 values. var oddNumbers = newList.removeAll([0]); var squares = oddNumbers.map(computeSquares); print(squares); // [1, 9, 25, 49, 81]
Эта парадигма особенно применима при работе с коллекциями. Если вы хотите применить к коллекции другой алгоритм на основе определенных условий, рекомендуется сначала отфильтровать коллекцию на основе условия, а затем map() другую функцию к каждому из подмножеств. Это позволяет системе распараллеливать операцию. Пример:
Редактор кода (JavaScript)
// Import Landsat 8 TOA collection and filter to 2018 images. var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterDate('2018-01-01', '2019-01-01'); // Divide the collection into 2 subsets and apply a different algorithm on them. var subset1 = collection.filter(ee.Filter.lt('SUN_ELEVATION', 40)); var subset2 = collection.filter(ee.Filter.gte('SUN_ELEVATION', 40)); // Multiply all images in subset1 collection by 2; // do nothing to subset2 collection. var processed1 = subset1.map(function(image) { return image.multiply(2); }); var processed2 = subset2; // Merge the collections to get a single collection. var final = processed1.merge(processed2); print('Original collection size', collection.size()); print('Processed collection size', final.size());
Совокупная итерация
Вам может потребоваться выполнить последовательную операцию, в которой результат каждой итерации используется в последующей итерации. Для таких задач в Earth Engine предусмотрен метод iterate(). Помните, что метод iterate() выполняется последовательно, поэтому при обработке больших объемов данных он может работать медленно. Используйте его, только если не можете получить нужный результат с помощью map() и фильтров.
Хороший пример использования iterate() – создание последовательности чисел Фибоначчи. В этом случае каждое число в ряду является суммой двух предыдущих чисел. Функция iterate() принимает два аргумента: функцию (алгоритм) и начальное значение. Сама функция передается по двум значениям: текущему значению в итерации и результату предыдущей итерации. В следующем примере показано, как реализовать последовательность Фибоначчи в Earth Engine.
Редактор кода (JavaScript)
var algorithm = function(current, previous) { previous = ee.List(previous); var n1 = ee.Number(previous.get(-1)); var n2 = ee.Number(previous.get(-2)); return previous.add(n1.add(n2)); }; // Compute 10 iterations. var numIteration = ee.List.repeat(1, 10); var start = [0, 1]; var sequence = numIteration.iterate(algorithm, start); print(sequence); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
Теперь, когда вы хорошо понимаете концепции JavaScript, вы можете ознакомиться с руководством по API, чтобы получить представление о геопространственных функциях Earth Engine API.