From c7201aa07bdc87503ef71aa1544fdb0b0dc5df72 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 15:48:11 +0200 Subject: [PATCH 01/10] =?UTF-8?q?Karten-Attribution:=20standardm=C3=A4?= =?UTF-8?q?=C3=9Fig=20eingeklappt=20(nur=20=E2=93=98)=20+=20doppelten=20Hi?= =?UTF-8?q?nweis=20entfernt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Punkt 6: MapLibre rendert die Compact-Attribution offen (maplibregl-compact-show + open) → voller Text '© OpenStreetMap contributors' immer sichtbar. Neuer Helper MapGLStyle.collapseAttribution() entfernt die Klasse/open nach dem Hinzufügen → nur noch das ⓘ, der Text erscheint erst auf Klick (rechtlich nach ODbL ausreichend). In map-gl-mini.js (Seitenkarten) + map.js (zentrale Karte) verdrahtet. Punkt 7: poison.js + lost.js hatten UNTER der Karte zusätzlich ein hartkodiertes '© OpenStreetMap-Mitwirkende' — doppelt zum Karten-ⓘ. Entfernt (+ ungenutzte .lost-map-attribution CSS-Klasse). Verifiziert: osmTextLeafCount 2-3 → 1, compactShown true → false. --- VERSION | 2 +- backend/static/css/components.css | 9 --------- backend/static/index.html | 24 ++++++++++++------------ backend/static/js/app.js | 2 +- backend/static/js/map-gl-mini.js | 1 + backend/static/js/map-gl-style.js | 17 ++++++++++++++++- backend/static/js/pages/lost.js | 6 +----- backend/static/js/pages/map.js | 1 + backend/static/js/pages/poison.js | 6 +----- backend/static/landing.html | 2 +- backend/static/sw.js | 2 +- 11 files changed, 36 insertions(+), 36 deletions(-) diff --git a/VERSION b/VERSION index 29001eb..b5811ff 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1206 \ No newline at end of file +1207 \ No newline at end of file diff --git a/backend/static/css/components.css b/backend/static/css/components.css index 366d2f7..9f996ab 100644 --- a/backend/static/css/components.css +++ b/backend/static/css/components.css @@ -6754,15 +6754,6 @@ html.modal-open { margin-bottom: 2px; } -/* OSM-Attribution unter der Karte */ -.lost-map-attribution { - font-size: 10px; - color: var(--c-text-secondary); - text-align: right; - padding: 2px var(--space-2) 0; - margin-bottom: var(--space-4); -} - /* Info-Zeile über der Liste ("X vermisste Hunde …") */ .lost-info-text { font-size: var(--text-sm); diff --git a/backend/static/index.html b/backend/static/index.html index 55377a3..9bf8a7b 100644 --- a/backend/static/index.html +++ b/backend/static/index.html @@ -86,14 +86,14 @@ Ban Yaro - + - - - - - + + + + + @@ -617,11 +617,11 @@ - - - - - + + + + + @@ -631,7 +631,7 @@ - + diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 676179c..d1d6ff9 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '1206'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '1207'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator) window.APP_VERSION = APP_VERSION; diff --git a/backend/static/js/map-gl-mini.js b/backend/static/js/map-gl-mini.js index 820f846..5afd176 100644 --- a/backend/static/js/map-gl-mini.js +++ b/backend/static/js/map-gl-mini.js @@ -244,6 +244,7 @@ map.addControl(new maplibregl.AttributionControl({ compact: true, customAttribution: '© OpenStreetMap contributors', })); + MapGLStyle.collapseAttribution(map); // nur ⓘ, nicht ausgeschrieben // Container kann beim Erstellen (Modal/Animation) noch 0×0 sein → mehrfach resizen. var _rz = function () { try { map.resize(); } catch (e) {} }; requestAnimationFrame(_rz); diff --git a/backend/static/js/map-gl-style.js b/backend/static/js/map-gl-style.js index 839192c..7fcf803 100644 --- a/backend/static/js/map-gl-style.js +++ b/backend/static/js/map-gl-style.js @@ -141,5 +141,20 @@ }; } - window.MapGLStyle = { build: build, tilesUrl: tilesUrl, tilesFile: TILES_FILE }; + // Compact-Attribution standardmäßig EINGEKLAPPT lassen (nur das ⓘ; der volle Text + // "© OpenStreetMap contributors" erscheint erst auf Klick). MapLibre rendert sie sonst + // offen (Klasse maplibregl-compact-show + open). Rechtlich reicht das ⓘ (ODbL). + function collapseAttribution(map) { + var fn = function () { + try { + var a = map.getContainer().querySelector('.maplibregl-ctrl-attrib'); + if (a) { a.classList.remove('maplibregl-compact-show'); a.removeAttribute('open'); } + } catch (e) {} + }; + fn(); + if (typeof requestAnimationFrame === 'function') requestAnimationFrame(fn); + setTimeout(fn, 60); + } + + window.MapGLStyle = { build: build, tilesUrl: tilesUrl, tilesFile: TILES_FILE, collapseAttribution: collapseAttribution }; })(); diff --git a/backend/static/js/pages/lost.js b/backend/static/js/pages/lost.js index 92769b3..30d18e5 100644 --- a/backend/static/js/pages/lost.js +++ b/backend/static/js/pages/lost.js @@ -107,13 +107,9 @@ window.Page_lost = (() => {
-
- © OpenStreetMap-Mitwirkende -

