Skip to content

Job extract_sentinel_2 #36

Closed

Description

@Nicolas-Dmb

📋 Description

Récupérer l'imagerie Sentinel-2 (API Copernicus), calculer les indices (NDVI, NDWI, NDBI, SWIR) par zone H3 pour un bucket_id mensuel, appliquer les filtres qualité (nuages/pixels valides), puis UPSERT direct dans satellite_features_by_zone (pipeline direct API → PostGIS, pas de parquet intermédiaire).

✅ Acceptance Criteria

  • DoD générales (logs, idempotence, job_runs)
  • Exécution par aoi_id + bucket_id (mensuel) + h3_res
  • Filtrage images (cloud threshold) + métriques qualité (obs_count, valid_pixel_ratio, cloud_score)
  • Calcul indices NDVI, NDWI, NDBI, SWIR
  • UPSERT idempotent dans satellite_features_by_zone (+ job_run_zone_errors si partial)
  • Retry backoff exponentiel sur 429, parallélisme configurable
  • Couverture de tests 100%

🔗 Prerequisite Issues

⚠️ DoR : Outils Commun (AOI registry, grille H3 zones_hex, normalisation bucket, idempotence, CLI) + table satellite_features_by_zone (UNIQUE(zone_id, bucket_id, source_version)) :


Source : Notion « Job extract_sentinel_2 » (Prétraitements & Pipeline Data).

