banyaro/docs/DWD_RAIN_FORECAST_PLAN.md
rene 72ee339860 Docs: DWD-Regen-Vorhersage-Pipeline gescoppt (Datenformat verifiziert)
DWD RV (composite/rv, kostenlos, alle 5 Min, 25 Frames 0-120min). Format
verifiziert: 194-Byte-ASCII-Header + 1200×1100 uint16 LE, Wert=&0x0FFF×0.01mm,
&0x2000=kein-Daten (PoC: Decode trivial, kein wradlib nötig). Pipeline:
fetch→decode→kolorieren→reprojizieren(DE1200→3857)→Kacheln→PMTiles/Cron 5min;
Frontend hängt Forecast-Frames rechts von 'jetzt' in die Timeline. Knackpunkt:
Georeferenzierung (PoC nötig).
2026-06-05 20:53:51 +02:00

4.2 KiB
Raw Blame History

DWD Regen-Vorhersage (Radar-Nowcast) — Scoping-Plan

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.pmtilesrv_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 (fetch→decode→tile→deploy) + 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.