| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .gitignore | ||
| germany-roads-places.yml | ||
| README.md | ||
Beschriftungs-Kacheln für die Satellitenkarte (ff-operation)
Selbst gehostetes, aus OpenStreetMap gebautes Vektor-Kachel-Overlay ("nur Straßen +
Orts-/Stadtnamen und Gewässer") für die Satellitenansicht ohne Beschriftung. Ohne
Drittanbieter-API-Key und ohne dauerhaft laufenden Rendering-Server — nach dem Bauen genügt
eine einzelne statische .pmtiles-Datei, ausgeliefert per nginx (HTTP-Range-Requests, kein
Rendering zur Laufzeit).
Architektur
- Planetiler wandelt einen Geofabrik-OSM-Extrakt
anhand des Profils
germany-roads-places.ymlin eine.pmtiles-Datei um (Vektor-Kacheln, die Layerroads,railways,placesundwater). Braucht keine Datenbank, läuft als einzelner Docker-Container.maxzoomliegt bei 14 (viaargs.maxzoomim Profil) — das Frontend überzoomt darüber hinaus clientseitig (maxDataZoom: 14inMapView.vue), statt Kacheln für nicht vorhandene höhere Zoomstufen anzufragen. - Die fertige
.pmtiles-Datei wird auf dem Zielserver per nginx als statische Datei ausgeliefert (nginx unterstützt Range-Requests für statische Dateien nativ). - Im Frontend liest protomaps-leaflet die Datei direkt per HTTP-Range-Requests und rendert sie als Canvas-Ebene über der Esri-Satellitenkarte — kein separater Kachel-Server, kein PostGIS.
Build ausführen
Auf einem Rechner/Server mit Docker, ausreichend Speicher (Faustregel: bei einem Deutschland-Extrakt sind ca. 8-16 GB RAM und ein paar Dutzend GB freier Plattenplatz realistisch) und einer stabilen Internetverbindung (der Geofabrik-Extrakt für Deutschland ist mehrere GB groß):
mkdir -p data
cp germany-roads-places.yml data/schema.yml
docker run -v "$(pwd)/data":/data ghcr.io/onthegomap/planetiler:latest \
generate-custom --schema=/data/schema.yml --output=/data/germany-roads-places.pmtiles
Das lädt automatisch den aktuellen Geofabrik-Deutschland-Extrakt herunter (per
geofabrik:germany im Profil) und erzeugt data/germany-roads-places.pmtiles. Je nach
Hardware kann das von einigen Minuten bis über eine Stunde dauern.
Aktualisierung: OSM-Daten ändern sich laufend (neue Straßen, umbenannte Orte). Der Befehl oben kann einfach erneut ausgeführt werden (lädt automatisch einen frischen Extrakt) — z. B. per Cronjob alle paar Wochen/Monate, je nach gewünschter Aktualität. Die neue Datei überschreibt dann einfach die alte auf dem Hosting-Server.
Build unter Windows
Läuft grundsätzlich genauso, ein paar Punkte vorher prüfen:
- Docker Desktop mit WSL2-Backend (nicht Hyper-V) — deutlich schneller bei den vielen kleinen Dateizugriffen, die Planetiler beim Verarbeiten des Extrakts macht. Einstellung unter Docker Desktop → Settings → General → "Use the WSL 2 based engine".
- Speicher-/CPU-Limit von Docker Desktop erhöhen: Docker Desktop deckelt der VM standardmäßig
einen Teil des Host-Speichers, oft weniger als die empfohlenen 8-16 GB. Unter Settings →
Resources (bzw. bei WSL2-Backend über
%UserProfile%\.wslconfig, z. B.memory=16GB,processors=8) passend hochsetzen und Docker Desktop neu starten. - Befehl in einer WSL2-Shell ausführen (z. B. Ubuntu unter WSL), nicht in PowerShell oder
Git Bash — der Befehl oben ist Bash-Syntax (
$(pwd)), und WSL2 übergibt Pfade beim Volume-Mount 1:1 an Docker, ohne die Windows→Linux-Pfadübersetzung, die in PowerShell (${PWD}statt$(pwd)) bzw. Git Bash (bekannte MSYS-Pfad-Stolperfallen bei-v) sonst nötig wäre. - Genug freier Platz auf dem Laufwerk, auf dem WSL2/Docker Desktop seine virtuelle Festplatte
hält (meist
C:, unabhängig davon, wodata/liegt) — mehrere Dutzend GB, siehe oben. - Energiesparen/Ruhezustand deaktivieren, falls auf einem Laptop gebaut wird — der Build kann je nach Hardware über eine Stunde dauern, ein zwischenzeitlicher Sleep bricht ihn ab.
- Optional, nur für Geschwindigkeit: den WSL2-/Docker-Arbeitsordner von der Windows-Defender-Echtzeitprüfung ausnehmen (viele kleine Dateien, häufiger Scan-Overhead) — kein Muss, aber spürbar schneller.
Alternative ohne Docker (direkt mit Java)
Planetiler ist im Kern ein Java-Programm — Docker ist nur eine bequeme Verpackung davon. Ohne Docker geht es auf Windows sogar einfacher, weil die ganze WSL2-/Pfadübersetzungs-Thematik oben komplett entfällt (kein Container, kein Volume-Mount, der Build schreibt direkt ins Windows-Dateisystem):
- Java 21 oder neuer installieren (JDK, nicht nur JRE), z. B. per winget:
winget install EclipseAdoptium.Temurin.21.JDK planetiler.jarherunterladen von der Releases-Seite (direkter Link:.../releases/latest/download/planetiler.jar).- Build starten (PowerShell, im Ordner mit
planetiler.jarundgermany-roads-places.yml):java -Xmx12g -jar planetiler.jar generate-custom --schema=germany-roads-places.yml --output=germany-roads-places.pmtiles-Xmx12greserviert dem Build 12 GB RAM — an den tatsächlich verfügbaren Hauptspeicher anpassen (Faustregel: Gesamt-RAM minus 2-4 GB für Windows selbst). Lädt wie beim Docker-Weg automatisch den Geofabrik-Deutschland-Extrakt herunter (steht bereits im Profil).
Zu beachten bleibt: genug freier Plattenplatz auf dem Laufwerk, auf dem der Befehl ausgeführt wird, Energiesparen/Ruhezustand deaktivieren bei langer Laufzeit. Die Windows-Defender-Ausnahme ist hier sogar eher relevanter als beim Docker/WSL2-Weg, da die Zwischendateien jetzt direkt im normalen (von Defender in Echtzeit überwachten) Windows-Dateisystem statt im von Defender standardmäßig ausgenommenen WSL2-Dateisystem liegen.
Über einen CI-Runner (Forgejo Actions)
Automatisiert statt manuell, z. B. für die regelmäßige Aktualisierung. Wichtig dabei: die Pipeline baut und lädt direkt hoch statt das Ergebnis als Forgejo-Artefakt oder Registry-Image zu speichern — sonst sammelt sich bei jedem Lauf ein mehrere GB großer Blob an, ohne sinnvolles Layer-Diffing (die Datei ändert sich bei jedem Build komplett). So bleibt nie mehr als eine Kopie im Umlauf: die aktuell auf dem Tile-Server liegende.
Voraussetzung ist ein eigener (self-hosted) Forgejo-Runner mit Docker-Zugriff und genug RAM/Plattenplatz (siehe oben) — Forgejo Actions kennt keine geteilten Cloud-Runner wie GitLab.com, der Runner muss also entsprechend dimensioniert selbst betrieben werden.
.forgejo/workflows/build-tiles.yml:
name: Build & Deploy Map Tiles
on:
workflow_dispatch: {} # manuell anstoßbar
schedule:
- cron: "0 3 1 * *" # zusätzlich automatisch monatlich am 1. um 03:00 Uhr
jobs:
build:
runs-on: self-hosted # eigener Runner mit ausreichend RAM/Platz + Docker
steps:
- uses: actions/checkout@v4
- name: .pmtiles mit Planetiler bauen
run: |
docker run --rm -v "$PWD":/data ghcr.io/onthegomap/planetiler:latest \
generate-custom --schema=/data/germany-roads-places.yml --output=/data/germany-roads-places.pmtiles
- name: SSH-Key für den Upload einrichten
run: |
mkdir -p ~/.ssh
echo "${{ secrets.TILES_DEPLOY_SSH_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan tiles.ff-admin.de >> ~/.ssh/known_hosts
- name: Auf den Tile-Server hochladen (überschreibt die bestehende Datei)
run: |
rsync -avz germany-roads-places.pmtiles deploy@tiles.ff-admin.de:/var/www/tiles/germany-roads-places.pmtiles
TILES_DEPLOY_SSH_KEY als Forgejo-Actions-Secret hinterlegen (Repo → Settings → Actions →
Secrets) — der private SSH-Key eines Deploy-Users auf dem Tile-Server, der nur Schreibrechte
auf /var/www/tiles/ braucht. Kein docker push, kein actions/upload-artifact — das
Ergebnis verlässt den Runner ausschließlich per rsync direkt zum Zielserver.
Hosting (Docker + nginx)
Die .pmtiles-Datei wird zentral unter tiles.ff-admin.de gehostet und von jeder
ff-operation-Installation gemeinsam genutzt, unabhängig von deren eigener Domain — kein
Selbstbau oder eigenes Hosting pro Kunde/Deployment nötig. Weil dadurch im Voraus nicht
feststeht, von welcher Origin aus zugegriffen wird, ist Access-Control-Allow-Origin bewusst
offen (*) statt auf eine einzelne Frontend-Domain beschränkt — unproblematisch, weil es eine
rein öffentliche, statische Datei ohne Zugriffsschutz/Credentials ist.
docker-compose.yml:
services:
tiles:
image: nginx:alpine
container_name: ff-operation-tiles
restart: unless-stopped
ports:
- "8080:80" # dahinter ein vorhandener Reverse Proxy für TLS auf tiles.ff-admin.de
volumes:
- ./data:/usr/share/nginx/html:ro # enthält germany-roads-places.pmtiles
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
nginx.conf (daneben liegend, wird oben ins Image gemountet):
server {
listen 80;
server_name tiles.ff-admin.de;
root /usr/share/nginx/html;
location ~ \.pmtiles$ {
# nginx unterstützt Range-Requests für statische Dateien automatisch (sendfile) -
# keine zusätzliche Konfiguration dafür nötig, nur die CORS-Header für den
# Cross-Origin-Zugriff von beliebigen ff-operation-Domains aus.
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, HEAD, OPTIONS" always;
add_header Access-Control-Allow-Headers "Range" always;
add_header Access-Control-Expose-Headers "Content-Length, Content-Range, Accept-Ranges" always;
add_header Cache-Control "public, max-age=86400" always;
# Manche Browser schicken vor der eigentlichen Range-Anfrage eine CORS-Preflight-
# OPTIONS-Anfrage - die hier direkt beantworten, statt sie durchfallen zu lassen.
if ($request_method = OPTIONS) {
return 204;
}
}
}
Start/Update:
docker compose up -d # einmalig starten
docker compose restart tiles # nach einer neuen .pmtiles-Datei in ./data (kein Rebuild nötig,
# nginx liefert einfach die neue Datei; restart nur falls die
# alte noch im Cache eines Clients/Proxys hängt)
Der Container hört auf Port 8080 im internen Netz - TLS-Terminierung für tiles.ff-admin.de
übernimmt der bereits vorhandene Reverse Proxy des Servers (dorthin 8080 weiterleiten), analog
zu den anderen *.ff-admin.de-Diensten.
Frontend-Anbindung
Die URL ist fest in ff-operation unter src/constants/mapTypes.ts (Konstante
LABEL_TILES_URL) hinterlegt — keine Umgebungsvariable, keine Pro-Deployment-Konfiguration,
da die Datei zentral für alle Installationen gehostet wird. MapView.vue rendert sie via
protomaps-leaflet (Styling in src/constants/labelTileStyle.ts). Ändert sich Dateiname oder
Hosting-URL, muss nur diese eine Konstante angepasst werden.
Bekannte Einschränkung
Das Beschriftungs-Overlay ist nicht Teil des bestehenden Offline-Kachel-Caches
(IndexedDB, "Bereich offline verfügbar machen") — der PMTiles-Reader macht sein eigenes
HTTP-Range-Caching, das aber nicht dauerhaft/explizit für Offline-Nutzung vorgesorgt ist. Für
echte Offline-Nutzung der Beschriftung wäre ein zusätzlicher Schritt nötig (z. B. die gesamte
.pmtiles-Datei oder einen Ausschnitt davon in den Service Worker/Cache Storage vorladen) —
bislang nicht umgesetzt, da noch nicht angefragt.
Upload
scp germany-roads-places.pmtiles USER@tiles.ff-admin.de:/pfad/zu/ff-operation-tiles/data/ rsync -avz --progress germany-roads-places.pmtiles USER@tiles.ff-admin.de:/pfad/zu/ff-operation-tiles/data/