banyaro/docs/TILE_SERVER_HANDOVER.md

9.9 KiB
Raw Permalink Blame History

Übergabe: Selbst-gehosteter Tile-Server (Karte + Touren auf eigenen Vektortiles)

An den Kollegen, der das hier im banyaro-Repo weiterbaut. Geschrieben aus dem banyaro-ios-Kontext heraus, nachdem der App-Store-Resubmit (Build 1.0(5)) raus war. Dies ist der Vorbereitungs- + Staging-Test-Plan. Bitte erst „Kontext & Entscheidung" lesen, dann den Staging-Spike ausführen, dann die offenen Punkte mit René klären, bevor irgendwas nach Produktion geht.


1. Worum geht's

Wir wollen Karten selbst hosten (analog Pocket Earth), statt vom öffentlichen OSM-Raster-Server zu ziehen. Damit bauen wir Karte UND Touren auf eigenen Vektortiles auf — Web (PWA) und nativ (iOS).

Warum überhaupt:

  • Lizenz/Policy: Die PWA nutzt aktuell https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png (backend/static/js/ui.jsMap.OSM_URL, offline-indicator.js). Die OSMF-Tile-Usage-Policy verbietet heavy/kommerzielle Nutzung — auf Dauer kein tragfähiges Fundament.
  • Offline: iOS soll Regionen offline vorhalten (Pocket-Earth-Modell). Mit eigenen Tiles ist das sauber machbar, mit dem öffentlichen Raster-Server nicht.
  • Kontrolle & Konsistenz: Eine Tile-/Style-Quelle für Web + iOS, kein Drittanbieter-Limit, Retina/Vektor-Styling, eigenes Karten-Design möglich.
  • Companion-Prinzip: Substanz lebt auf banyaro.app — die Tile-Infrastruktur gehört genau hierher (DS/Docker/NPM/Staging sind alle in diesem Repo). Die iOS-App ist nur das native Fenster.

Bewusst NICHT Teil des laufenden App-Store-Resubmits — das ist eigener Scope nach der Freigabe (andere Risikoklasse: Infra/Ops, Heim-Uplink-Verfügbarkeit; kein offener Apple-Punkt). Siehe iOS-Memory project_app_review_build3 / project_build4_karte.


2. Ist-Zustand (im Repo verifiziert)

  • Karte Web: Leaflet + leaflet.markercluster, Raster-Basemap vom öffentlichen OSM-Server. Helper in backend/static/js/ui.js (Map.OSM_URL, L.tileLayer(...)), weitere Vorkommen in ui.js:1006 und offline-indicator.js:193.
  • Karte iOS: aktuell Apple MapKit (im banyaro-ios-Repo). Soll später auf MapLibre Native + Offline-Regionen umgestellt werden — separater iOS-Workstream, dieser Tile-Server ist die Voraussetzung dafür.
  • Deployment: ein Docker-Service banyaro (FastAPI :8000 → DS :3010) hinter NPM (Nginx Proxy Manager). Static-Files via FastAPI-StaticFiles-Mounts (/css, /js, /icons, /img in backend/main.py:369372). Volume ./data:/data.
  • Staging: eigener Container banyaro-staging, docker-compose.staging.yml, Pfad /volume1/docker/banyaro-staging, URL https://staging.banyaro.app, Deploy via make staging (pusht develop).
  • DS-Zugang: Host ds (10.47.11.10, SSH-Port 4711), sudo docker.
  • Tile-Pipeline: existiert noch nicht (keine pbf/mbtiles/pmtiles im Repo). Es gibt konzeptionell validierte OSM-POI-Pipeline-Notizen (iOS-Memory project_build4_pois) — separat von den Basemap-Tiles, aber gleiche pbf-Quelle.

3. Empfohlene Architektur: planetiler → PMTiles → MapLibre

Für einen Heim-Server (DiskStation) mit Offline-Anspruch ist das die klar beste Wahl — leichter als ein klassischer Raster-Renderer und ohne laufenden Render-Prozess:

