Concepts de programmation fonctionnelle

Présentation de la programmation fonctionnelle

Earth Engine utilise un système de traitement parallèle pour effectuer des calculs sur un grand nombre de machines. Pour permettre un tel traitement, Earth Engine exploite des techniques standards couramment utilisées par les langages fonctionnels, telles que la transparence référentielle et l'évaluation paresseuse, pour optimiser et améliorer l'efficacité de manière significative.

Le principal concept qui distingue la programmation fonctionnelle de la programmation procédurale est l'absence d'effets secondaires. Cela signifie que les fonctions que vous écrivez ne dépendent pas de données extérieures à la fonction et ne les mettent pas à jour. Comme vous le verrez dans les exemples ci-dessous, il est possible de restructurer votre problème afin de le résoudre à l'aide de fonctions sans effets secondaires, qui sont beaucoup plus adaptées à une exécution en parallèle.

Boucles For

L'utilisation de boucles for est déconseillée dans Earth Engine. Vous pouvez obtenir les mêmes résultats à l'aide d'une opération map(), dans laquelle vous spécifiez une fonction pouvant être appliquée indépendamment à chaque élément. Cela permet au système de répartir le traitement sur différentes machines.

L'exemple ci-dessous montre comment prendre une liste de nombres et créer une autre liste avec les carrés de chaque nombre à l'aide de map() :

Éditeur de code (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]

Conditions If/Else

Un autre problème courant rencontré par les nouveaux utilisateurs habitués au paradigme de programmation procédurale est l'utilisation correcte des opérateurs conditionnels if/else dans Earth Engine. Bien que l'API fournisse un algorithme ee.Algorithms.If(), son utilisation est fortement déconseillée au profit d'une approche plus fonctionnelle utilisant map() et des filtres. Earth Engine utilise l' exécution différée, ce qui signifie que l'évaluation d'une expression est retardée jusqu'à ce que sa valeur réalisée soit réellement requise. Dans certains cas, ce type de modèle d'exécution évalue les alternatives "true" et "false" d'une instruction ee.Algorithms.If(). Cela peut entraîner une utilisation supplémentaire des ressources de calcul et de mémoire, en fonction des expressions et des ressources nécessaires pour les exécuter.

Imaginons que vous souhaitiez résoudre une variante de l'exemple ci-dessus, où la tâche consiste à calculer les carrés des nombres impairs uniquement. Une approche fonctionnelle pour résoudre ce problème sans conditions if/else est présentée ci-dessous :

Éditeur de code (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]

Ce paradigme est particulièrement applicable lorsque vous travaillez avec des collections. Si vous souhaitez appliquer un algorithme différent à la collection en fonction de certaines conditions, la méthode recommandée consiste à filtrer d'abord la collection en fonction de la condition, puis à appliquer (map()) une fonction différente à chacun des sous-ensembles. Cela permet au système de paralléliser l'opération. Exemple :

Éditeur de code (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());

Itération cumulative

Vous devrez peut-être effectuer une opération séquentielle, où le résultat de chaque itération est utilisé par l'itération suivante. Earth Engine fournit une méthode iterate() pour ces tâches. N'oubliez pas que iterate() est exécuté de manière séquentielle et sera donc lent pour les opérations de grande envergure. Utilisez-la uniquement lorsque vous ne pouvez pas utiliser map() et des filtres pour obtenir le résultat souhaité.

La création d'une séquence de nombres de Fibonacci est un bon exemple d'utilisation de iterate(). Ici, chaque nombre de la série est la somme des deux nombres précédents. La fonction iterate() accepte deux arguments : une fonction (algorithme) et une valeur de départ. La fonction elle-même reçoit deux valeurs : la valeur actuelle de l'itération et le résultat de l'itération précédente. L'exemple suivant montre comment implémenter une suite de Fibonacci dans Earth Engine.

Éditeur de code (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]

Maintenant que vous maîtrisez les concepts JavaScript, vous pouvez consulter le tutoriel sur l'API pour découvrir les fonctionnalités géospatiales de l'API Earth Engine.