OpenStreetMap contributors', })); + MapGLStyle.collapseAttribution(_map); // nur ⓘ, nicht ausgeschrieben if (!_userPos) { _frankfurtTimer = setTimeout(() => _mapFlyTo(50.1109, 8.6821, 14, { duration: 2.5 }), 1200); diff --git a/backend/static/js/pages/poison.js b/backend/static/js/pages/poison.js index cd9e642..8ae1bb1 100644 --- a/backend/static/js/pages/poison.js +++ b/backend/static/js/pages/poison.js @@ -61,13 +61,9 @@ window.Page_poison = (() => {

-
- © OpenStreetMap-Mitwirkende -
diff --git a/backend/static/landing.html b/backend/static/landing.html index 91b71b8..4f46967 100644 --- a/backend/static/landing.html +++ b/backend/static/landing.html @@ -4,7 +4,7 @@ - + Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz diff --git a/backend/static/sw.js b/backend/static/sw.js index 07cb52e..49882fb 100644 --- a/backend/static/sw.js +++ b/backend/static/sw.js @@ -4,7 +4,7 @@ ============================================================ */ // ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab -const VER = '1206'; +const VER = '1207'; const CACHE_VERSION = `by-v${VER}`; const CACHE_STATIC = `${CACHE_VERSION}-static`; const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten From d11794355c08054eb426b65d13fa8cd5cf36ed2b Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 16:15:20 +0200 Subject: [PATCH 02/10] =?UTF-8?q?Tiles:=20DACH=20+=20alle=20Anrainer=20(15?= =?UTF-8?q?=20L=C3=A4nder)=20+=20Einzel-PBFs=20nach=20Merge=20freigeben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TILES_REGIONS auf germany/austria/switzerland + france/italy/czech-republic/ poland/slovakia/hungary/slovenia/netherlands/belgium/luxembourg/denmark/ liechtenstein erweitert. Output bleibt dach.pmtiles (Frontend-Name stabil). Nach time-filter werden History + Einzel-PBFs gelöscht → ~27 GB weniger Spitzen-Plattenplatz vor planetiler. --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index f1db7cd..e8d24b1 100644 --- a/Makefile +++ b/Makefile @@ -150,7 +150,10 @@ staging-db: check-ssh # NICHT im Image — wird per Range-Route (/tiles) ausgeliefert. # ---------------------------------------------------------- TILES_DIR := tiles/build -TILES_REGIONS := germany austria switzerland +# DACH + alle angrenzenden Länder (15). Reihenfolge egal — osmium merge -H + time-filter +# dedupliziert Grenz-Nodes. Output bleibt dach.pmtiles (Frontend referenziert den Namen). +TILES_REGIONS := germany austria switzerland france italy czech-republic poland \ + slovakia hungary slovenia netherlands belgium luxembourg denmark liechtenstein PLANETILER_IMAGE := ghcr.io/onthegomap/planetiler:latest TILES_TARGET := $(if $(filter prod,$(ENV)),$(DS_PATH),$(DS_PATH_STAGING)) @@ -166,7 +169,8 @@ tiles: @# liefert genau eine Version pro ID (planetiler braucht eindeutige, sortierte IDs). @osmium merge -H $(foreach r,$(TILES_REGIONS),$(TILES_DIR)/$(r).osm.pbf) -o $(TILES_DIR)/dach-hist.osm.pbf --overwrite @osmium time-filter $(TILES_DIR)/dach-hist.osm.pbf -o $(TILES_DIR)/dach.osm.pbf --overwrite - @rm -f $(TILES_DIR)/dach-hist.osm.pbf + @# History + Einzel-PBFs jetzt freigeben (spart ~Quellsumme an Spitzen-Plattenplatz vor planetiler). + @rm -f $(TILES_DIR)/dach-hist.osm.pbf $(foreach r,$(TILES_REGIONS),$(TILES_DIR)/$(r).osm.pbf) @echo "→ planetiler → dach.pmtiles (disk-backed mmap)..." @docker run --rm -v "$(CURDIR)/$(TILES_DIR):/data" $(PLANETILER_IMAGE) \ --osm-path=/data/dach.osm.pbf --download --output=/data/dach.pmtiles --force \ From 29076bcdff2c5aa7f4724bb349e4c3832280bd5d Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 16:50:52 +0200 Subject: [PATCH 03/10] =?UTF-8?q?Tiles:=20Fortschritts-Snapshot-Skript=20(?= =?UTF-8?q?Stufe/Balken/ETA)=20f=C3=BCr=20Build-Monitoring=20+=20.gitignor?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 ++ tiles/progress.sh | 54 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100755 tiles/progress.sh diff --git a/.gitignore b/.gitignore index e23a514..8319981 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ tiles/build/ *.pmtiles *.osm.pbf *.mbtiles +tiles/build.log +tiles/.DS_Store diff --git a/tiles/progress.sh b/tiles/progress.sh new file mode 100755 index 0000000..eefeb2d --- /dev/null +++ b/tiles/progress.sh @@ -0,0 +1,54 @@ +#!/bin/bash +# Ein Fortschritts-Snapshot für den Tile-Build. Gibt EINE Zeile aus: +# STAGE=<1|2|3|4|DONE> PCT= LABEL= +B=tiles/build +LOG=tiles/build.log +EXP_DL=$((24 * 1024 * 1024 * 1024)) # ~24 GB erwartete Quell-/Merge-Summe (15 Länder) +EXP_PLAN_SEC=$((90 * 60)) # ~90 Min planetiler-Schätzung (Mittel-Europa) +now=$(date +%s) + +_bytes() { local s=0 f; for f in "$@"; do [ -f "$f" ] && s=$((s + $(stat -f%z "$f" 2>/dev/null || echo 0))); done; echo "$s"; } +_birth() { if [ -f "$1" ]; then stat -f%B "$1" 2>/dev/null || echo "$now"; else echo "$now"; fi; } +_mtime() { if [ -f "$1" ]; then stat -f%m "$1" 2>/dev/null || echo "$now"; else echo "$now"; fi; } +_bar() { local p=$1 n i out=""; n=$((p * 20 / 100)); [ $n -gt 20 ] && n=20; [ $n -lt 0 ] && n=0 + for ((i=0;i<20;i++)); do [ $i -lt $n ] && out+="█" || out+="░"; done; printf "%s" "$out"; } +_eta() { local s=$1; [ "$s" -lt 0 ] 2>/dev/null && s=0 + if [ "$s" -ge 3600 ]; then printf "~%dh %dm" $((s/3600)) $(((s%3600)/60)) + elif [ "$s" -ge 60 ]; then printf "~%d Min" $((s/60)); else printf "~%ds" "$s"; fi; } +_gb() { awk "BEGIN{printf \"%.1f\", $1/1073741824}"; } + +if grep -q "Tiles gebaut" "$LOG" 2>/dev/null; then + sz=$(_bytes "$B/dach.pmtiles"); echo "STAGE=DONE PCT=100 LABEL=Fertig — dach.pmtiles $(_gb $sz) GB"; exit 0 +fi + +if grep -q "→ planetiler" "$LOG" 2>/dev/null; then + start=$(_mtime "$B/dach.osm.pbf"); el=$((now - start)); [ $el -lt 1 ] && el=1 + pp=$(grep -oE "[0-9]{1,3}%" "$LOG" 2>/dev/null | tail -1 | tr -d '%') + if [ -n "$pp" ] && [ "$pp" -ge 0 ] 2>/dev/null && [ "$pp" -le 100 ] 2>/dev/null; then pct=$pp; else pct=$((el * 100 / EXP_PLAN_SEC)); fi + [ $pct -gt 95 ] && pct=95; [ $pct -lt 1 ] && pct=1 + eta=$((el * (100 - pct) / pct)) + echo "STAGE=4 PCT=$pct LABEL=planetiler (Vektor-Tiles) $(_bar $pct) ${pct}% · ETA $(_eta $eta)"; exit 0 +fi + +if [ -f "$B/dach.osm.pbf" ]; then + b=$(_bytes "$B/dach.osm.pbf"); start=$(_birth "$B/dach.osm.pbf"); el=$((now - start)); [ $el -lt 1 ] && el=1 + pct=$((b * 100 / EXP_DL)); [ $pct -gt 99 ] && pct=99 + rate=$((b / el)); [ $rate -lt 1 ] && rate=1; eta=$(((EXP_DL - b) / rate)) + echo "STAGE=3 PCT=$pct LABEL=osmium time-filter (dedup) $(_bar $pct) ${pct}% · $(_gb $b) GB · ETA $(_eta $eta)"; exit 0 +fi + +if [ -f "$B/dach-hist.osm.pbf" ]; then + b=$(_bytes "$B/dach-hist.osm.pbf"); start=$(_birth "$B/dach-hist.osm.pbf"); el=$((now - start)); [ $el -lt 1 ] && el=1 + pct=$((b * 100 / EXP_DL)); [ $pct -gt 99 ] && pct=99 + rate=$((b / el)); [ $rate -lt 1 ] && rate=1; eta=$(((EXP_DL - b) / rate)) + echo "STAGE=2 PCT=$pct LABEL=osmium merge (Grenz-Nodes) $(_bar $pct) ${pct}% · $(_gb $b) GB · ETA $(_eta $eta)"; exit 0 +fi + +# Stufe 1: Download +files=$(ls "$B"/*.osm.pbf 2>/dev/null | grep -v dach) +b=$(_bytes $files); start=$(_birth "$LOG"); el=$((now - start)); [ $el -lt 1 ] && el=1 +pct=$((b * 100 / EXP_DL)); [ $pct -gt 99 ] && pct=99 +rate=$((b / el)); [ $rate -lt 1 ] && rate=1; eta=$(((EXP_DL - b) / rate)) +ndone=$(echo "$files" | grep -c .) +cur=$(grep -E "^ [a-z]" "$LOG" 2>/dev/null | tail -1 | tr -d ' ') +echo "STAGE=1 PCT=$pct LABEL=Download $(_bar $pct) ${pct}% · ${ndone}/15 Länder (${cur}) · $(_gb $b)/24 GB · ETA $(_eta $eta)" From 43b1e8026f629ac81d4de245ddbdf2fbdbda2037 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 17:16:16 +0200 Subject: [PATCH 04/10] =?UTF-8?q?Docs:=20Offline-Karten-Plan=20(GL/Vektor)?= =?UTF-8?q?=20=E2=80=94=20budget-getrieben=20+=20adaptives=20Funkloch-Lern?= =?UTF-8?q?en?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Festgehalten als Feature-Plan: Region-Extract per pmtiles (budget-getrieben statt Radius, ~16 MB Default, gemessen 15 MB für 10km München / ~18-22km Land), Client IndexedDB-Blob + MapLibre lokal/remote, adaptives Lernen (rollendes Vorausladen beim Aufzeichnen + Funkloch-Gedächtnis aus echten Fetch-Fehlern, alles lokal), manuelles Vorab-Laden, Budget+LRU. Umsetzung nach Produktions-Rollout. --- docs/OFFLINE_MAPS_PLAN.md | 72 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/OFFLINE_MAPS_PLAN.md diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md new file mode 100644 index 0000000..d0acd84 --- /dev/null +++ b/docs/OFFLINE_MAPS_PLAN.md @@ -0,0 +1,72 @@ +# Offline-Karten (GL/Vektor) — Feature-Plan + +**Status:** geplant (Umsetzung nach Tile-Build/Produktions-Rollout der GL-Karten). +**Stand:** 2026-06-05. Autor: René + Claude (Design). + +## 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) +| Gebiet | Fläche | Tiles | Größe | +|---|---|---|---| +| München (48,1/11,5), dicht | 20×20 km (10 km Radius) | 252 | **15 MB** | +| Bayerischer Wald, ländlich | 20×20 km | 285 | **4,4 MB** | +| Bayerischer Wald, ländlich | 50×50 km (~25 km Radius) | 1.595 | **20 MB** | + +→ Vektor ist ~10× sparsamer als Raster (Raster 10 km ≈ 100–300 MB). Stadt-Tiles ~3,4× dicker als Land. +→ **Budget ≈ 16 MB** deckt in der Stadt ~10 km, auf dem **Land ~18–22 km** Radius ab (mehr Reichweite + genau dort wo die Funklöcher sind). Glyphs (~1–2 MB) + Style (winzig) obendrauf → ~17 MB pro Gegend. +→ 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=16` (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**, ~16 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:** Button „Diese Gegend offline speichern" für geplante neue Touren (Lernen + deckt nur Bekanntes ab). Fortschritt + „X MB gespeichert" + Löschen/Aktualisieren. + +### 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). + +## Offene Entscheidungen / Defaults +- Budget-Default **16 MB**, optional Stufe „Groß 40 MB" (Stadt ~16 km / Land ~30+ 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`. From 827ea9519118d7a045683178ac0feb08fbd9151d Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 17:21:08 +0200 Subject: [PATCH 05/10] =?UTF-8?q?Tiles-Progress:=20Stufe=204=20zeigt=20ech?= =?UTF-8?q?te=20planetiler-Phase=20statt=20M=C3=BCll-ETA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tiles/progress.sh | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tiles/progress.sh b/tiles/progress.sh index eefeb2d..a993ba1 100755 --- a/tiles/progress.sh +++ b/tiles/progress.sh @@ -22,12 +22,18 @@ if grep -q "Tiles gebaut" "$LOG" 2>/dev/null; then fi if grep -q "→ planetiler" "$LOG" 2>/dev/null; then - start=$(_mtime "$B/dach.osm.pbf"); el=$((now - start)); [ $el -lt 1 ] && el=1 - pp=$(grep -oE "[0-9]{1,3}%" "$LOG" 2>/dev/null | tail -1 | tr -d '%') - if [ -n "$pp" ] && [ "$pp" -ge 0 ] 2>/dev/null && [ "$pp" -le 100 ] 2>/dev/null; then pct=$pp; else pct=$((el * 100 / EXP_PLAN_SEC)); fi - [ $pct -gt 95 ] && pct=95; [ $pct -lt 1 ] && pct=1 - eta=$((el * (100 - pct) / pct)) - echo "STAGE=4 PCT=$pct LABEL=planetiler (Vektor-Tiles) $(_bar $pct) ${pct}% · ETA $(_eta $eta)"; exit 0 + # Echte planetiler-Zeile parsen (ANSI/\r entfernen). Phasen: osm_pass1(nodes)→osm_pass2(ways/rels)→ + # write/archive(tiles). Eine ehrliche Gesamt-% gibt planetiler nicht her → Phase + Phasen-% zeigen, + # KEINE erfundene Zeit-ETA (war zuvor Quatsch wegen Log-Rauschen). + line=$(sed 's/\x1b\[[0-9;]*m//g; s/\r/\n/g' "$LOG" 2>/dev/null | grep -aE "^[0-9]+:[0-9]{2}:[0-9]{2} .*INF \[" | tail -1) + phase=$(printf '%s' "$line" | sed -nE 's/.*INF \[([a-z0-9_]+)[]:].*/\1/p') + if printf '%s' "$line" | grep -q "ways:"; then + pct=$(printf '%s' "$line" | sed -nE 's/.*ways: \[[^]]*[[:space:]]([0-9]{1,3})%[[:space:]].*/\1/p') + else + pct=$(printf '%s' "$line" | grep -oE "[0-9]{1,3}%" | tail -1 | tr -d '%') + fi + [ -z "$pct" ] && pct=0 + echo "STAGE=4 PCT=$pct LABEL=planetiler · ${phase:-läuft} $(_bar $pct) ${pct}% (Phasen-Fortschritt, Gesamt-ETA unsicher)"; exit 0 fi if [ -f "$B/dach.osm.pbf" ]; then From daa44946f1f20d0b5c20e511e742624085653e27 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 17:48:59 +0200 Subject: [PATCH 06/10] =?UTF-8?q?Docs/Karten-Plan:=20Wetter-Chip=20Nieders?= =?UTF-8?q?chlag=20auf=20n=C3=A4chste=203h-Max=20umstellen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Karten-Chip zeigt aktuell Tages-Max (weather.py:98 daily.precipitation_probability_max[0]); soll Höchstwert der nächsten 3 Std zeigen (stündliche Daten h_precip schon vorhanden: max(h_precip[now_idx:now_idx+3])). Als abgegrenzte 'Weitere Karten-To-Dos'-Sektion festgehalten. --- docs/OFFLINE_MAPS_PLAN.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md index d0acd84..ea12814 100644 --- a/docs/OFFLINE_MAPS_PLAN.md +++ b/docs/OFFLINE_MAPS_PLAN.md @@ -70,3 +70,21 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau - 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 +**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)`). + From d1e44ebfb931dadd75f6b377730c641a2f4624cd Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 17:52:20 +0200 Subject: [PATCH 07/10] =?UTF-8?q?Offline-Plan:=20Referenz-Radius=2010?= =?UTF-8?q?=E2=86=925=20km=20(gemessen=206,4=20MB=20Stadt=20/=202,6=20MB?= =?UTF-8?q?=20Land),=20Budget=20~7=20MB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/OFFLINE_MAPS_PLAN.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md index ea12814..55c464d 100644 --- a/docs/OFFLINE_MAPS_PLAN.md +++ b/docs/OFFLINE_MAPS_PLAN.md @@ -16,15 +16,21 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau - 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 | 20×20 km (10 km Radius) | 252 | **15 MB** | -| Bayerischer Wald, ländlich | 20×20 km | 285 | **4,4 MB** | -| Bayerischer Wald, ländlich | 50×50 km (~25 km Radius) | 1.595 | **20 MB** | +| **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 10 km ≈ 100–300 MB). Stadt-Tiles ~3,4× dicker als Land. -→ **Budget ≈ 16 MB** deckt in der Stadt ~10 km, auf dem **Land ~18–22 km** Radius ab (mehr Reichweite - genau dort wo die Funklöcher sind). Glyphs (~1–2 MB) + Style (winzig) obendrauf → ~17 MB pro Gegend. +→ 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. @@ -32,9 +38,9 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau ### 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=16` (MB): wächst die bbox um die Position, bis die +- 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**, ~16 MB). `pmtiles extract` (go-pmtiles) oder + 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); @@ -59,7 +65,8 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau - **Aufräumen:** Den alten OSM-Raster-Prefetch in `offline-indicator.js` ablösen/abschalten (cacht ungenutztes Raster). ## Offene Entscheidungen / Defaults -- Budget-Default **16 MB**, optional Stufe „Groß 40 MB" (Stadt ~16 km / Land ~30+ km) für Wandertage. +- 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. From 27e7590eedde04aecdb9412b74453870e68abc0a Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 17:55:33 +0200 Subject: [PATCH 08/10] Offline-Plan: Pfoten-Indikator (Welten-FAB) Segment 5 als Kopplung festgehalten MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Segment 5 'Karten-Kacheln' misst aktuell OSM-Raster-Cache (offline-indicator.js, CACHE_TILES) — GL nutzt das nicht → falsches 'Karte offline bereit'. Bei GL-Region- Download umdefinieren (region.pmtiles + Glyphs prüfen, offline-fill-btn stößt Region- Download an). An Offline-Strategie gekoppelt. --- docs/OFFLINE_MAPS_PLAN.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md index 55c464d..ef84bb0 100644 --- a/docs/OFFLINE_MAPS_PLAN.md +++ b/docs/OFFLINE_MAPS_PLAN.md @@ -63,6 +63,13 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau - **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. From f38301a39150ebbcb4e10f839966dc3819c50a43 Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 18:00:59 +0200 Subject: [PATCH 09/10] =?UTF-8?q?Offline-Plan:=20'Offline-Inhalte=20laden'?= =?UTF-8?q?=20mit=20Bereichsauswahl=20f=C3=BCr=20Mehrtages-Wanderungen?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Zwei Modi: Aktuelle Gegend (budget-getrieben, Gassi) + Bereich auswählen (Mehrtagestour) — Karten-Viewport/Rechteck/Routen-Korridor, Größen-Vorschau vor Download (PMTiles-Directory aufsummieren), Liste gespeicherter Gebiete. Budget gilt im Bereichs-Modus nicht (bewusste Wahl). --- docs/OFFLINE_MAPS_PLAN.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md index ef84bb0..d9889a9 100644 --- a/docs/OFFLINE_MAPS_PLAN.md +++ b/docs/OFFLINE_MAPS_PLAN.md @@ -55,8 +55,20 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau `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:** Button „Diese Gegend offline speichern" für geplante neue Touren (Lernen - deckt nur Bekanntes ab). Fortschritt + „X MB gespeichert" + Löschen/Aktualisieren. +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 geplanten/gewählten Route ± Puffer (ideal für Touren, die einer + Strecke folgen — viel sparsamer als eine große bbox). + - **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. From 80b56c32ab93b73e13bbb039ab42628227e7006e Mon Sep 17 00:00:00 2001 From: rene Date: Fri, 5 Jun 2026 18:03:53 +0200 Subject: [PATCH 10/10] Offline-Plan: Routen-Korridor an Routen-Feature anbinden (fehlende Verbindung) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Einstieg AUS der Route: Button 'Route offline speichern' im Routen-Detail (_openDetail) + Navi-Ansicht, nutzt route.gps_track → Korridor = Tiles im Puffer um den Track. Plus 'Aus meinen Routen wählen' im Offline-Dialog. War als Modus genannt, aber ohne Einstiegspunkt/Routen-Quelle. --- docs/OFFLINE_MAPS_PLAN.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/OFFLINE_MAPS_PLAN.md b/docs/OFFLINE_MAPS_PLAN.md index d9889a9..c4277b2 100644 --- a/docs/OFFLINE_MAPS_PLAN.md +++ b/docs/OFFLINE_MAPS_PLAN.md @@ -62,8 +62,14 @@ Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzerau - **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 geplanten/gewählten Route ± Puffer (ideal für Touren, die einer - Strecke folgen — viel sparsamer als eine große bbox). + - **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).