OSM-Extract (.osm.pbf, Geofabrik)
        │  planetiler (OpenMapTiles-Schema, einmalig + bei Updates)
        ▼
   region.pmtiles   ← EIN Single-File-Tile-Archiv (Vektor)
        │  per HTTP Range-Requests ausgeliefert (nginx/FastAPI StaticFiles → 206)
        ▼
   MapLibre  ── Web: MapLibre GL JS + pmtiles-Protokoll (ersetzt Leaflet-Raster)
             └─ iOS: MapLibre Native, Offline = .pmtiles lokal auf dem Gerät
        +  Style-JSON + Glyphs (Fonts) + Sprite  (statisch gehostet)
        +  Touren = GeoJSON-Linien-Layer obendrauf (Basemap-unabhängig)

Warum PMTiles (und nicht ein Raster-Tile-Server):

  • Kein Server-Prozess, kein PostGIS, kein renderd/Mapnik. Eine Datei, ausgeliefert per Range-Request — die DS muss zur Laufzeit nichts rendern. Ideal für schwache Hardware + Heim-Uplink.
  • Offline trivial: dieselbe .pmtiles lokal auf dem iPhone → MapLibre liest direkt. Das ist das Pocket-Earth-Modell.
  • Vektor: Retina-scharf, eigenes Styling, Labels drehbar, klein.

Verworfen — openstreetmap-tile-server / renderd+Mapnik (Raster): schwerer PostGIS-Import, CPU-Rendering pro Request, große Storage, schlechte Offline-Story, kein Vektor-Styling. Für unseren Fall strikt unterlegen.

Stack-Komponenten:

Teil Tool Hinweis
Tile-Generierung planetiler (Docker) OpenMapTiles-Schema, Output direkt .pmtiles
Auslieferung nginx/NPM oder FastAPI StaticFiles beide können Range-Requests (206); für Spike reicht StaticFiles
Web-Client maplibre-gl + pmtiles (JS) ersetzt Leaflet schrittweise (Feature-Flag)
iOS-Client MapLibre Native (eigener Workstream) Offline-Region = lokale .pmtiles
Style OpenMapTiles-kompatibel (z. B. Positron/OSM-Bright) + Glyphs + Sprite selbst hosten für volle Unabhängigkeit

4. Staging-Spike (konkret, zum Loslegen)

Ziel: eine kleine Region als .pmtiles erzeugen, auf staging ausliefern, mit MapLibre rendern — und Größe/Zeit/Performance/Range-Requests messen. Erst klein (Bayern), nicht gleich DACH.

4.1 Extract + Tiles erzeugen (lokal/Build-Maschine, NICHT auf der DS)

mkdir -p tiles/build && cd tiles/build
# planetiler lädt den Geofabrik-Extract selbst und schreibt direkt PMTiles:
docker run --rm -v "$PWD:/data" ghcr.io/onthegomap/planetiler:latest \
  --download --area=bayern --output=/data/bayern.pmtiles
# Ergebnis: bayern.pmtiles  (grobe Schätzung: paar hundert MB, wenige Minuten)

DACH gibt es bei Geofabrik nicht als ein Extract → später germany + austria + switzerland per osmium merge zusammenführen, dann planetiler darauf. Für den Spike reicht bayern.

4.2 Auf Staging ausliefern (einfachster Weg: FastAPI StaticFiles vom data-Volume)

Die große Datei NICHT ins Image bauen — ins ./data-Volume legen und mounten.

# backend/main.py — nahe der bestehenden StaticFiles-Mounts (Z. 369ff)
import os
_TILES_DIR = os.getenv("TILES_DIR", "/data/tiles")
if os.path.isdir(_TILES_DIR):
    app.mount("/tiles", StaticFiles(directory=_TILES_DIR), name="tiles")
  • bayern.pmtiles nach /volume1/docker/banyaro-staging/data/tiles/ kopieren (scp zur DS), dann make staging.
  • Starlette FileResponse beherrscht Range-Requests → MapLibre/pmtiles bekommt 206.
  • Verifizieren: curl -I -H "Range: bytes=0-1023" https://staging.banyaro.app/tiles/bayern.pmtiles muss HTTP/1.1 206 Partial Content + Accept-Ranges: bytes liefern. Prüfen, dass NPM die Range-Header nicht verschluckt.

