# DATA MODEL — Sargasse-Sentry

## Principe fondamental

Séparer strictement :
- **Observation** — ce qui a été détecté (réel)
- **Forecast** — ce qui est simulé (prédiction)
- **Job** — traçabilité du pipeline
- **Score** — agrégat calculé pour un point côtier
- **Feedback** — retour terrain anonyme

---

## Entité : SargassumObservation

Représente une détection satellite réelle.

| Champ | Type | Notes |
|---|---|---|
| `id` | UUID | PK |
| `geometry` | MULTIPOLYGON (SRID 4326) | Polygones détectés |
| `bbox` | GEOMETRY BOX | Bounding box — requêtes spatiales légères |
| `detectedAt` | datetime UTC | Immutable |
| `source` | string | `Sentinel-2` / `Sentinel-3` |
| `tileId` | string | Identifiant tuile Sentinel |
| `cloudCoverage` | float 0–100 | % couverture nuageuse |
| `confidence` | float 0–1 | Indice de confiance détection |
| `afaiMean` | float | Valeur AFAI moyenne |
| `afaiStd` | float | Écart-type AFAI |
| `coverageArea` | float (km²) | Surface totale détectée |
| `processingVersion` | string | Version de l'algorithme AFAI utilisé |
| `createdAt` | datetime | |

**Index :**
- `GIST (geometry)`
- `GIST (bbox)`
- `BTREE (detectedAt)`
- `BTREE (source)`

---

## Entité : SargassumForecast

Projection issue du modèle de dérive.

| Champ | Type | Notes |
|---|---|---|
| `id` | UUID | PK |
| `sourceObservation` | FK → SargassumObservation | |
| `geometry` | MULTIPOLYGON (SRID 4326) | Polygone prédit |
| `validAt` | datetime UTC | Moment auquel la prédiction est valide |
| `computedAt` | datetime | Moment du calcul |
| `modelVersion` | string | Version du modèle de dérive |
| `timeHorizon` | int (heures) | `6 / 12 / 24 / 48` |
| `confidence` | float 0–1 | |
| `driftVectorAvg` | GEOMETRY(POINT, 4326) | Vecteur de dérive moyen (PostGIS natif) |
| `createdAt` | datetime | |

**Index :**
- `GIST (geometry)`
- `BTREE (validAt)`
- `BTREE (timeHorizon)`

---

## Entité : DataIngestionJob

Traçabilité du pipeline — robustesse obligatoire.

| Champ | Type | Notes |
|---|---|---|
| `id` | UUID | PK |
| `source` | string | `Sentinel-2` / `Sentinel-3` |
| `tileId` | string | |
| `requestedAt` | datetime | |
| `processedAt` | datetime (nullable) | |
| `status` | enum | `pending / success / failed` |
| `retryCount` | int | |
| `errorMessage` | string (nullable) | |

---

## Entité : CoastalPoint

Points d'intérêt utilisateur.

| Champ | Type | Notes |
|---|---|---|
| `id` | UUID | PK |
| `name` | string | |
| `geometry` | POINT (SRID 4326) | |
| `type` | enum | `beach / port / surf / fishing` |
| `region` | string | |

---

## Entité : ImpactScore

Score calculé pour un point côtier à un instant donné.

| Champ | Type | Notes |
|---|---|---|
| `id` | UUID | PK |
| `coastalPoint` | FK → CoastalPoint | |
| `timestamp` | datetime | |
| `score` | int 0–100 | |
| `level` | enum | `low / medium / high` |
| `distanceToNearestSargassum` | float (km) | |
| `densityEstimate` | float | |
| `trend` | enum | `increasing / stable / decreasing` |

---

## Entité : UserFeedback

Retour terrain anonyme — alimente la boucle d'apprentissage.

| Champ | Type | Notes |
|---|---|---|
| `id` | UUID | PK |
| `location` | POINT (SRID 4326) | Localisation signalée |
| `coastalPoint` | FK → CoastalPoint (nullable) | Si associé à un point connu |
| `timestamp` | datetime | |
| `hasSeaweed` | bool | Présence confirmée ou infirmée |
| `density` | enum (nullable) | `low / medium / high` |
| `source` | string | Fingerprint anonyme (IP hachée) — pas de compte |

---

## Schéma des relations

```
SargassumObservation
    └── SargassumForecast (N)

CoastalPoint
    └── ImpactScore (N)
    └── UserFeedback (N, nullable)

DataIngestionJob (indépendant, audit trail)
```
