Rene: Funkloecher + Routen waren nach 'Alles loeschen' weiter weg.
- Funkloch-Regionen jetzt im Keep-Set (geloescht wird NUR Manuelles);
Zonen behalten ihren Fuellstatus (Komplett-Wipe setzt weiter zurueck)
- Korridor-Migration beim Loeschen: keepTracks=[{name,track}] schreibt
Tracks in Alt-Eintraege ohne r.track (Bestand vor v1236) bzw. legt
fehlende Korridor-Regionen an — kein Warten auf Self-Healing
- clear() liefert Summary; Toast zeigt 'behalten: Standort, X Routen,
Y Funkloch-Gebiete' — Diagnose-Sichtbarkeit fuer Geraetetests
Bump v1237
270 lines
20 KiB
Markdown
270 lines
20 KiB
Markdown
# Offline-Karten (GL/Vektor) — Feature-Plan
|
||
|
||
**Status:** LIVE auf Production + Staging (Default AN auf banyaro.app/.de, Prod-Freigabe René 2026-06-07
|
||
nach bestandenen Gerätetests Runde 1+2). localhost = Leaflet/AUS.
|
||
**Stand:** 2026-06-07. Autor: René + Claude (Design).
|
||
|
||
## Umsetzungsstand (2026-06-06, v1222 auf Staging)
|
||
**✅ Fertig + headless bewiesen (2026-06-05, v1213):**
|
||
- `map-offline.js` (`window.MapOffline`): Region-Download (`downloadAround(lat,lon,radiusKm)`) → Vektorkacheln
|
||
z0–14 via `pmtiles.getZxy` (liefert bereits dekomprimierte MVT) + Glyphs in **IndexedDB** (`by-offline-tiles`).
|
||
`byt://`-MapLibre-Protokoll (IndexedDB-first, remote-Fallback). ~15 MB / 5 km (dekomprimiert).
|
||
- `map-gl-style.js` `build({offline})`: `byt`-Source statt `pmtiles://`.
|
||
- ui.js/map.js laden map-offline + registrieren `byt`. `UI.loadMapLibreUI` exportiert.
|
||
- Welten-FAB Segment 5: prüft im GL-Modus gespeicherte Region (nicht mehr OSM-Raster); „Fehlende nachladen"
|
||
stößt `MapOffline.downloadAround(GPS, 5km)` an.
|
||
- **Beweis:** Download 97 Tiles (5 km München) → Netz AUS → **1903 Features gerendert**, nicht geladene
|
||
Gegend (Paris) leer; Glyphs nötig (sonst lässt MapLibre offline die ganze Kachel fallen).
|
||
|
||
**✅ Follow-ups Runde 1 (2026-06-06, v1222):**
|
||
- **Flag-Default Staging-AN:** `by_offline_tiles` Default AN auf `staging.banyaro.app`, AUS sonst;
|
||
localStorage `1`/`0` bzw. `?tilesoffline=1/0` (boot.js) übersteuert. Default-Logik 3× synchron:
|
||
`map-gl-style.js _offlineEnabled()`, `offline-indicator.js _offlineTilesMode()`, `pages/map.js _offlineTilesEnabled()`.
|
||
- **Karten-Download-Button:** Speed-Dial „Karte offline speichern" (`map-offline-btn`, war seit FAB-Redesign
|
||
verwaist) — GL-Modus → `downloadAround(Kartenmitte, 5 km)` mit Fortschritt in der Statusbar (Kartenmitte
|
||
statt GPS: Urlaubsort vorab speicherbar); Leaflet-Modus → alter Raster-Prefetch (`_cacheTiles`).
|
||
Sichtbarkeit gated: GL ohne Offline-Flag (= Production) zeigt den Button nicht.
|
||
- **Glyph-Persistenz:** Glyphs in IndexedDB (Key-Präfix `f/` im Tiles-Store, kein Schema-Bump) + Protokoll
|
||
`byt://f/{fontstack}/{range}` (IndexedDB-first, remote-Fallback); Style nutzt offline die byt-Glyph-URL
|
||
→ überlebt App-Updates (SW-Cache wird gepurged, IndexedDB nicht).
|
||
- **Raster-Prefetch gegated:** `offline-indicator.js init()` überspringt `_prefetchTiles()` im
|
||
Offline-Tiles-Modus (GL nutzt das OSM-Raster nicht).
|
||
|
||
**✅ Gerätetest-Befunde behoben (2026-06-06, v1223) — Gerätetest iOS BESTANDEN (Basemap+Labels offline ok):**
|
||
- **POI-Marker offline:** `downloadAround` speichert zusätzlich `/api/osm/pois` (fast=true, liest lokale
|
||
osm_pois-DB) je Typ für die Region-Bbox in IndexedDB (Key-Präfix `p/<type>`, Merge per id — zweite
|
||
Region löscht die erste nicht). `MapOffline.pois(type, bbox)` filtert für den Ausschnitt; map.js
|
||
Phase-1-Catch fällt offline darauf zurück. POI-Typen-Liste in map-offline.js synchron mit
|
||
`OSM_LAYER_MAP` (pages/map.js) halten! Marker erscheinen erst nach ERNEUTEM Region-Download.
|
||
- **Offline-Banner** klappt 5 s nach Offline-Gang auf schmale Icon-Leiste ein (volles Banner verdeckte
|
||
die Karten-Legende); Banner-Styles von index.html-Inline nach components.css konsolidiert.
|
||
|
||
**✅ Runde 2 — adaptives Modell (2026-06-07, Design René 2026-06-06):**
|
||
- **Budget-Download statt fester Radius:** `downloadAround(lat, lon, {budgetMB:5})` expandiert z14-Ringe
|
||
(+ Eltern z10–13, Basis z0–9 immer dabei) um den Standort, bis **5 MB GESPEICHERTE Bytes**
|
||
(dekomprimiert, IndexedDB) erreicht sind → Stadt ~1,5–3 km, Land ~6–10 km Radius — passend zur
|
||
Funknetzdichte. **CLIENT-seitig — der geplante Server-Region-Extract-Endpoint ist NICHT nötig.**
|
||
- **Funkloch-Gedächtnis:** Tile-Remote-Miss bei aktivem GPS (map.js Recording speist
|
||
`MapOffline.setGps`) → `markDeadZone` (Dedupe 2 km, Cap 50, **komplett lokal, nie hochgeladen**).
|
||
`autoFillDeadZones()` lädt offene Zonen budget-getrieben nach, sobald online (Trigger:
|
||
offline-indicator init +30 s, `online`-Event +8 s; Vorab-Check ohne GL-Stack-Load).
|
||
- **Routen-Korridor:** `downloadCorridor(track, {bufferKm:1, capMB:50})` + Button „Offline" im
|
||
Routen-Detail (`rd-offline`, flag-gated) — Kacheln ±1 km um den Track + Marker der Korridor-Bbox.
|
||
- **Coverage-Layer:** `MapOffline.coverage()` (GeoJSON der gespeicherten z14-Kacheln) als blauer
|
||
GL-Fill-Layer; Offline-Button öffnet jetzt ein **Verwaltungs-Modal** (Gebiete/MB/Marker-Stats,
|
||
Gebiet speichern, Bereiche ein-/ausblenden, Alles löschen per Zweiklick).
|
||
- Flag-Logik zentralisiert: `boot.js window.BY.offlineTiles()` (vorher 3× dupliziert).
|
||
- Meta neu: `regions`-Liste (Cap 30) + `deadzones`; `region` (letztes Gebiet) bleibt für Back-Compat.
|
||
|
||
**✅ Gerätetest-Befunde Runde 2 behoben (v1227):**
|
||
- **Giftköder + vermisste Hunde offline sichtbar** (René: „müssen unbedingt sichtbar sein"):
|
||
Region-Download speichert zusätzlich `/api/poison` + `/api/lost` der Gegend (`p/_poison`,
|
||
`p/_lost`; Reader `MapOffline.alerts(kind, bbox)`). map.js `_loadAll` fällt **pro Quelle**
|
||
(nicht alles-oder-nichts) auf localStorage zurück — vorher verhinderte das SW-gecachte
|
||
`/api/places` den Fallback, während die Bbox-URL `/api/poison?lat=…` scheiterte.
|
||
lost.js merged den Region-Snapshot in beiden Offline-Pfaden.
|
||
- **Korridor „unsichtbar"**: Logik war korrekt (Node-Stub-Test `downloadCorridor`/`coverage`
|
||
bestanden) — er lag im bereits gespeicherten Gebiet. Nach dem Speichern werden die
|
||
gespeicherten Bereiche jetzt blau auf der Routen-Detailkarte eingeblendet (`_detailMap._gl`).
|
||
|
||
**✅ Runde 3 (2026-06-07):**
|
||
- **Offline-Indikator = pulsierendes Icon** oben rechts (32 px, unterhalb der Kopfzeilen-Höhe) statt
|
||
Banner über die volle Breite — verdeckte Nav-Elemente, z.B. „← Zurück" in der Routennavigation
|
||
(Gerätetest René). Vollbanner weiterhin 5 s beim Offline-Gang, dann Icon.
|
||
- **Rollendes Vorausladen beim Aufzeichnen:** `setGps()` lädt alle ~400 m still die FEHLENDEN
|
||
z14±2-Kacheln (+Eltern) um die Position, solange online — deckt Weg + Anfahrt schon beim ERSTEN
|
||
Besuch ab (das Funkloch-Gedächtnis greift erst ab dem 2.). Kein Region-Eintrag, kein UI.
|
||
- **Bereichsauswahl light:** Modal-Button „Sichtbaren Ausschnitt speichern" → `downloadBbox(viewport,
|
||
{capMB:40})` mit Zu-groß-Schutz (>4000 z14-Kacheln → „bitte reinzoomen").
|
||
- **Speicher-Cap 250 MB (Soft-Guard):** `totalBytes`-Zähler in Meta; AUTOMATISCHE Pfade (Vorausladen,
|
||
Funkloch-Autofill) stoppen über dem Cap, manuelle bleiben; `navigator.storage.persist()` best-effort.
|
||
Echte LRU-Eviction bewusst vertagt (Kacheln werden regionsübergreifend geteilt → Eviction braucht
|
||
Refcounting; bei ~8 MB/Gebiet kein Druck).
|
||
- **Auto-OSM-Raster-Prefetch entfernt** (offline-indicator init); `_prefetchTiles`/`_cacheTiles`
|
||
bleiben nur für den manuellen Leaflet-Pfad (localhost / by_map_gl=0).
|
||
- Logik per Node-Stub-Tests verifiziert (Bbox, Zu-groß, Cap, Prefetch-Throttle, persist).
|
||
Achtung Node 21+: eingebautes `navigator`-Global schluckt `global.navigator=`-Stubs —
|
||
`Object.defineProperty(globalThis, 'navigator', …)` verwenden.
|
||
|
||
**✅ Runde 4 — Minimal-Speicher-Modell (2026-06-08, Modell René):**
|
||
Prinzip: Gespeichert bleibt NUR, was wegen Funklöchern nötig ist + manuell Hinzugefügtes
|
||
(jeweils mit allen Markern + Warnungen). Das Funkloch-Gedächtnis ist die QUELLE DER WAHRHEIT,
|
||
die Kacheln sind jederzeit neu ableitbarer Cache.
|
||
- **Ephemeres Vorausladen:** Rolling-Prefetch-Kacheln werden bei Aufzeichnungsende GELÖSCHT,
|
||
wenn die Runde kein Funkloch hatte (nur neu gespeicherte Keys; Bestand unangetastet).
|
||
- **Netz-Probe bei Aufzeichnung** (alle ~2 Min, /api/version, 6-s-Timeout): erkennt Funklöcher
|
||
auch in BEREITS GESPEICHERTEN Gebieten — dort kommen Kacheln aus IndexedDB, es gibt keinen
|
||
Remote-Miss als Signal (Lücke von René gefunden).
|
||
- **clear() behält das Funkloch-Gedächtnis** (Zonen → filled:false): Beim nächsten Online-Start
|
||
werden Funkloch-Gebiete automatisch neu geladen, auch wenn man alles gelöscht hat.
|
||
- **Start-Check mit Position:** autoFillDeadZones({lat,lon,maxKm:50}) — nur Zonen im Umkreis
|
||
(Speicher minimal), nächstgelegene zuerst; VERIFIZIERT gefüllte Zonen (Zentrums-Kachel da?)
|
||
→ fängt Löschen + iOS-Eviction ab. Pfote-Segment 5 = „alle bekannten Funkloch-Zonen gefüllt".
|
||
- **Coverage-Layer zweifarbig:** Funkloch-Gebiete ORANGE (#f59e0b), manuelle BLAU (#3b82f6);
|
||
Legende im Offline-Modal; Regionen tragen type 'funkloch'/'gebiet'/'korridor'/'ausschnitt'.
|
||
|
||
**✅ Runde 5 — Gerätetest-Feedback (2026-06-08):**
|
||
- **Indikator links unter die Zoom-Regler** (+/−): rechts verdeckte er die Legenden-Chips.
|
||
- **Flugmodus bei offener App = Funkloch-Signal:** `offline`-Event → Position raw als Zone in
|
||
IndexedDB (GL-Stack offline evtl. nicht ladbar) → wird künftig automatisch geladen.
|
||
- **Ent-Funklochen:** Zonen-Liste im Offline-Modal (Datum/Koordinaten/Status) mit ✕ →
|
||
`removeDeadZone(ts)`; Kacheln bleiben bis „Alles löschen".
|
||
- **Warnungs-Aktualität (Frage René):** `_mergeStore` mit **Bbox-Replace** — die Server-Antwort
|
||
ist für die geladene Bbox autoritativ, aufgehobene Giftköder/gefundene Hunde fliegen raus
|
||
(Fetch-Kreis ⊇ Bbox via ×√2; fresh=null merged nie → Offline-Fetch putzt nichts weg).
|
||
Zusätzlich **24-h-Refresh** der Warnungen im 50-km-Umkreis beim Start-Check.
|
||
- **Routen-Korridore im Start-Check:** `ensureRouteCorridors(routes)` — eigene Routen in
|
||
Positionsnähe per Stichproben-Kacheln verifizieren, bei Lücken Korridor aus `preview_track`
|
||
(40 Punkte, ±1-km-Puffer schluckt die Vereinfachung) neu laden. Gespeicherte Routen bleiben
|
||
offline nutzbar, auch nach „Alles löschen"/Eviction. Region-Dedupe per Typ+Name.
|
||
- Stub-Tests jetzt im Repo: `tests/js/test-map-offline-r*.js` (s. tests/js/README.md).
|
||
|
||
**✅ Runde 6 — Standort-Grundversorgung (2026-06-08):**
|
||
Renés Original-Modell Punkt 1 („die App holt sich am Standort die Karten- und Markerdaten bis
|
||
5 MB") war zu eng als nur-Funkloch interpretiert: Das Gebiet um die AKTUELLE POSITION ist jetzt
|
||
IMMER geladen — `ensureHomeArea(lat, lon)` (Zentrums-Kachel-Check → bei Lücke Budget-Download,
|
||
type 'standort', Cap-gated). Greift: (a) im Start-Check (raw IDB-Check ohne GL-Stack-Load),
|
||
(b) SOFORT nach „Alles löschen" (Standort wird direkt neu geladen + Toast). Pfote Segment 5 =
|
||
Standort-Kachel da UND Zonen im 50-km-Umkreis gefüllt (ferne Zonen zählen nicht mehr — sie
|
||
laden erst vor Ort). Tests: tests/js/test-map-offline-r6.js.
|
||
|
||
**✅ Runde 7 — selektives Löschen (2026-06-08, Idee René: Vorladezeit sparen):**
|
||
„Alles löschen" löscht nicht mehr alles-und-lädt-neu, sondern **behält** Standort-Gebiete
|
||
(`type 'standort'` aus der Regions-Meta), die **Korridore der gespeicherten Routen**
|
||
(`clear({keepTracks})`, Tracks via API.routes.list/preview_track), den 5-km-Umkreis der
|
||
aktuellen Position, Basis-Zooms 0–9 sowie Marker/Warnungen + Glyphs. Seit v1237 bleiben auch FUNKLOCH-Gebiete
|
||
(René: sonst sofort wieder Vorladezeit) — gelöscht wird NUR Manuelles ('gebiet'/'ausschnitt').
|
||
Keep-Set ist SELBSTTRAGEND aus der Region-Meta (Korridor-Track ≤60 Pkt im Eintrag, r.track;
|
||
Standort-Adoption; keepTracks=[{name,track}] migriert Alt-Einträge). clear() liefert Summary
|
||
{standort, funkloch, korridore} → Toast zeigt, was behalten wurde (Diagnose).
|
||
Ohne Keep-Kandidaten: Komplett-Wipe (Zonen → ungefüllt). Batch-Delete in einer Transaktion.
|
||
Tests: r7 (+ r6 angepasst).
|
||
|
||
**🔲 Offen (Backlog):**
|
||
- Echte LRU-Eviction (Refcounting/Region-Zuordnung der Kacheln), wenn Nutzer real ans Cap kommen.
|
||
- Rechteck-Zeichnen als präzisere Bereichsauswahl (Viewport-Variante deckt den Hauptfall ab).
|
||
- POIs auch beim rollenden Vorausladen (aktuell nur Kacheln; Giftköder kommen aus dem
|
||
localStorage-Fallback der letzten Online-Position).
|
||
|
||
## Ziel
|
||
GL-Vektorkarten offline-tauglich machen — Kernszenario **Gassi/Wandern im Funkloch**.
|
||
Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzeraufwand.
|
||
|
||
## Problem (warum GL aktuell NICHT offline geht)
|
||
- PMTiles lädt per **HTTP-Range (206 Partial Content)**. Die **Cache-API kann 206 nicht speichern**
|
||
(`cache.put()` wirft) → Basemap-Kacheln landen nie im Offline-Cache.
|
||
- SW hat **keine Regel für `/tiles`** (nur für `tile.openstreetmap.org` = altes Raster).
|
||
- `offline-indicator.js` prefetcht weiterhin **OSM-Raster** (a.tile.openstreetmap.org), das die GL-Karte
|
||
gar nicht nutzt → doppelter Regress: Raster gecacht das niemand zeigt, GL-Karte offline trotzdem leer.
|
||
- Folge offline heute: App + Daten da, aber GL-Karte = Routenlinie/Marker auf **leerem Hintergrund**.
|
||
|
||
## Gemessene Speicher-Fakten (an echter dach.pmtiles, maxzoom=14 + Overzoom bis ~16)
|
||
**Referenz-Radius = 5 km** (René, 2026-06-05: „5 km genügen"). Messungen:
|
||
| Gebiet | Fläche | Tiles | Größe |
|
||
|---|---|---|---|
|
||
| **München (48,1/11,5), dicht — 5 km** | 10×10 km | 82 | **6,4 MB** |
|
||
| **Bayerischer Wald, ländlich — 5 km** | 10×10 km | 99 | **2,6 MB** |
|
||
| (Kontext) München 10 km | 20×20 km | 252 | 15 MB |
|
||
| (Kontext) Bayerischer Wald 10 km | 20×20 km | 285 | 4,4 MB |
|
||
| (Kontext) Bayerischer Wald ~25 km | 50×50 km | 1.595 | 20 MB |
|
||
|
||
→ Vektor ist ~10× sparsamer als Raster (Raster 5 km ≈ 40–120 MB). Stadt-Tiles ~2,5× dicker als Land.
|
||
→ 5 km ist NICHT 1/4 von 10 km (6,4 vs 15 MB) — die unteren Zoomstufen (Übersicht) sind immer dabei,
|
||
unabhängig von der bbox-Größe.
|
||
→ **Budget ≈ 7 MB** (5 km dichte Stadt + Glyphs). Budget-getrieben deckt das in der Stadt ~5 km, auf dem
|
||
**Land ~8–10 km** Radius ab (mehr Reichweite genau dort wo die Funklöcher sind). Glyphs (~1–2 MB) +
|
||
Style (winzig) → ~8 MB pro Gegend. Sehr sparsam → viele Gegenden problemlos (10 ≈ 80 MB).
|
||
→ Messmethode (reproduzierbar): `docker run --rm -v /tmp/pmt:/out protomaps/go-pmtiles:latest extract
|
||
https://staging.banyaro.app/tiles/dach.pmtiles /out/x.pmtiles --bbox=W,S,E,N` → Dateigröße ablesen.
|
||
|
||
## Architektur
|
||
### Region-Extract (budget-getrieben, NICHT fester Radius)
|
||
- PMTiles-**Directory enthält pro Tile die Byte-Länge** → Server kann die Größe einer Region
|
||
**aufsummieren OHNE die Tiles zu laden**.
|
||
- Endpoint `GET /tiles/region?lat=&lon=&budget=7` (MB): wächst die bbox um die Position, bis die
|
||
summierte Tile-Länge ≈ Budget erreicht (Stadt → kleiner Radius, Land → großer Radius), extrahiert dann
|
||
genau diese Region als `region.pmtiles` (**ein 200er**, ~7 MB). `pmtiles extract` (go-pmtiles) oder
|
||
python-pmtiles im Container.
|
||
- Client lädt die Datei einmal → **IndexedDB** (Blob; 200er, anders als die 206-Ranges cachebar).
|
||
- MapLibre liest **offline** aus dem lokalen Blob via `pmtiles://` (pmtiles.js kann aus ArrayBuffer lesen);
|
||
**online** weiter remote `dach.pmtiles` (immer aktuell, ganz DACH+Anrainer). Source je nach Verbindung wählen.
|
||
- Glyphs (`/fonts/*.pbf`, Open Sans Regular+Semibold) mit cachen (200er, cachebar).
|
||
|
||
### Adaptive Strategie (der eigentliche Clou — lernt von selbst)
|
||
1. **Rollendes Vorausladen beim Aufzeichnen:** Solange GPS aktiv UND Empfang da, fortlaufend Tiles um die
|
||
**aktuelle Position** cachen. Deckt den echten Weg + die Anfahrt automatisch ab — auch beim ersten Mal,
|
||
bevor man ins Funkloch läuft.
|
||
2. **Funkloch-Gedächtnis:** Wo echte Requests **scheitern** (Timeout/Fehler während aktivem GPS — NICHT
|
||
`navigator.onLine`, das lügt bei Captive-Portal/Schwachempfang), den Bereich als „Offline nötig"
|
||
markieren → priorisiert behalten, beim nächsten Online-Durchgang großzügiger nachladen.
|
||
Caveat: im Funkloch selbst kann nicht geladen werden → greift ab dem 2. Besuch (Gassi = repetitiv → ok).
|
||
3. **Manuelles Vorab-Laden** („Offline-Inhalte laden"-Button) — zwei Modi:
|
||
- **Aktuelle Gegend** (Default, Gassi): budget-getrieben um die Position (~7 MB), ein Tipp.
|
||
- **Bereich auswählen** (mehrtägige Wanderung — Auto-5km reicht da nicht): Nutzer wählt ein größeres
|
||
Gebiet, das ganz heruntergeladen wird. Auswahl-Optionen:
|
||
- **Karten-Ausschnitt:** aktuellen Karten-Viewport (durch Zoomen/Verschieben gewählter bbox) als
|
||
Download-Gebiet nehmen — simpel, kein Zeichnen nötig.
|
||
- **Rechteck ziehen** auf der Karte (präziser).
|
||
- **Routen-Korridor:** entlang einer Route ± Puffer (ideal für Touren, die einer Strecke folgen —
|
||
viel sparsamer als eine große bbox).
|
||
**Verbindung zum Routen-Feature (WICHTIG, sonst hängt der Modus in der Luft):** Einstieg primär
|
||
AUS der Route — im Routen-Detail (`routes.js` `_openDetail`, Aktionsleiste neben GPX/Teilen/Navi)
|
||
und in der Navigations-Ansicht ein Button **„Route offline speichern"**. Nutzt den bereits
|
||
geladenen `route.gps_track` → Korridor = alle Tiles im Puffer (z.B. 1–2 km) um den Track (nicht die
|
||
ganze bbox). Zusätzlich im „Offline-Inhalte laden"-Dialog ein Modus **„Aus meinen Routen wählen"**
|
||
(Liste der gespeicherten Routen → Korridor laden). Größen-Vorschau wie unten, vor dem Download.
|
||
- **Größen-Vorschau VOR dem Download:** die Tile-Byte-Längen aus der PMTiles-Directory aufsummieren
|
||
(kein Tile-Download nötig) → „~45 MB" anzeigen, Nutzer bestätigt. Schützt vor versehentlichem Riesen-Download.
|
||
- Fortschritt + „X MB gespeichert" + Liste gespeicherter Gebiete (umbenennen/löschen/aktualisieren).
|
||
- Im Bereichs-Modus gilt das ~7-MB-Budget NICHT (Nutzer entscheidet bewusst), aber eine sinnvolle
|
||
Obergrenze + der globale Speicher-Cap greifen.
|
||
|
||
### Drumherum
|
||
- **Budget-Cap + LRU:** Gesamtspeicher gedeckelt; selten besuchte Funkloch-Caches fallen raus.
|
||
- **Privatsphäre:** „Wo verliere ich Netz" = Aufenthaltsorte → **komplett lokal (IndexedDB), nie hochgeladen.**
|
||
- **Aktualität:** Offline-Region beim nächsten Online-Sein neu ziehbar (Basemap-Updates / pmtiles-Refresh).
|
||
- **Aufräumen:** Den alten OSM-Raster-Prefetch in `offline-indicator.js` ablösen/abschalten (cacht ungenutztes Raster).
|
||
- **Pfoten-Offline-Indikator (Welten-FAB) anpassen:** `offline-indicator.js` füllt 5 Pfoten-Segmente;
|
||
**Segment 5 „Karten-Kacheln"** prüft aktuell `CACHE_TILES` auf ≥ N **OSM-Raster**-Tiles (TILE_PREFETCH
|
||
z14/z13). Die GL-Karte nutzt dieses Raster NICHT → grünes Segment = falsches „Karte offline bereit".
|
||
→ Segment 5 umdefinieren: prüfen ob für die aktuelle Gegend eine **`region.pmtiles` + Glyphs** lokal
|
||
vorliegen (statt Raster-Tile-Count). `offline-fill-btn` („Offline-Inhalte laden") soll dann den
|
||
Region-Download anstoßen statt den Raster-Prefetch. (Bei Variante 1 „Raster-Fallback" bliebe Segment 5
|
||
wie es ist — Entscheidung hängt an der Offline-Strategie oben.)
|
||
|
||
## Offene Entscheidungen / Defaults
|
||
- Budget-Default **~7 MB** (Referenz 5 km Stadt; René 2026-06-05). Stadt ~5 km / Land ~8–10 km.
|
||
Optional Stufe „Groß ~16 MB" (Stadt ~10 km / Land ~18–22 km) für Wandertage.
|
||
- Zoom z0–14 (Overzoom liefert Straßenebene gratis).
|
||
- Detektionssignal = echte Fetch-Timeouts bei aktivem GPS (nicht `navigator.onLine`).
|
||
- Speicher = IndexedDB (Blobs); MapLibre-Source-Umschaltung online/offline.
|
||
|
||
## Abhängigkeiten
|
||
- GL-Tiles in **Produktion** (dach.pmtiles + fonts auf Prod-Volume) — Voraussetzung.
|
||
- pmtiles-Directory-Byte-Summierung (Server) + pmtiles.js Blob-Source (Client).
|
||
- WebGL-Kontext-Disziplin beachten (siehe Skill/Memory: jede GL-Karte beim Schließen `remove()`).
|
||
|
||
Siehe `docs/TILE_SERVER_HANDOVER.md` (Tile-Pipeline) + Memory `project_tile_server_maintenance`.
|
||
|
||
---
|
||
|
||
# Weitere Karten-To-Dos (nicht offline-spezifisch)
|
||
|
||
## Wetter-Chip: Niederschlag „nächste 3 Std" statt ganzer Tag — ✅ ERLEDIGT (2026-06-05, v1214)
|
||
Umgesetzt: `weather.py get_weather_for_location` → `precip = max(h_precip[now_h:now_h+3])` (Fallback Tages-Max);
|
||
Pill `map.js` zeigt `💧 X% (3h)`. Gilt auch fürs Welten-Banner (geteiltes Feld). Hinweis: forecast_days=1 →
|
||
Slice am Tagesende kürzer (späte Nacht weniger Vorausschau); für volle Mitternachts-Abdeckung forecast_days=2.
|
||
|
||
(Original-Notiz:)
|
||
**Ist:** Der Karten-Chip unten zeigt die Regenwahrscheinlichkeit als **Tages-Maximum**
|
||
(`backend/weather.py:98` → `precip = daily['precipitation_probability_max'][0]`; angezeigt in
|
||
`pages/map.js:~2598` als `💧 {w.precip_prob}%`). Über den ganzen Tag gemittelt/maximiert = wenig
|
||
aussagekräftig für „soll ich JETZT raus".
|
||
|
||
**Soll:** Den **höchsten Wert der nächsten 3 Stunden** (ab aktueller Stunde) zeigen.
|
||
- Die stündlichen Daten werden in `weather.py` bereits geladen (`&hourly=precipitation_probability`,
|
||
Array `h_precip` ab Zeile ~116) — kein neuer API-Call nötig.
|
||
- Ändern: `precip_prob = max(h_precip[now_idx : now_idx+3])` (aktuellen Stundenindex bestimmen wie bei
|
||
der bestehenden `next_rain_time`-Logik). `next_rain_time`/Warnungen können bleiben.
|
||
- Optional Chip-Text klarstellen, dass sich der Wert auf die nächsten 3 h bezieht (z.B. `💧 {x}% (3h)`).
|
||
|