Imagerie Parcellaire
Contexte
CerOps collecte des images satellite via l’API Copernicus (Sentinel-2) pour chaque parcelle agricole enregistrée. Ces images sont analysées pour produire des indices agronomiques (NDVI, stress hydrique, teneur en chlorophylle) qui permettent à l’agriculteur de suivre l’évolution de ses cultures dans le temps.
Objectif
Donner à l’agriculteur une vision temporelle de l’état de ses parcelles sans qu’il ait à commander une mission drone : les données satellite sont collectées automatiquement à chaque passage disponible (tous les 5 jours environ pour Sentinel-2), traitées en arrière-plan, et consultables depuis l’interface web.
Utilisateurs concernés
- Agriculteur : consulte les cartes d’indices et l’historique de ses parcelles
- Admin : supervise les acquisitions et relance les analyses en échec
Pipeline d’acquisition et d’analyse
Vue d’ensemble
Copernicus API
│
▼ acquisition image brute
RustFS (S3)
│
▼ message RabbitMQ
Worker d'analyse
├── calcule indices (NDVI, B04, B08…)
├── appende au cube Zarr de la parcelle (RustFS)
└── met à jour les métadonnées en base
Acquisition
Les images brutes Sentinel-2 sont téléchargées depuis l’API Copernicus et stockées dans RustFS avant traitement. Ce stockage intermédiaire découple la récupération (réseau externe, latence variable, possible indisponibilité) du pipeline d’analyse.
Traitement asynchrone (RabbitMQ)
Chaque nouvelle image disponible publie un message dans RabbitMQ. Un worker consomme la queue, calcule les indices spectraux et met à jour le cube Zarr de la parcelle. Ce découplage permet de traiter les analyses sans bloquer l’API, et de scaler horizontalement en ajoutant des workers.
Stockage des données : cubes Zarr
C’est l’un des choix architecturaux les plus structurants du projet.
Le problème
Les données agronomiques sont intrinsèquement temporelles : chaque passage satellite produit une nouvelle tranche de données par parcelle. Accumulées sur une saison ou plusieurs années, elles forment une série temporelle volumineuse.
Stocker ça naïvement pose des problèmes :
- En base de données (PostgreSQL) : une ligne par parcelle × date × indice crée des tables qui grossissent de façon incontrôlée, les requêtes “donne-moi le NDVI de cette parcelle sur 18 mois” deviennent coûteuses, et PostgreSQL n’est pas fait pour des tableaux numériques denses.
- En fichiers individuels (GeoTIFF par date) : pour reconstituer une série temporelle, il faut lire N fichiers séquentiellement, ce qui est lent et peu pratique pour de l’analyse.
La solution : le cube Zarr
Zarr est un format de tableau N-dimensionnel chunké et compressé, conçu nativement pour les accès partiels sur stockage objet (S3).
Un cube Zarr par parcelle a trois dimensions :
cube[parcelle_id] → tableau 3D :
├── axe 0 : temps (dates d'acquisition satellite)
├── axe 1 : x (pixels longitude)
└── axe 2 : y (pixels latitude)
... par bande / indice (NDVI, B04, B08…)
Le cube est découpé en chunks (ex. 30 dates × 64px × 64px). Lire “le NDVI de janvier à mars” ne charge que les chunks correspondants, pas l’intégralité du cube. C’est le même principe qu’un index en base de données, mais pour des tableaux multidimensionnels.
Avantages concrets
| Besoin | Approche naïve | Zarr |
|---|---|---|
| Série temporelle d’une parcelle | N requêtes SQL ou N fichiers | 1 lecture de chunks ciblés |
| Ajouter une nouvelle date d’acquisition | INSERT + gestion du schéma | Append d’un chunk sur l’axe temps |
| Analyse sur une fenêtre temporelle | Scan de table ou boucle fichiers | Slice cube[t_start:t_end, :, :] |
| Stockage sur S3 | Peu adapté (fichiers plats) | Natif, chaque chunk est un objet S3 |
| Interopérabilité Python (xarray, numpy) | Conversion nécessaire | Lecture directe |
Flux de mise à jour
À chaque nouvelle acquisition Copernicus, le worker :
- Récupère l’image brute depuis RustFS
- Calcule les indices (NDVI, stress hydrique…)
- Appende la nouvelle tranche au cube Zarr de la parcelle dans RustFS
- Met à jour les métadonnées en base (dernière date disponible, couverture nuageuse)
La base de données ne contient jamais les valeurs pixel, uniquement les métadonnées. Les cubes Zarr sont la source de vérité pour toute l’analyse agronomique.
Cas limites
- Couverture nuageuse : Sentinel-2 peut être inutilisable certains jours. La couverture nuageuse est stockée en métadonnée ; l’interface signale les lacunes à l’agriculteur.
- Parcelle sans historique : à la création d’une parcelle, le cube Zarr est initialisé vide et se remplit au fil des acquisitions suivantes.
- Échec du worker : RabbitMQ gère les relivraisons automatiques. Un message en échec répété passe en dead-letter queue pour traitement manuel.
Dépendances
- RustFS : stockage des images brutes et des cubes Zarr
- RabbitMQ : orchestration du pipeline d’analyse
- Copernicus API : source des images Sentinel-2
- Module Parcelles : les cubes sont indexés par
parcelle_id