Gwadaking
1429eaa71c
fix: DriftSimulationService v2 — ST_Translate au lieu de sample+ConcaveHull
...
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 >
2026-04-03 20:14:22 -04:00
Gwadaking
fb25142bad
feat: diffusion turbulente + recalcul forcé — polygones qui évoluent dans le temps
...
- 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 >
2026-04-03 20:03:51 -04:00
Gwadaking
492ed02b6f
feat: ForecastController avec filtres bbox+horizon — polygones par horizon sur la carte
2026-04-03 19:38:22 -04:00
Gwadaking
878174404e
fix: calculer ETA depuis horizons API — évite dépendance aux données stale en base
2026-04-03 19:35:21 -04:00
Gwadaking
22a3ce23a4
fix: corriger null paint et line-dasharray invalide dans cartoonizeStyle
2026-04-03 19:27:15 -04:00
Gwadaking
744f76f85e
feat: ETA côtier — transformer la map en outil de décision actionnable
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-03 18:42:21 -04:00
Gwadaking
00996cb163
fix: monter PHPStan à niveau 8 — corriger 2 bugs file_get_contents/json_encode
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-03 17:08:45 -04:00
Gwadaking
0d323b871b
add trend UX + PHPStan level 6 clean (0 errors)
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-03 16:06:13 -04:00
Gwadaking
997e2d186c
fix: GeometryCollection incompatible avec colonne MultiPolygon
...
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 >
2026-04-02 17:06:42 -04:00
Gwadaking
6752b6df0b
fix: durcir les filtres AFAI — SWIR guard, NDWI marge côtière, filtre PostGIS 1km²
...
- S2 : ajout garde SWIR (B11 > 0.03 = artifact atmo/cirrus), NIR abaissé
à 0.06, NDWI marge côtière (> 0.05), AFAI threshold 0.020
- S3 : même logique, NIR 0.06, NDWI 0.05, MCI 0.020
- PostGIS : suppression des polygones < 1 km² après vectorisation
(résidus ponctuels = bruit ou artefact atmosphérique en Caraïbe)
- Auto-suppression de l'observation si plus aucun polygone valide
- PROCESSING_VERSION 1.2.0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-02 17:04:20 -04:00
Gwadaking
edca50200e
fix: corriger les faux positifs AFAI — seuils, masque gdal, filtre bruit
...
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 >
2026-04-02 16:52:28 -04:00
Gwadaking
8a60c783b1
fix: supprimer le pattern dots, réduire opacité observations
...
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 >
2026-04-02 16:42:33 -04:00
Gwadaking
6bf6762dac
fix: ne pas injecter afai:null dans les propriétés GeoJSON
...
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 >
2026-04-02 16:36:57 -04:00
Gwadaking
3f1e49bb3b
fix: masquer les zones d'observation sans sargasses (afaiIndex = 0)
...
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 >
2026-04-02 16:21:15 -04:00
Gwadaking
7169dffc67
feat: refonte visuelle cartoon — fond OpenFreeMap, marqueurs SVG, légende comic, SpotPanel clair
...
- Fond cartoon : OpenFreeMap bright transformé (îles #7ec850, océan #3ab5e6, bordure #4a7c2f)
- Overlay vagues SVG subtil sur toute la carte (opacity 6%)
- Marqueurs spots SVG expressifs : ★ (ok), ! (risque), ✕ (impact) — animation bounce sur impact
- Sargasses #f4a020 avec texture dots MapLibre + contour épais 3.5px
- Légende comic : fond #fffbf0, bordure 3px, ombre dure 4px 4px 0 #333 , Fredoka One
- SpotPanel blanc cassé avec ombre dure cartoon, gauge coloré pastel (vert/jaune/rouge)
- Topbar #1a1a2e avec boutons bleus ombre dure + hover translate(-2px,-2px)
- Timeline : step actif avec ombre dure cartoon
- Police Fredoka One chargée via Google Fonts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-02 15:25:17 -04:00
Gwadaking
1078a3459b
fix: supprimer le width 1126px du #root qui bridait la map en desktop
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-02 15:05:13 -04:00
Gwadaking
6e6df9fd89
build: bundle frontend assets
...
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-02 14:42:46 -04:00
Gwadaking
d4c513e5e5
build: bundle frontend assets
2026-04-01 23:39:52 -04:00
Gwadaking
438a1fc392
fix: add NDWI water mask to evalscripts (S2 + S3) to exclude land pixels
2026-04-01 23:21:19 -04:00
Gwadaking
f0f28dca47
fix: json_encode geometry array before persisting SargassumObservation
2026-04-01 23:08:06 -04:00
Gwadaking
1d03698888
fix: remove unsupported sortby from CDSE catalog request
2026-04-01 23:06:29 -04:00
Gwadaking
1485339aec
fix: expose lat/lng from CoastalPoint, fix getCoords in map
2026-04-01 22:37:14 -04:00
Gwadaking
15079ea7b4
fix: enable application/json format in API Platform
2026-04-01 22:14:55 -04:00
Gwadaking
6cbd190ba0
fix: disable transaction on initial migration (CONCURRENTLY indexes)
2026-04-01 21:07:12 -04:00
Gwadaking
c02b58c3ce
fix: remove unused frontend/dist volume, commit built assets
2026-04-01 20:57:28 -04:00
Gwadaking
77be044e3f
fix: commit Symfony .env defaults, fix gitignore, rename to CLAUDE.md
2026-04-01 20:48:40 -04:00
Gwadaking
1474b14e35
fix: use traefik-public network name
2026-04-01 19:56:44 -04:00
Gwadaking
2cef3725e9
feat: Phase 3 complete — push alerts, feedback scoring, S3 fallback, SPA routing, mobile CSS
...
- ImpactScoreService: implement getFeedbackBonus() (ST_Distance query on UserFeedback within 20km/6h)
- ImpactScoreService: trigger PushNotificationService when score >= 70
- PushNotificationService: send VAPID WebPush to spot subscribers, clean expired subs
- PushController: GET vapid-public-key, POST/DELETE subscribe with rate limiting
- SentinelHubClient: add optional $collection param, add Sentinel-3 OLCI FAI evalscript (MCI)
- IngestionService: add $collection param + HighCloudCoverageException for fallback logic
- IngestionService: add ingestWithFallback() — tries S2, falls back to S3 on high cloud
- IngestSentinelCommand: --source=auto (default) triggers ingestWithFallback
- FeedbackController: rate limiting via apiFeedbackLimiter
- Migration: push_subscription table
- rate_limiter.yaml: api_read(120/min), api_feedback(10/min), api_push(5/min)
- sw.js: service worker handling push events + notificationclick
- usePushSubscription hook: subscribe/unsubscribe lifecycle with VAPID
- SpotPanel: PushButton component integrated
- SpotPanel.css + TimelineSlider.css: mobile responsive (bottom-sheet on small screens)
- Caddyfile: SPA served at / with try_files fallback, sw.js served from root scope
- vite.config.js: build outDir → backend/public (not /spa)
- deploy/post-receive.sh: full deploy script (composer, npm build, migrations, cache, docker up)
- docs/roadmap.md: all Phase 3 + transversal items marked done
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 03:43:56 -04:00
Gwadaking
94bb6f5e8c
Phase 2 : dérive, ImpactScore, slider temporel
...
Backend :
- OpenMeteoClient : vent horaire 72h (gratuit, sans clé)
- DriftSimulationService : échantillonnage ST_GeneratePoints,
déplacement itératif Stokes 3%, reconstruction ST_ConcaveHull,
4 horizons H+6/12/24/48, confiance décroissante
- ImpactScoreService : score 4 composantes (distance/densité/
vitesse/tendance), cache Redis TTL 3h, invalidation à chaque calcul
- ComputeForecastsCommand : traite les observations sans forecast
- SpotScoreController : score courant + horizons depuis forecasts
Frontend :
- TimelineSlider : navigation Now/+6h/+12h/+24h/+48h
- SargassesMap : couche observations (orange) vs forecasts (violet)
rechargée à chaque changement d'étape
- SpotPanel : affichage score par horizon actif + indicateur confiance
- Build → backend/public/spa/
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 03:23:21 -04:00
Gwadaking
0c1deb93cb
Phase 1 : seed CoastalPoints + frontend React complet
...
Backend :
- SeedCoastalPointsCommand : 29 points Antilles (idempotent)
Martinique, Guadeloupe, Sainte-Lucie, Barbade, Saint-Martin
Frontend :
- api/client.js : wrapper fetch pour spots, observations, feedback
- SargassesMap : MapLibre GL JS, marqueurs spots, couche observations
GeoJSON avec rechargement bbox dynamique à chaque mouvement carte
- SpotPanel : Spot Mode (score/niveau/distance/trend), bouton feedback
terrain anonyme, gestion état loading/error/null
- App.jsx : composition map + panel, état selectedSpot
- vite.config.js : proxy /api → Symfony dev, build → backend/public/spa
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 03:17:43 -04:00
Gwadaking
4af5e62465
Phase 1 : endpoints API complets
...
- CoastalPoint → shortName Spot → /api/spots (API Platform)
- SargassumForecast → shortName Forecast → /api/forecasts
- ObservationController : GET /api/observations avec filtres
bbox (PostGIS ST_Intersects), date, source + geometry ST_AsGeoJSON
- SpotScoreController : GET /api/spots/{id}/score avec ?at= optionnel
- FeedbackController : POST /api/feedback, anonyme, fingerprint SHA256
- CORS : autorisation radarsargasses971.com + localhost dev
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 02:50:06 -04:00
Gwadaking
bf64340525
Phase 1 : pipeline d'ingestion Sentinel-2
...
- SentinelHubClient : auth OAuth2, Catalog API (cloud coverage),
Process API avec evalscript AFAI binaire (UINT8, cloud-side)
- IngestionService : orchestration complète — job tracking,
rejet nuages, téléchargement GeoTIFF, polygonize GDAL,
simplification PostGIS, persistance SargassumObservation
- IngestSentinelCommand : 5 zones Antilles configurées,
options --date et --zone, appelable via cron docker exec
- Dockerfile : ajout gdal-bin + python3-gdal
- Correction entity : paramètre immutable retiré de #[ORM\Column]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 02:45:12 -04:00
Gwadaking
e5e729c487
Phase 0 : entités, migrations, API Platform, PostGIS
...
- API Platform + Doctrine ORM + jsor/doctrine-postgis installés
- 6 entités Symfony (SargassumObservation, SargassumForecast,
DataIngestionJob, CoastalPoint, ImpactScore, UserFeedback)
- Migration initiale manuelle avec CREATE EXTENSION postgis,
toutes les tables, index GIST et BTREE
- doctrine.yaml configuré pour PostgreSQL 16 + types PostGIS
- Roadmap Phase 0 complète
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 02:37:27 -04:00
Gwadaking
34e9675350
Fix : backend intégré en monorepo (suppression git imbriqué)
...
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 >
2026-04-01 02:23:26 -04:00
Gwadaking
f685c96ed6
Phase 0 : scaffolding complet
...
- Structure monorepo (backend Symfony, frontend React/Vite)
- Docker : FrankenPHP + PostgreSQL/PostGIS + Redis + Traefik labels
- Caddyfile, Dockerfile, .env.example, .gitignore
- MapLibre GL JS + react-map-gl installés
- Roadmap mise à jour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-04-01 02:23:10 -04:00