4.3 MapLibre-Testseite (Feature-Flag, Leaflet bleibt Fallback)

  • maplibre-gl + pmtiles einbinden, Protokoll registrieren:
    const p = new pmtiles.Protocol();
    maplibregl.addProtocol('pmtiles', p.tile);
    // source: { type:'vector', url:'pmtiles://https://staging.banyaro.app/tiles/bayern.pmtiles' }
    
  • Style-JSON (OpenMapTiles-Schema) + Glyphs + Sprite hosten (z. B. unter /tiles/style/). Fertige freie Styles: OSM-Bright / Positron (maputnik-kompatibel). Glyphs z. B. aus openmaptiles/fonts. Für den Spike darf der Style minimal sein.
  • Touren-Polyline als GeoJSON-line-Layer auf die Map legen (ist basemap-unabhängig — sobald MapLibre läuft, ist die Tour nur noch ein Layer).

4.4 Messen & festhalten

  • Dateigröße + Generierungszeit (Bayern, dann hochrechnen auf DACH).
  • Render-Performance über den Heim-Uplink (erste Zoomstufen, Labels).
  • DS-Storage-Budget (/volume1/... frei?).
  • Range-Requests durch NPM ok? CORS nötig (falls Tiles auf anderer Subdomain als App)?

5. Offene Entscheidungen (mit René klären)

  1. Region-Scope: DACH am Stück vs. Deutschland-only vs. herunterladbare Regionen (für iOS-Offline granular). DACH-PMTiles grob ~23 GB (verifizieren).
  2. Hosting-Pfad: Pfad (/tiles an der App) vs. eigene Subdomain (tiles.banyaro.app) hinter NPM. Subdomain = sauberer cachebar, aber CORS-Setup. Für Skalierung ggf. nginx direkt statt FastAPI StaticFiles (kein App-CPU).
  3. Style: welcher Basis-Style (Positron/OSM-Bright/eigenes Hunde-Theme), Glyphs/Sprite selbst hosten.
  4. Update-Kadenz: wie oft aus frischem OSM-Extract neu generieren (monatlich?). planetiler-Rerun + Datei tauschen (atomar). Cron/Make-Target dafür.
  5. iOS-Workstream: MapLibre Native + Offline-Region-Download ersetzt MapKit — eigener Build-4-Task im banyaro-ios-Repo, nach dieser Infra.
  6. Attribution (Pflicht): „© OpenStreetMap contributors" (ODbL) muss in Web und iOS sichtbar sein. Bei eigenem Style ggf. zusätzliche Datenquellen-Hinweise.

6. Vorgeschlagene Reihenfolge

  1. Staging-Spike (Abschnitt 4) mit Bayern — Proof of Concept, Zahlen sammeln.
  2. Entscheidungen aus Abschnitt 5 mit René.
  3. DACH-PMTiles generieren (osmium merge → planetiler), Update-Make-Target.
  4. PWA: MapLibre hinter Feature-Flag produktiv, Leaflet-Pfad rausnehmen wenn stabil.
  5. iOS (separat): MapLibre Native + Offline-Regionen, MapKit ablösen.

7. Querverweise

  • iOS-Memory: project_build4_karte (Entscheidung OSM/MapLibre/Offline), project_build4_pois (POI-Pipeline aus pbf — gleiche Datenquelle), project_osm_contribution (OSM-Beitragskreislauf, gehört in die PWA), project_companion (Companion-Prinzip), project_app_review_build3 (warum nicht im Resubmit).
  • NPM/IPv6/Reverse-Proxy-Stolpersteine auf der DS: Skill synology-troubleshooting.
  • Deploy: make staging (→ staging.banyaro.app), make deploy (→ Produktion, deployt den Arbeitsbaum). make bump NUR bei Frontend-Asset-Änderungen (SW-Cache).