Activity

  1. self-assigned this
    on Sep 15, 2026
  2. Nicolas-Dmb commented on Oct 6, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    ⏳ Retard identifié : rate-limit réel de la Statistical API Sentinel Hub

    Après implémentation complète du job (packages/sentinel/sentinel_extract.py, branche feat/sentinel-hub-client) — client auth OAuth + Statistical API (PR #90), evalscript NDVI/NDWI/NDBI/SWIR, gestion d'erreur par zone (job_run_zone_errors), parallélisme configurable, retry/backoff sur 429, 100% de couverture de tests — un premier run réel sur l'AOI idf en résolution 8 (18 307 cellules) a révélé un vrai goulot d'étranglement côté API, indépendant du code :

    • ~24 requêtes passent en ~6s, puis 429 Too Many Requests avec Retry-After ≈ 130s.
    • Extrapolé sur les 18 307 cellules : ~24 à 28h pour un run mensuel complet, contre 1-2h estimées initialement.
    • Confirmé : ce n'est pas un bug (le retry/backoff fonctionne correctement), c'est le débit réel autorisé par le compte CDSE gratuit.

    Deux bugs réels ont aussi été trouvés et corrigés en testant en conditions réelles (pas détectables sans un vrai run contre l'API) :

    • time_range envoyait une borne de fin une seconde trop courte pour aggregationInterval=P1M → l'API renvoyait zéro intervalle pour chaque requête.
    • La sortie dataMask de l'evalscript n'est jamais renvoyée par l'API (elle est consommée comme masque interne) → valid_pixel_ratio doit être dérivé de sampleCount/noDataCount d'une vraie sortie (ndvi), pas lu depuis dataMask.

    Détail complet du raisonnement et des options évaluées (quota, Batch Statistical API, alternatives) : ADR-003 dans docs/architecture-decisions.md (branche feat/sentinel-hub-client), mise à jour le 2026-10-06.

    Statut actuel : demande de quota envoyée au support Copernicus + candidature "Copernicus Services user" en cours (accès potentiel à la Batch Statistical API, sans rate-limit par minute). Le job n'est pas bloqué fonctionnellement — juste lent — et peut tourner en job de fond/nocturne sans risque (idempotence par zone, reprise sans doublon). En attente de retour du support avant de considérer le ticket terminé.

  3. Nicolas-Dmb commented on Oct 9, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Update 2026-10-09 — réponse du support Copernicus : refus, décision prise

    Le support Copernicus a répondu : refus explicite de la demande de quota. "every registered CDSE account is subject to the same quota limitations [...] we can't simply increase it per nature of the project." Les paliers de rate-limit sont fixes par compte, pas négociables au cas par cas. Cette piste est fermée.

    Deux alternatives envisagées pour éviter le run long ont été vérifiées puis écartées :

    • Changer d'API Sentinel Hub (Process, Catalog...) sur le même compte : le rate-limit est attaché au compte, pas à l'API appelée (confirmé par le support + par la doc technique) — on retomberait sur le même mur. Pire : la Statistical API déclenche en interne une sous-requête par date d'observation, donc structurellement plus coûteuse en quota que les autres pour un même volume de travail.
    • Une seule requête globale sur l'AOI au lieu d'une par cellule H3 : le coût en PU dépend de la surface totale, pas du nombre de requêtes (regrouper ne réduit rien). Et la Statistical API ne renvoie qu'un seul résultat agrégé par requête, quelle que soit la taille de la géométrie — une requête globale donnerait un NDVI unique pour toute l'Île-de-France, pas une valeur par zone H3. Casserait la fonctionnalité, pas qu'une question de perf.

    Décision retenue : on accepte la durée de run actuelle (~24-28h pour l'AOI complète en résolution 8) en attendant l'issue des candidatures en cours, plutôt que de changer d'architecture ou de dégrader la résolution pour contourner le problème.

    Pistes qui restent ouvertes :

    • Credits for Value Creators (recommandé par le support lui-même) — candidature à déposer sur creodias.eu/pricing/credits-for-value-creators, comité sous 30 jours.
    • Copernicus Services user — toujours en attente, indépendante de ce refus.

    Alternative identifiée mais non retenue pour l'instant : Google Earth Engine (seule option technique crédible hors écosystème Copernicus). Écartée pour l'instant — nouvelle auth/modèle de requête, clause non-commerciale des CGU à vérifier avant tout usage réel, perte du travail déjà stabilisé sur le client Sentinel Hub. À reconsidérer uniquement si les deux candidatures en cours échouent.

    Détail complet : ADR-003 mise à jour dans docs/architecture-decisions.md (branche feat/sentinel-hub-client), commit ed5c8ee.

  4. Nicolas-Dmb commented on Oct 9, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Update 2026-10-09 (suite) — "Copernicus Services user" confirmé fermé, décision CREODIAS

    Vérification plus poussée de l'éligibilité "Copernicus Services user" (FAQ officielle Copernicus) : réservé aux institutions opérant officiellement les Copernicus Services (les 6 services thématiques EU) — le dossier demande une référence de contrat Copernicus Services et un contact côté Commission Européenne. Pas ouvert à la recherche académique en général, contrairement à la première lecture du 06/10 (basée sur une liste de secteurs marketing trop large). Un projet étudiant comme BioWatch n'y est pas éligible. Piste fermée, confirmé par deux sources officielles distinctes.

    La doc officielle sur les quotas Copernicus confirme par ailleurs que pour un dépassement de quota Sentinel Hub spécifiquement, il n'existe que deux leviers : Credits for Value Creators (gratuit) ou accès commercial direct via CREODIAS (payant). Aucun autre levier gratuit n'existe dans l'écosystème Copernicus pour ce blocage précis — confirmé par la doc officielle elle-même.

    Décision prise : candidature à Credits for Value Creators, malgré la dépendance à une plateforme commerciale (CREODIAS) que ça introduit — seule option gratuite reconnue par Copernicus pour ce problème. Déposée sur creodias.eu/pricing/credits-for-value-creators, Sentinel Hub sélectionné.

    À vérifier dans la réponse du comité (sous 30 jours) : si ça lève bien le rate-limit par minute, pas juste le quota mensuel de PU.

    Détail complet : ADR-003, commit bf228be.

  5. Nicolas-Dmb commented on Oct 9, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Update 2026-10-09 (suite) — passage à une cadence annuelle

    Décision complémentaire à la demande CREODIAS : la cadence d'extraction passe de mensuelle à annuelle, sur une fenêtre temporelle courte et fixe répétée chaque année (pas une moyenne lissée sur 12 mois).

    Pourquoi :

    • Cohérence : toutes les autres sources sont déjà annuelles (update_gbif_occurrences, update_iucn_taxonomy, compute_species_features_by_zone, import OSM) — le satellite était la seule source mensuelle, une incohérence qu'on corrige.
    • Compatible avec le rate-limit actuel : à ~635 req/h, un run national (~840k cellules) prend ~55 jours — ne rentre pas dans un mois, rentre confortablement dans une année. Plus besoin de quota supplémentaire pour le run récurrent.
    • Fenêtre courte plutôt qu'agrégation P1Y pleine : délibéré — la Statistical API génère une sous-requête interne par date d'observation, donc une fenêtre courte reste moins coûteuse qu'une agrégation annuelle complète, et garde la comparabilité saisonnière (même période chaque année).

    Point explicitement laissé ouvert : la fenêtre de référence exacte (quel mois, quelle durée) est une décision écologique/produit, pas tranchée ici — à valider avec l'équipe avant implémentation.

    Ce qui ne change pas : le backfill historique (N années × ~840k cellules) reste la vraie justification de la demande CREODIAS en cours — la cadence annuelle résout le run récurrent, pas le chargement initial.

    Tickets Notion mis à jour : Job extract_sentinel_2 (bucket annuel) et 6. Industrialisation du calcul des scores (DoD "tous les deux mois" → "annuel"). Détail complet : ADR-003, commit 4772cc1.

  6. Nicolas-Dmb commented on Oct 9, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Update 2026-10-09 (suite) — fenêtre de référence annuelle tranchée : 1er-31 mai

    Point laissé ouvert plus tôt, maintenant décidé : la fenêtre annuelle est le mois complet de mai (pas une sous-période plus courte).

    Pourquoi mai : couvert nuageux moyen 45% en Île-de-France sur ce mois — point médian correct (vs 58-60% nov-janvier), végétation déjà active sans le stress hydrique de l'été. Mois complet plutôt qu'une fenêtre plus courte : maximise le nombre de scènes agrégées par cellule (signal plus robuste), au prix d'un coût en quota légèrement supérieur.

    Implémentation prévue : bucket_id reste au format annuel ("YYYY"), cohérent avec les autres sources. Le time_range envoyé à la Statistical API sera construit directement sur mai de cette année-là ({year}-05-01 → {year}-06-01, borne exclusive) plutôt que via get_bucket_range sur l'année complète.

    Détail : ADR-003, commit 6aef864. Code pas encore modifié — à faire dans une prochaine étape.

  7. Nicolas-Dmb commented on Oct 9, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Update 2026-10-09 (suite) — backfill fixé à 10 ans, objectif CREODIAS élargi

    Profondeur du backfill historique tranchée : 10 ans, alignée sur la fenêtre glissante déjà actée pour les espèces (update_gbif_occurrences : "pour l'année N, on recharge N-9 → N"). Volume total : 10 × ~840 000 cellules ≈ 8,4 millions de requêtes, en une fois.

    La demande CREODIAS vise maintenant à rendre chaque run (backfill et récurrent annuel) rapide (quelques jours, pas des semaines/mois) — pas juste un dimensionnement minimal qui fait tenir les chiffres dans le calendrier. Un run de ~55 jours reste opérationnellement risqué (VPS à maintenir en continu) même s'il tient dans l'année.

    Détail : ADR-003, commit 4dbcf3b.

  8. Nicolas-Dmb commented on Oct 9, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Update 2026-10-09 (suite) — ratio PU/requête mesuré, risque critique quantifié

    Export réel des stats d'usage Sentinel Hub (dashboard CDSE, 2 jours) : 802 requêtes pour 30 PU, ratio ~0,0374 PU/requête. Permet de chiffrer : backfill 10 ans ≈ 314 000 PU (one-shot), récurrent annuel ≈ 31 400 PU/an. Demande CREODIAS dimensionnée à ~400 000 PU.

    Risque critique identifié : le mur de rate-limit déjà observé (429 après ~24 requêtes) n'a consommé que ~0,9 PU — on était loin d'épuiser un quota mensuel. Le vrai plafond tapé est un débit par minute, mécanisme indépendant du solde total de PU. Les crédits CREODIAS sont un solde à consommer (comme un forfait prépayé), pas un abonnement qui relève nécessairement ce débit.

    Conséquence : si l'octroi de crédits n'augmente pas explicitement le débit par minute, un solde plus gros ne change rien à la vitesse — toujours ~551 jours pour consommer 8,4M requêtes au débit actuel, quel que soit le solde de PU accordé. À clarifier explicitement avec CREODIAS avant de considérer cette demande comme une vraie solution.

    Détail : ADR-003, commit ff65855.

  9. Nicolas-Dmb commented on Oct 23, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Changement d'approche : ADR-004 (remplace ADR-003)

    La Statistical API Sentinel Hub est abandonnée comme source principale : le rate-limit par compte (24 h à 28 h pour un mois IDF), le refus du support et l'incertitude CREODIAS la rendent inexploitable. On calcule désormais les indices localement, à partir des COG Sentinel-2 L2A publics.

    Décisions

    • Source unique : Planetary Computer (sentinel-2-l2a), jamais mélangée avec une autre.
    • Cadence mensuelle (bucket_id YYYY-MM), historique depuis janvier 2016.
    • Masque SCL : on garde {4, 5, 6}, avec une médiane mensuelle par pixel, une agrégation H3 res 8 par centre de pixel et une tuile propriétaire par cellule.
    • On garde l'existant : zones_hex, satellite_features_by_zone (nouvelle source_version), job_run / job_run_zone_errors, CLI biowatch-jobs.
    • Backfill hors VPS (Mac / machine temporaire), mensuel sur le VPS.

    Mesures du spike (IDF, H3 res 8, Mac)

    Planetary Computer vs Earth Search (même tuile / mois) 20 s vs 679 s (Earth Search ~35× plus lent depuis l'Europe)
    Historique homogène Earth Search C1 complet seulement depuis 04/2019
    IDF complète, mai 2025 12 min (4 blocs) / 18 min et 1,8 Go RAM (config VPS) ; 18 307 cellules, 0 vide
    Écart vs série homogène ESA (mai 2021) NDVI −0,01, corrélation 0,97 → petite marche possible début 2022
    SCL 7 à Paris 0,3 % des pixels → exclusion sans risque

    Détail complet dans docs/architecture-decisions.md (ADR-004), branche feat/sentinel2-cog-extraction.
    Reste à faire : comparer avec les valeurs Sentinel Hub déjà en base.

  10. Nicolas-Dmb commented on Oct 23, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Implémentation terminée (phases 2 à 4) : feat/sentinel2-cog-extraction

    Le job extract_sentinel est mensuel et calcule les indices localement depuis les images Planetary Computer (ADR-004).

    uv run biowatch-jobs extract_sentinel --aoi idf --bucket_id 2025-05   # défaut : mois précédent
    uv run biowatch-jobs backfill_sentinel --aoi idf --from 2016-01 --to 2026-09
    • Une ligne par cellule et par mois dans satellite_features_by_zone (source_version 2.0.0). Les cellules sans image exploitable sont écrites avec NULL et is_valid_data = false.
    • obs_count = dates claires par pixel ; cloud_score = part des dates masquées.
    • Une tuile illisible → job_run_zone_errors pour ses cellules (run partial).
    • Backfill : reprise automatique par idempotence, exécuté hors VPS via tunnel SSH (voir README).
    • Client Sentinel Hub supprimé.
    • Tests : 329 tests, 100 % de coverage sur les nouveaux modules, lint/mypy verts. Run réel vérifié sur 61 cellules de Paris (3,8 s, valeurs cohérentes avec le spike).
    • Doc : README, ADR-004, shared-functions.md, Notion.

    Reste à faire : un run complet sur une vraie base (IDF, un mois) et la comparaison avec les valeurs Sentinel Hub déjà en base.

  11. Nicolas-Dmb commented on Nov 6, 2026

    @Nicolas-Dmb
    CollaboratorAuthor

    Mise à jour des critères suite à l'ADR-004 (implémentés dans #99)

    Le passage de la Statistical API Sentinel Hub au calcul local depuis Planetary Computer (ADR-004) rend trois passages de cette issue obsolètes. Ce qui fait foi désormais :

    Passage de l'issue Devient
    Description : « imagerie Sentinel-2 (API Copernicus) … pipeline direct API → PostGIS » Indices calculés localement à partir des images Sentinel-2 L2A de Planetary Computer (COG, sans compte ni clé), puis UPSERT dans satellite_features_by_zone
    Critère : « Retry backoff exponentiel sur 429, parallélisme configurable » Retry réseau : timeouts et retries HTTP (GDAL), retry par bloc de pixels. Parallélisme configurable : --io_threads. (Le 429 était le rate-limit propre à Sentinel Hub, qui n'est plus utilisé.)
    Prérequis externe : « credentials API Copernicus (Secrets Manager), infra d'exécution AWS » Aucun credential : Planetary Computer est public. Exécution sur le VPS (systemd timer, infra/vps/systemd/)

    Critère ajouté : backfill_sentinel (historique depuis 2016), avec reprise par idempotence. Les mois à relancer (en échec ou incomplets) sont listés, avec la commande --months pour ne relancer qu'eux.

    Les autres critères restent valables tels quels et sont couverts par #99 : cadence mensuelle, indices, obs_count / valid_pixel_ratio / cloud_score, UPSERT idempotent, job_run_zone_errors si partial, 100 % de tests sur les nouveaux modules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions