banyaro/docs/DWD_RAIN_FORECAST_PLAN.md

78 lines
5.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DWD Regen-Vorhersage (Radar-Nowcast) — Scoping-Plan
**Status:** UMGESETZT auf Staging (v1240, 2026-06-08) — PoC bestanden, Pipeline + Frontend live.
**Offen:** DSM-Aufgabenplaner-Cron (alle 5 Min, Staging + Prod) anlegen; Gerätetest; Prod-Deploy.
## Umsetzung (2026-06-08)
- **PoC Georeferenzierung BESTANDEN** (`tools/dwd-radar/poc/`): DE1200 = polar-stereografisch auf
WGS84-Ellipsoid (wradlib-Parameter, False Easting/Northing 543196.835/3622588.862 → LL-Ecke (0,0);
in GDAL-Konvention y südwärts negativ, Gitter y ∈ [-1200000, 0]). Anker (9°E, 51°N) = Mitte von
Pixel (470/600) ✓, Ecken decken sich mit der DE1200-Spec ✓.
- **Pipeline** `tools/dwd-radar/make_radar_tiles.py` (Container: GDAL + go-pmtiles): RV-Komposit →
je Frame dekodieren → RGBA-Farbskala (transparent < 0,05 mm/5min) Warp 3857 MBTiles z7-Basis
+ Overviews bis z4 PMTiles. 25 Frames 14 s (Mac) / ~30 s (DS). Manifest + atomarer Swap
(`run-<id>/`), KEEP_RUNS=2. `docker-compose.dwd.yml` **DSM-Aufgabenplaner ALLE 5 MIN**:
`cd <pfad> && docker compose -f docker-compose.dwd.yml run --rm dwd-radar` (⚠ NIE --remove-orphans).
- **Serving** main.py: `/radar/manifest.json` (no-store) + `/radar/{run}/{file}` (Range/206,
immutable Run-Id im Pfad). sw.js: `/radar/` Pass-through.
- **Frontend** map.js: Radar-Frames heterogen (`{url, time, dwd}`) DWD ersetzt den RainViewer-
Nowcast (0120 min) wenn: Toggle an + GL-Modus + Kartenmitte in DE1200-Bbox + Manifest frisch
(< 30 min). Sonst RainViewer-Fallback (auch außerhalb DE / offline / DWD-Ausfall). DWD-Frames
als `pmtiles://`-Raster-Template über das vorhandene Protokoll; Label „+X Min · DWD".
- **Settings-Toggle** DWD-Regenvorhersage" (`by_dwd_radar`, Default AN), settings.js.
(Ursprüngliches Scoping:)
~~**Status:** gescoppt + Datenformat verifiziert (2026-06-05). Umsetzung offen.~~
**Ziel:** Verlässliche, längere Regen-**Vorhersage** als animiertes Karten-Overlay (bis +2 h) statt RainViewers
unzuverlässigem 30-Min-Nowcast (der oft leer ist). Self-hosted wie die Basemap passt zur Tile-Server-Philosophie.
## Quelle: DWD RV (Composite RV) — kostenlos, kein API-Key
- `https://opendata.dwd.de/weather/radar/composite/rv/DE1200_RV<YYMMDDHHMM>.tar.bz2`
- **Alle 5 Min** publiziert. Jedes Archiv = ein Vorhersage-Lauf mit **25 Frames** `_000``_120`
(**0 bis +120 Min**, 5-Min-Schritte), je ~2,5 MB unkomprimiert (~1 MB als .tar.bz2).
- **Format (verifiziert):** RADOLAN-Binär. 194-Byte-ASCII-Header bis `ETX (0x03)`, dann **1200×1100 uint16
little-endian** (= 2.640.000 Byte). Header-Felder: `PR E-02` (0,01 mm), `INT 5` (5-Min-Summe),
`GP1200x1100`, `VV<lead>` (Lead-Time). **Wert = `raw & 0x0FFF` × 0,01 mm/5min; `raw & 0x2000` = kein Daten.**
Decode trivial, **kein wradlib nötig** (PoC: 1,32 Mio Zellen geparst, Regen korrekt erkannt).
- **Gitter/Projektion:** DE1200 (1 km), polar-stereografisch, fest georeferenziert (Eckkoordinaten dokumentiert;
wradlib `get_radolan_grid` ODER GDAL mit dem bekannten RADOLAN-PROJ-String).
- **Abdeckung:** Deutschland + Randbereiche (reicht etwas nach AT/CH/Nachbarn, aber DE-zentriert).
Voll-AT/CH bräuchte ACG/MeteoSwiss out of scope.
## Pipeline (Server-seitig, Cron alle 5 Min — analog zum OSM-POI-Job)
1. **Fetch** neueste `DE1200_RV<time>.tar.bz2` (Verzeichnis-Listing letzte Datei).
2. **Entpacken** 25 RADOLAN-Grids.
3. **Decode** je Grid 2D-Niederschlags-Array (eigener ~15-Z.-Parser, PoC-bewiesen).
4. **Kolorieren** RGBA (transparent bei 0/kein-Regen; Radar-Farbskala wie das aktuelle Overlay).
5. **Reprojektion** DE1200 EPSG:3857 + **Kacheln z09** (Radar ist grob 1 km höher zoomen bringt nichts).
Tooling: **GDAL** (gdalwarp + gdal2tiles) oder rasterio/rio-tiler.
6. **Output:** 25 Frame-Tilesets je eine kleine **PMTiles** pro Lead-Time (`rv_000.pmtiles``rv_120.pmtiles`)
ODER XYZ-PNG. Nur neuesten Lauf behalten (atomarer Swap wie dach.pmtiles). Plus **`rv_manifest.json`**
(Lauf-Zeit, Lead-Times, Tile-URL-Muster).
7. **Ausliefern** von der DS (wie `/tiles`).
## Frontend (bestehende Radar-Timeline erweitern, map.js)
- Manifest laden DWD-Vorhersage-Frames (0…+120 Min) **rechts von „jetzt"** in die Timeline einhängen
(die Forecast-Markierung `is-forecast` + Scrub/Play gibt es schon).
- **Vergangenheit:** weiter RainViewer (einfach) ODER DWD RADOLAN-RY (5-Min-Analyse) für all-DWD-Konsistenz.
- Quelle pro Frame: Vergangenheit = RainViewer-Tiles, Vorhersage = DWD-PMTiles (byt-/raster-Source).
## Aufwand & offene Entscheidungen
- **Decode:** trivial (verifiziert). **Projektion:** der einzige Knackpunkt DE1200-Georeferenzierung korrekt
nach 3857 (wradlib nimmt's ab, oder bekannter PROJ-String + Eckkoordinaten). **PoC nötig:** 1 Frame
Tiles über MapLibre rendern und gegen RainViewer/echten Regen gegenchecken (Passgenauigkeit).
- **Tiling alle 5 Min × 25 Frames:** z09 für DE ist schnell (Sekunden~1 Min auf der DS). Last beachten
(läuft neben Immich & Co.); ggf. nur jeden 2. Lead-Time tilen (10-Min-Schritte) zum Sparen.
- **Speicher:** Radar-Tiles sind dünn/transparent wenige MB pro Lauf.
- **Cron alle 5 Min:** neuer Container/Job (analog `docker-compose.osm.yml`); `--remove-orphans`-Falle.
- **Entscheidungen:** (a) Vergangenheit RainViewer vs. DWD-RY; (b) PMTiles-pro-Frame vs. XYZ; (c) Farbskala;
(d) Zoom-Range (z09) + Lead-Schrittweite (5 vs 10 Min); (e) Container-Stack (GDAL/Python) auf der DS.
- **Abhängigkeit:** Docker auf der DS.
## Nächste Schritte
1. **PoC**: 1 RV-Archiv 1 Frame decode kolorieren reprojizieren 1 PMTiles headless über MapLibre
rendern. **Kernfrage: stimmt die Georeferenzierung?** (Wenn ja, ist der Rest Fleißarbeit.)
2. Pipeline-Skript (fetchdecodetiledeploy) + Cron-Job + Manifest.
3. Frontend: Manifest in die Timeline einhängen (Vorhersage-Frames rechts von jetzt").
Siehe `docs/TILE_SERVER_HANDOVER.md` (Tile-Infra), Memory `project_tile_server_maintenance`.