ST_Dump sur la dernière observation → 5 patches les plus proches dans 200km, exposés
dans /spots/{id}/score. SpotPanel affiche le nombre de patches et la distance+surface
de chacun sous forme de barres (échelle 0–100km).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Expose breakdown {distance, density, velocity, trend} dans l'API /spots/{id}/score
(pour le score courant reconstruit depuis les champs stockés, et pour chaque horizon
avec confidence appliquée). Affiche les barres avec valeur/max dans le SpotPanel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- OceanCurrentClient : requête ERDDAP/OSCAR sans clé API, JSON direct
Résolution 1/3°, snap au point de grille le plus proche
Fallback gracieux (wind-only) si OSCAR indisponible
- DriftSimulationService v3 : déplacement total = vent Stokes (3%) + courant OSCAR (100%)
Physique correcte : les sargasses dérivent avec les courants de surface,
pas uniquement sous l'effet du vent
MODEL_VERSION 2.0.0 → 3.0.0
- Courant Caraïbe typique ~0.3–0.5 m/s (courant des Antilles NW) — contribution
majeure sur 24-48h vs vent Stokes seul (~0.15-0.25 m/s)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Symfony Scheduler (MainSchedule) : cron 0,6,12,18h UTC
- IngestionScheduleMessage + IngestionScheduleHandler : pipeline complet
ingestion → forecasts → impact scores pour toutes les zones Antilles
- messenger.yaml : transport scheduler_main + async Redis (rs971_async)
- entrypoint.sh : 2 workers en background (scheduler_main + async)
- GET /api/status : date/heure de la dernière observation
- SargassesMap : badge "Données du JJ/MM à HH:MM" en bas à gauche
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Avant : horizons = (1 - dist/50km) × 100 × confidence — échelle incomparable
avec le score now qui utilise distance(40) + densité(30) + vitesse(20) + tendance(10).
Après : même décomposition pour les 4 horizons, en passant currentDistanceKm
pour calculer la vitesse d'approche réelle par rapport à la position courante.
La confiance s'applique sur le score total (incertitude du modèle).
Résultat : si la sargasse s'éloigne à H+6, le score baisse. Si elle approche,
le score monte — mais atténué par la confiance du modèle (0.85 à H+6).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sans pondération, H+6 pouvait afficher 67 quand "now" était 34 car les
deux formules n'ont pas la même échelle. Multiplier par confidence (0.85
à H+6, 0.45 à H+48) rend les scores cohérents entre horizons : un risque
ne peut pas paraître croître si c'est juste l'incertitude qui augmente.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1. DriftSimulationService : suppression ST_Buffer(::geography) — produisait des
géométries invalides/dégénérées sur grand MULTIPOLYGON → ST_Distance retournait
652 029km. Remplacé par ST_Translate + ST_SimplifyPreserveTopology uniquement.
2. ImpactScoreService : filtre MAX_RELEVANT_M (300km) — une observation distante
ne remplace plus le score d'un spot proche. Les scores existants sont préservés
si l'observation est hors zone pertinente.
3. ComputeForecastsCommand --force : suppression du DELETE FROM impact_score.
On ne purge plus les scores existants ; computeForObservation ajoute les
nouveaux scores, les anciens restent valides pour les spots éloignés.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
L'ancienne approche (ST_GeneratePoints × 40 + ST_ConcaveHull) avait un défaut
majeur : les points aléatoires pouvaient ne pas couvrir les patches locaux
(ex: le patch à 3.61km de Barbados), donnant des distances absurdes à H+6 (654km).
Nouvelle approche :
- ST_Translate(geometry, dx_deg, dy_deg) : translation directe du MULTIPOLYGON
source → chaque patch individuel se déplace correctement
- ST_Buffer(... DIFFUSION_M × √horizon) : diffusion turbulente via dilatation
(H+6 ≈ +3.7km, H+48 ≈ +10.4km) — formes qui s'élargissent sans perdre la structure
- Déterministe : pas d'aléatoire, distances ST_Distance fiables
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DriftSimulationService : ajout diffusion turbulente 600m/h (marche aléatoire
indépendante par point) → les patches se dispersent et déforment au fil des horizons
au lieu d'une simple translation rigide avec les alizés constants
- SAMPLE_POINTS 30→40, CONCAVITY 0.8→0.7, MODEL_VERSION 1.0→1.1
- ComputeForecastsCommand : option --force pour supprimer et recalculer les
forecasts existants (utile après changement de modèle)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ST_Collect peut retourner une GeometryCollection selon les sous-types.
Correction : ST_CollectionExtract(..., 3) extrait uniquement les polygones,
ST_Multi garantit le type MultiPolygon, ST_GeomFromText fournit un fallback
vide typé correctement.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Problème : le threshold AFAI 0.005 (S2) / MCI 0.008 (S3) trop bas
classifie la majorité des pixels océan comme sargasse → gros rectangle
orange sur des zones sans sargasses.
Corrections :
- S2 AFAI threshold 0.005 → 0.015 (littérature : Hu 2009, Wang & Hu 2016)
- S3 MCI threshold 0.008 → 0.015
- Ajout protection sun glint : si NIR > 0.08 → pixel rejeté (S2 + S3)
- Division NDWI sécurisée (dénominateur = 0 → return 0)
- gdal_polygonize avec -mask (gdal_calc.py) : seuls les pixels DN=1
sont vectorisés, le reste n'existe pas dans le GeoJSON
- Filtre MIN_RING_POINTS=8 : supprime les polygones < ~2px (bruit)
- PROCESSING_VERSION 1.0.0 → 1.1.0 pour traçabilité
⚠️ Les observations en base avec processing_version='1.0.0' sont
faussées — les supprimer avant de relancer l'ingestion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Les dots couvraient tout le tile Sentinel (pas seulement les pixels
sargasses), saturant la vue. Fill opacity réduit à 0.5 pour laisser
respirer la carte sous-jacente.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MapLibre considère null comme "propriété présente" → le filtre !has
ne prenait pas le relais → tous les polygones étaient masqués.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Passe afaiIndex en propriété GeoJSON et applique un filtre MapLibre
sur les layers observation : seules les features avec afai > 0 sont
affichées. Fallback : si la propriété est absente, affichage normal.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
symfony new crée son propre .git — supprimé pour intégrer
backend/ comme dossier ordinaire dans le monorepo.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>