Introdução à programação funcional
O Earth Engine usa um sistema de processamento paralelo para realizar cálculos em um grande número de máquinas. Para ativar esse processamento, o Earth Engine usa técnicas padrão comumente usadas por linguagens funcionais, como transparência referencial e avaliação lenta, para otimizar e aumentar a eficiência de maneira significativa.
O principal conceito que diferencia a programação funcional da procedural é a ausência de efeitos colaterais. Isso significa que as funções que você escreve não dependem nem atualizam dados que estão fora da função. Como você verá nos exemplos abaixo, é possível reestruturar o problema para que ele possa ser resolvido usando funções sem efeitos colaterais, que são muito mais adequadas para serem executadas em paralelo.
Repetições for
O uso de loops for não é recomendado no Earth Engine. Os mesmos resultados podem ser alcançados usando uma operação map()
em que você especifica uma função que pode ser aplicada de forma independente a cada elemento. Isso permite que o sistema distribua o processamento para
máquinas diferentes.
O exemplo abaixo ilustra como pegar uma lista de números e criar outra com os quadrados 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]
Condições if/else
Outro problema comum enfrentado por novos usuários acostumados ao paradigma de programação procedural é o uso adequado de operadores condicionais if/else no Earth Engine. Embora a API forneça um algoritmo ee.Algorithms.If()
, o uso dele é altamente desencorajado em favor de uma abordagem mais funcional usando map()
e filtros.
O Earth Engine usa a
execução adiada, o que significa que a avaliação de uma expressão é adiada até que o valor realizado seja realmente necessário. Em alguns casos, esse tipo de modelo de execução avalia as alternativas verdadeira e falsa de uma instrução ee.Algorithms.If()
. Isso pode
levar a um uso extra de computação e memória, dependendo das expressões e dos recursos
necessários para executá-las.
Suponha que você queira resolver uma variante do exemplo acima, em que a tarefa é calcular os quadrados de apenas números ímpares. Uma abordagem funcional para resolver isso sem condições if/else é demonstrada abaixo:
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]
Esse paradigma é especialmente aplicável ao trabalhar com coleções. Se você quiser aplicar um algoritmo diferente à coleção com base em algumas condições, a maneira recomendada é primeiro filtrar a coleção com base na condição e, em seguida, map()
uma função diferente para cada um dos subconjuntos. Isso permite que o sistema paralelise a operação. Por exemplo:
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());
Iteração cumulativa
Talvez seja necessário fazer uma operação sequencial, em que o resultado de cada iteração é usado pela iteração subsequente. O Earth Engine oferece um método iterate()
para essas tarefas. Lembre-se de que iterate()
é executado de maneira sequencial e, portanto, será lento para operações grandes. Use-o apenas quando não for possível usar map()
e filtros para alcançar o resultado desejado.
Uma boa demonstração de iterate()
é a criação de uma sequência de números de Fibonacci. Aqui, cada número na série é a soma dos dois números anteriores. A função iterate()
usa dois argumentos: uma função (algoritmo) e um valor inicial. A função recebe dois valores: o valor atual na iteração e o resultado da iteração anterior. O exemplo a seguir demonstra como implementar uma sequência de Fibonacci no 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]
Agora que você tem um bom entendimento dos conceitos de JavaScript, confira o tutorial da API para uma introdução à funcionalidade geoespacial da API Earth Engine.