You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: implement flood calibration and prediction tasks
- Add flood calibration task to re-learn link models from historical river levels.
- Introduce flood prediction task to evaluate calibrated links and send alerts based on upstream readings.
- Create utility functions for flood prediction, including model calibration and exceedance event detection.
- Implement river station utilities for parsing sensor data and calculating distances.
- Add tests for flood calibration, prediction, river levels, and utility functions to ensure functionality and reliability.
-**Feature:** Monitoring of water levels at configured hydrometric stations.
26
-
-**Details:** Periodically polls the Allerta Meteo time-series endpoint for each registered station and compares the latest reading against operator-defined thresholds (`soglia1`, `soglia2`, `soglia3`). Station metadata and thresholds live in the `rivers` table; each check is appended to `river_levels`.
26
+
-**Details:** Periodically polls the Allerta Meteo time-series endpoint for each registered station and compares the latest reading against operator-defined thresholds (`soglia1`, `soglia2`, `soglia3`). Station metadata and thresholds live in the `rivers` table; each new reading is appended to `river_levels` (de-duplicated by measurement time). A hysteresis deadband (`RIVER_THRESHOLD_MARGIN`, default 0.05 m) avoids alert flapping when a level hovers around a threshold.
27
27
-**Notification:** Sends a Telegram message only on crossing events — when a reading rises above or falls below a threshold relative to the previous check.
-**Feature:** Warns that a downstream point of interest is likely to exceed a threshold, with an estimated lead time, based on what upstream gauges are doing now.
31
+
-**Details:** For each configured `river_link` (an upstream gauge → a downstream point), a daily calibration mines the accumulated `river_levels` history for past downstream exceedances and learns, from those events only, the upstream `precursor_level` and the typical `lead_time`. A link stays inactive until it has enough historical events. Online, when the upstream gauge reaches the learned level while rising, a single prediction is emitted and recorded in `link_predictions` for later scoring. No AI — event detection plus robust statistics.
32
+
-**Bootstrap:** Since the live API only retains ~2.4 days, historical data is backfilled from the free ARPAE-SIMC open archive (see below). Without a backfill the model simply stays dormant until enough live events accrue.
33
+
29
34
## Scheduled Tasks (Crons)
30
35
31
36
The application relies on scheduled tasks (crons) to automate the weather monitoring flow:
@@ -38,6 +43,10 @@ The application relies on scheduled tasks (crons) to automate the weather monito
38
43
- Verifies and sends the Estofex map for the following day, following the same conditional logic based on ongoing alerts.
39
44
-**River Levels Check**
40
45
- Every 5 minutes, for each row in the `rivers` table, fetches the latest hydrometric reading and appends it to `river_levels`. Sends a Telegram message only when the reading crosses one of the configured thresholds since the previous check.
46
+
-**Flood Prediction Check**
47
+
- Every 5 minutes, evaluates each calibrated `river_link`: if the upstream gauge has reached its learned precursor level while rising, emits a single de-duplicated flood-arrival prediction.
48
+
-**Flood Calibration**
49
+
- Daily, re-learns every link's model (lead time + precursor level) from the accumulated `river_levels` history. Also runnable on demand after a backfill.
41
50
42
51
## Database bootstrap
43
52
@@ -55,9 +64,29 @@ Stations are managed via HTTP (port 3000):
-`GET /rivers/nearest?lat=&lon=&limit=` — discovery: nearest stations to a point, with coordinates and the official soglie the Allerta sensor list reports (suggestions only)
69
+
70
+
The `station_id` is the Allerta Meteo `idstazione`; threshold values are operator-defined (the `nearest` endpoint surfaces the portal's official soglie as suggestions, but they are not auto-applied).
Both ends of a link must be registered as `rivers` so their readings accumulate in `river_levels`.
59
75
60
-
The `station_id` is the Allerta Meteo `idstazione`; threshold values must be looked up manually from the [Allerta Meteo portal](https://allertameteo.regione.emilia-romagna.it/) and stored alongside the station.
76
+
-`GET /river-links` — list links and their learned models
-`POST /flood-calibration` — re-learn all link models from history (returns `{ calibrated, active, skipped }`)
80
+
-`POST /flood-prediction` — evaluate all links now (returns `{ evaluated, predictions, skipped }`)
81
+
-`POST /flood-backfill` — body `{ from: "YYYY-MM", to: "YYYY-MM" }`; backfills `river_levels` from the ARPAE-SIMC open archive (`https://dati-simc.arpae.it/opendata/osservati/meteo/storico/`) for every registered station. Returns `202` immediately and runs in the background (it streams ~20 MB/month). Run it once, then `POST /flood-calibration`.
82
+
83
+
Example bootstrap for the Molinella stations (Idice S. Antonio + Reno Gandazzolo and their upstream gauges):
84
+
85
+
```bash
86
+
# register stations, link them, then:
87
+
curl -X POST localhost:3000/flood-backfill -H 'content-type: application/json' -d '{"from":"2020-01","to":"2024-12"}'
0 commit comments