Introducción a la programación funcional
Earth Engine usa un sistema de procesamiento paralelo para realizar cálculos en una gran cantidad de máquinas. Para habilitar este procesamiento, Earth Engine aprovecha las técnicas estándar que suelen usar los lenguajes funcionales, como la transparencia referencial y la evaluación diferida, para obtener mejoras significativas en la optimización y la eficiencia.
El concepto principal que distingue la programación funcional de la programación procedimental es la ausencia de efectos secundarios. Esto significa que las funciones que escribes no dependen de datos que están fuera de la función ni los actualizan. Como verás en los ejemplos que se muestran a continuación, es posible reestructurar tu problema para que se pueda resolver con funciones sin efectos secundarios, que son mucho más adecuadas para ejecutarse en paralelo.
Bucles for
No se recomienda el uso de bucles for en Earth Engine. Se pueden lograr los mismos resultados con una operación map() en la que especifiques una función que se pueda aplicar de forma independiente a cada elemento. Esto permite que el sistema distribuya el procesamiento a
diferentes máquinas.
En el siguiente ejemplo, se muestra cómo tomar una lista de números y crear otra con los cuadrados de cada número usando map():
Editor de código (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]
Condiciones If/Else
Otro problema común que enfrentan los usuarios nuevos que están acostumbrados al paradigma de programación procedimental es el uso adecuado de los operadores condicionales if/else en Earth Engine. Si bien la API proporciona un algoritmo ee.Algorithms.If(), se desaconseja su uso en favor de un enfoque más funcional con map() y filtros.
Earth Engine usa la
ejecución diferida, lo que significa que la evaluación de una expresión se retrasa hasta que
se requiere su valor real. En algunos casos, este tipo de modelo de ejecución evaluará
las alternativas verdaderas y falsas de una sentencia ee.Algorithms.If(). Esto puede
generar un uso adicional de procesamiento y memoria, según las expresiones y los recursos
necesarios para ejecutarlas.
Supongamos que quieres resolver una variante del ejemplo anterior, en la que la tarea es calcular los cuadrados de solo números impares. A continuación, se muestra un enfoque funcional para resolver este problema sin condiciones if/else:
Editor de código (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]
Este paradigma es especialmente aplicable cuando se trabaja con colecciones. Si quisieras aplicar un algoritmo diferente a la colección según algunas condiciones, la forma preferida es filtrar primero la colección según la condición y, luego, map() una función diferente para cada uno de los subconjuntos. Esto permite que el sistema paralelice la operación. Por ejemplo:
Editor de código (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());
Iteración acumulativa
Es posible que debas realizar una operación secuencial, en la que el resultado de cada iteración se use en la iteración posterior. Earth Engine proporciona un método iterate()
para esas tareas. Recuerda que iterate() se ejecuta de manera secuencial y,
por lo tanto, será lento para operaciones grandes. Úsala solo cuando no puedas usar map()
y filtros para lograr el resultado deseado.
Una buena demostración de iterate() es la creación de la secuencia de números de Fibonacci. Aquí, cada
número de la serie es la suma de los 2 números anteriores. La función iterate() toma 2 argumentos: una función (algoritmo) y un valor inicial. La función en sí recibe 2 valores:
el valor actual de la iteración y el resultado de la iteración anterior. En el siguiente ejemplo,
se muestra cómo implementar una secuencia de Fibonacci en Earth Engine.
Editor de código (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]
Ahora que comprendes bien los conceptos de JavaScript, puedes consultar el instructivo de la API para obtener una introducción a la funcionalidad geoespacial de la API de Earth Engine.