Compare commits

...

13 commits

Author SHA1 Message Date
c01ddeed31 feat: docker-compose.build.yml – Europa z16 Build-Config, 12 CPUs 28GB RAM 2026-06-13 10:41:49 +00:00
83492ff96a feat: docker-compose.build.yml – Planetiler Build-Container für z16 auf Host-Rechner 2026-06-13 10:31:21 +00:00
407f6c4faf fix: satellite-download OOM gefixt (bounded queue), 16 Threads, 2GB Limit 2026-06-13 10:12:48 +00:00
6b5009f51e feat: satellite-download als eigenständiger Compose-Stack, build-host.sh, Script robust 2026-06-13 09:46:09 +00:00
c47f4785b1 docs: ADR-002 – kein Self-Build z16/Satellit auf aktueller HW, Constraints dokumentiert 2026-06-13 09:20:22 +00:00
4268b419b8 feat: vollständiger OSM Bright GL Style (128 Layer) mit Wanderwegen + POIs 2026-06-13 06:41:54 +00:00
9081ed91a5 fix: glyphs URL absolute – font loading working at all zoom levels 2026-06-13 06:26:44 +00:00
2765bb93f3 docs: project context updated – stack fully operational 2026-06-13 2026-06-13 06:19:38 +00:00
46ac204e03 feat: tileserver-gl fully operational – config+style in config/, PNG tiles confirmed 2026-06-12 09:43:07 +00:00
79bba7ca02 feat: tileserver-gl on port 9983, docs updated 2026-06-12 09:29:50 +00:00
f4fbb690be chore: update copilot instructions and reorganize history into docs/ 2026-06-12 09:26:01 +00:00
ba1d888ef2 docs: ADR-001 Martin + tileserver-gl Stack-Entscheidung 2026-06-12 09:24:45 +00:00
dbebc96c2e docs: align tile hostnames 2026-06-10 08:30:08 +00:00
21 changed files with 7572 additions and 176 deletions

View file

@ -5,11 +5,11 @@
> Diese Sektion hat höchste Priorität und darf nicht ignoriert werden.
**Session START** Bevor du irgendetwas tust:
1. Lies `history/summary/PROJECT_CONTEXT.md` (falls vorhanden)
1. Lies `docs/history/summary/PROJECT_CONTEXT.md` (falls vorhanden)
2. Verstehe den aktuellen Projektzustand bevor du anfängst
**Session END** Der Agent führt dies **automatisch** am Ende jeder Aufgabe aus, bevor er `git commit` ausführt:
1. Lies die bestehende `history/prompts/YYYY-MM-DD_*_session.md` (falls vorhanden) und ergänze nur den neuen Teil
1. Lies die bestehende `docs/history/prompts/YYYY-MM-DD_*_session.md` (falls vorhanden) und ergänze nur den neuen Teil
**Dateiformat zwingend:** Suffix `_session.md`, Dateiname beginnt mit heutigem Datum (`YYYY-MM-DD`)
2. **Für jeden Benutzer-Prompt seit dem letzten Commit** einen Block ergänzen:
```
@ -21,9 +21,9 @@
```
⚠ Kein „Nachtrag"-Block statt echter Prompt-Einträge. Die Benutzer-Nachricht **wörtlich** zitieren.
⚠ Keine reinen Stichpunkt-Zusammenfassungen. Ausgeführte Kommandos und Outputs gehören **wörtlich** in die Antwort.
⚠ Keine Platzhalter wie `PENDING` committen. Ist der Commit-Hash noch unbekannt: Hash weglassen oder Datei direkt nach dem Commit mit dem echten Hash aktualisieren und sofort (`git add history/ && git commit`) nachziehen.
3. Aktualisiere `history/summary/PROJECT_CONTEXT.md` mit dem neuen Projektstand
4. **Stage beide Dateien VOR `git commit`:** `git add history/`
⚠ Keine Platzhalter wie `PENDING` committen. Ist der Commit-Hash noch unbekannt: Hash weglassen oder Datei direkt nach dem Commit mit dem echten Hash aktualisieren und sofort (`git add docs/history/ && git commit`) nachziehen.
3. Aktualisiere `docs/history/summary/PROJECT_CONTEXT.md` mit dem neuen Projektstand
4. **Stage beide Dateien VOR `git commit`:** `git add docs/history/`
Der pre-commit Hook (Check 6) blockiert wenn:
- keine `*_session.md` gestaged ist
- der Dateiname nicht mit dem heutigen Datum beginnt
@ -41,6 +41,23 @@
- **`git push --force`** Nie force-pushen ohne explizite Anweisung des Users.
- **Secrets in Code** Keine Passwörter, API-Keys, Tokens in Dateien.
## ⚠ Bekannte Fallstricke (nicht vergessen!)
- **Scripts mit Self-Update (`copilot-update.sh/.fish`) immer erst committen+pushen, DANN manuell deployen.**
Reihenfolge zwingend:
1. Änderung im Repo vornehmen
2. `git add … && git commit && git push`
3. `cp scripts/copilot-update.sh ~/.local/bin/copilot-update.sh`
4. Testen
Falsche Reihenfolge (erst deployen, dann committen) → Self-Update überschreibt den manuell deployten Fix
mit der alten Version aus dem Remote-Cache. Der Fix geht verloren.
- **`fi ───` oder `end ───` in POSIX-sh/fish:** Box-Drawing-Zeichen nie direkt ans `fi`/`end` anhängen.
Kommentar-Trennlinie immer auf eigener Zeile: `# ── Abschnitt ──`
- **`printf '%s\n%s\n'` frisst trailing newlines** aus `$(awk ...)` Substitution.
Wenn eine Leerzeile zwischen zwei Blöcken erhalten bleiben soll: `printf '%s\n\n%s\n'` verwenden.
---
## Project

3
.gitignore vendored
View file

@ -1,6 +1,9 @@
# Datendateien nicht im Repo (können mehrere GB groß sein)
data/*
# Planetiler Build-Output (Host-Build, nicht auf dem Server)
build/
# Laufzeit
.env

6109
config/styles/osm-intl.json Normal file

File diff suppressed because it is too large Load diff

20
config/tileserver.json Normal file
View file

@ -0,0 +1,20 @@
{
"options": {
"paths": {
"fonts": "/data/fonts",
"sprites": "/data/sprites",
"styles": "/data/styles",
"mbtiles": "/data"
}
},
"styles": {
"osm-intl": {
"style": "osm-intl.json"
}
},
"data": {
"osm": {
"mbtiles": "osm.mbtiles"
}
}
}

76
docker-compose.build.yml Normal file
View file

@ -0,0 +1,76 @@
# Planetiler Build Vektor-Tiles für höhere Zoom-Level
#
# LÄUFT AUF DEM HOST-RECHNER (nicht auf dem Server).
# Voraussetzung: Docker Desktop (macOS/Windows) oder Docker Engine (Linux)
#
# ══════════════════════════════════════════════════════════════════════
# DISK-ANFORDERUNGEN (auf dem Host-Rechner)
# ══════════════════════════════════════════════════════════════════════
#
# AREA=germany MAX_ZOOM=16: ~30 GB temp + ~12 GB output = ~50 GB frei
# AREA=europe MAX_ZOOM=16: ~200 GB temp + ~80 GB output = ~350 GB frei
# AREA=planet MAX_ZOOM=15: ~200 GB temp + ~80 GB output = ~350 GB frei
# AREA=planet MAX_ZOOM=16: ~500 GB temp + ~300 GB output = ~900 GB frei
#
# ══════════════════════════════════════════════════════════════════════
# SCHRITT 1: Build starten
# ══════════════════════════════════════════════════════════════════════
#
# # Germany z0-16 (empfohlen als Einstieg)
# AREA=germany MAX_ZOOM=16 RAM=8g docker compose -f docker-compose.build.yml up
#
# # Planet z0-15 (~80 GB Output, ~8h, 28 GB RAM, ~350 GB Temp+Output Disk)
# AREA=planet MAX_ZOOM=15 RAM=28g BUILD_CPUS=12 BUILD_MEM=30g \
# docker compose -f docker-compose.build.yml up
#
# # Planet z0-16 (~300 GB Output, ~15h, 28 GB RAM, ~900 GB Temp+Output Disk)
# AREA=planet MAX_ZOOM=16 RAM=28g BUILD_CPUS=12 BUILD_MEM=30g \
# docker compose -f docker-compose.build.yml up
#
# Status verfolgen (zweites Terminal):
# docker compose -f docker-compose.build.yml logs -f
#
# ══════════════════════════════════════════════════════════════════════
# SCHRITT 2: Ergebnis auf Server übertragen
# ══════════════════════════════════════════════════════════════════════
#
# rsync -avh --progress --partial \
# ./build/output/<AREA>.mbtiles \
# rd13server@192.168.178.6:/mnt/services-data/rd13_tile_server/data/osm-<AREA>.mbtiles
#
# ══════════════════════════════════════════════════════════════════════
# SCHRITT 3: Martin auf dem Server neu starten
# ══════════════════════════════════════════════════════════════════════
#
# ssh rd13server@192.168.178.6 \
# "cd /mnt/services-data/rd13_tile_server && docker compose restart martin"
#
# # Neue Source prüfen:
# curl https://tiles.rd13server.de/catalog | python3 -m json.tool | grep -i area
services:
planetiler-build:
image: ghcr.io/onthegomap/planetiler:latest
container_name: rd13_planetiler_build
volumes:
# Output: fertige MBTiles
- ./build/output:/output
# Source-Cache: OSM-PBF wird hier gecacht (verhindert Re-Download bei Neustart)
- ./build/sources:/data/sources
# Temp: sehr viel Platz nötig (siehe Tabelle oben)
- ./build/tmp:/data/tmp
environment:
- JAVA_TOOL_OPTIONS=-Xmx${RAM:-8g} -Xms2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200
command:
- "--download"
- "--area=${AREA:-germany}"
- "--output=/output/${AREA:-germany}.mbtiles"
- "--maxzoom=${MAX_ZOOM:-16}"
- "--nodemap-type=sortedtable"
- "--force"
restart: "no"
deploy:
resources:
limits:
cpus: "${BUILD_CPUS:-4.0}"
memory: ${BUILD_MEM:-12g}

View file

@ -0,0 +1,42 @@
# Satellite-Download eigenständiger Container
#
# Läuft vollständig unabhängig von VS Code / SSH-Sessions.
# Resume-fähig: bereits heruntergeladene Tiles werden übersprungen.
#
# STARTEN:
# cd /mnt/services-data/rd13_tile_server
# docker compose -f docker-compose.download.yml up
#
# STATUS (in zweitem Terminal):
# docker compose -f docker-compose.download.yml logs -f
#
# STOPPEN (Download pausieren):
# docker compose -f docker-compose.download.yml stop
#
# FORTSETZEN:
# docker compose -f docker-compose.download.yml up
#
# ERGEBNIS:
# data/satellite.mbtiles (~38 GB bei MAX_ZOOM=11)
services:
satellite-download:
image: python:3.12-slim
container_name: rd13_satellite_download
volumes:
- ./data:/data
- ./scripts:/scripts:ro
- ./logs:/logs
environment:
- SAT_MAX_ZOOM=11
- SAT_THREADS=16
- PYTHONUNBUFFERED=1
command: ["python3", "-u", "/scripts/download-satellite.py", "/data/satellite.mbtiles"]
restart: "no"
deploy:
resources:
limits:
cpus: "4.0"
memory: 2048M
reservations:
memory: 128M

View file

@ -15,3 +15,24 @@ services:
timeout: 10s
retries: 3
start_period: 10s
tileserver:
image: maptiler/tileserver-gl:latest
container_name: rd13_tileserver
ports:
- "9983:8080"
volumes:
- ./config/tileserver.json:/data/config.json:ro
- ./config/styles:/data/styles:ro
- ./data/fonts:/data/fonts:ro
- ./data/sprites:/data/sprites:ro
- ./data/osm.mbtiles:/data/osm.mbtiles:ro
restart: unless-stopped
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:8080/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
depends_on:
- martin

View file

@ -1,20 +1,19 @@
# Administrator-Handbuch [PROJEKT_NAME]
# Administrator-Handbuch rd13_tile_server
> Zielgruppe: **Administratoren** Menschen, die das System betreiben, deployen und konfigurieren.
> Voraussetzung: Grundkenntnisse in Linux, Docker und dem eingesetzten Stack.
> Voraussetzung: Grundkenntnisse in Linux, Docker und der eingesetzten Stack (Martin, tileserver-gl).
---
## Systemvoraussetzungen
<!-- TODO: Anforderungen an Host/Server -->
| Komponente | Mindestanforderung | Empfohlen |
|---|---|---|
| OS | … | … |
| RAM | … | … |
| Disk | … | … |
| Dependencies | … | … |
| OS | Linux 5.10+ (oder macOS 12+) | Ubuntu 22.04 LTS |
| RAM | 1 GB | 2-4 GB |
| Disk | 5 GB (OS + Containers) | 20 GB (mit Cache-Warmup) |
| Dependencies | Docker 20.10+, docker-compose 2.0+ | Docker Desktop / Compose v2 |
| Network | Port 9982 (Martin), Port 8080 (tileserver-gl) internal | Public domain via NPM reverse proxy |
---
@ -22,32 +21,256 @@
### Erstes Aufsetzen
<!-- TODO: Schritt-für-Schritt Deployment -->
```bash
# Beispiel
git clone <repo> /opt/service
cd /opt/service
cp .env.example .env
# .env ausfüllen!
# Repository klonen
git clone ssh://git@192.168.178.6:2222/cschulz/rd13_tile_server.git /mnt/services-data/rd13_tile_server
cd /mnt/services-data/rd13_tile_server
# Daten-Verzeichnis initialisieren (falls nicht vorhanden)
mkdir -p data/{fonts,sprites,styles}
# Services starten
docker compose up -d
# Health-Checks verifizieren
docker compose ps
curl -s http://localhost:9982/health | jq .
curl -s http://localhost:8080/health
```
### Updates einspielen
### Updates einspielen (Tile-Daten)
<!-- TODO: Update-Prozedur -->
**Szenario: osm.mbtiles aktualisieren (24×/Jahr)**
```bash
git pull
# 1. Neue MBTiles herunterladen (z.B. von OpenFreeMap)
cd /mnt/services-data/rd13_tile_server
python3 scripts/download-data.sh data/osm.mbtiles.new
# 2. Alte Datei backup-en
mv data/osm.mbtiles data/osm.mbtiles.$(date +%Y%m%d)
# 3. Neue Datei aktivieren
mv data/osm.mbtiles.new data/osm.mbtiles
# 4. Container neu starten (warm caches)
docker compose restart martin tileserver
# 5. Health-Check + Sampling testen
curl -s http://localhost:9982/osm | jq .
curl -s "http://localhost:9983/osm-intl/0/0/0.png" > /tmp/test.png && file /tmp/test.png
```
### Höhere Zoom-Level bauen (auf Host-Rechner, nicht auf dem Server)
Planetiler läuft in einem eigenen Container auf dem **Host-Rechner** (MacBook, Desktop-PC
etc.) mit genug RAM. Das Ergebnis wird per `rsync` auf den Server übertragen.
**Europa z16 (empfohlen):** ~280 GB Host-Disk, ~812h, 12 CPUs / 28 GB RAM
```bash
# Auf dem HOST-RECHNER ausführen (im geklonten Repo-Verzeichnis):
# Europa z0-16 (~80 GB Output, ~8-12h, ~280 GB Disk nötig)
AREA=europe MAX_ZOOM=16 RAM=28g BUILD_CPUS=12 BUILD_MEM=30g \
docker compose -f docker-compose.build.yml up
# Germany z0-16 (kleiner Test: ~12 GB Output, ~2h, ~50 GB Disk)
AREA=germany MAX_ZOOM=16 RAM=8g \
docker compose -f docker-compose.build.yml up
# Fertiges MBTiles auf Server übertragen (resume-fähig):
rsync -avh --progress --partial \
./build/output/europe.mbtiles \
rd13server@192.168.178.6:/mnt/services-data/rd13_tile_server/data/osm-europe.mbtiles
# Martin auf dem Server neu starten:
ssh rd13server@192.168.178.6 \
"cd /mnt/services-data/rd13_tile_server && docker compose restart martin"
# Neue Source prüfen:
curl https://tiles.rd13server.de/catalog | python3 -m json.tool | grep osm-europe
```
Build-Output liegt lokal unter `./build/output/<AREA>.mbtiles` (gitignored).
Sources-Cache unter `./build/sources/` bei Rebuild nicht löschen (spart Re-Download).
### Satellit-Tiles herunterladen (einmalig, ~38 GB, ~24h)
Der Download läuft in einem eigenen Container vollständig unabhängig von
SSH-Sessions und VS Code. Resume-fähig: bei Unterbrechung einfach neu starten.
**Ressourcen:** 16 Threads, 2 GB RAM-Limit, 4 CPUs konfigurierbar in `docker-compose.download.yml`.
```bash
cd /mnt/services-data/rd13_tile_server
# Starten (läuft im Vordergrund, Logs direkt sichtbar)
docker compose -f docker-compose.download.yml up
# Status in zweitem Terminal verfolgen
docker compose -f docker-compose.download.yml logs -f
# Pausieren
docker compose -f docker-compose.download.yml stop
# Fortsetzen (bereits heruntergeladene Tiles werden übersprungen)
docker compose -f docker-compose.download.yml up
```
Wenn fertig: `data/satellite.mbtiles` (~38 GB) liegt bereit.
Martin lädt sie automatisch beim nächsten Restart als Source `satellite`.
### Service Restart / Update Images
```bash
# Nur Image-Updates (Martin, tileserver-gl)
docker compose pull
docker compose up -d --force-recreate
docker compose up -d
# Bei Compose-Konfiguration-Änderungen
docker compose down
docker compose up -d
# Logs ansehen
docker compose logs -f martin # Martin Vektor-API
docker compose logs -f tileserver # Raster-Rendering
```
---
## Konfiguration
### Umgebungsvariablen (`.env`)
### Tile-Server-Komponenten
**Martin (Vektor-Tiles)**
- Konfiguration: `config/martin.yaml`
- API Root: `http://localhost:9982`
- Catalog: `http://localhost:9982/catalog`
- Datenquelle: `data/osm.mbtiles`
**tileserver-gl (Raster-PNG-Rendering)**
- Konfiguration: Styles unter `data/styles/`
- API Root: `http://localhost:9983` (exposed port)
- Style `osm-intl`: `data/styles/osm-intl.json`
- Fonts: `data/fonts/`
- Sprites: `data/sprites/`
### Reverse Proxy (NPM) Öffentliche URLs
**Konfiguration (manuell im NPM WebUI):**
1. **Neuer Proxy-Host:** `tiles.rd13server.de`
- Forward Hostname/IP: `martin` (Docker-DNS)
- Forward Port: `3000`
- Schema: `http`
- Websockets Support: ☑
2. **Advanced Tab Custom Nginx Config:**
```nginx
# Rate Limiting
limit_req_zone $binary_remote_addr zone=tiles:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=tile_conn:10m;
location / {
limit_req zone=tiles burst=60 nodelay;
limit_conn tile_conn 20;
limit_req_status 429;
limit_conn_status 429;
# Cache-Header für Tiles
add_header Cache-Control "public, max-age=86400, stale-while-revalidate=3600" always;
add_header Vary "Accept-Encoding" always;
}
# Raster-Routing zu tileserver-gl
location ~ ^/osm-intl/ {
proxy_pass http://localhost:9983;
proxy_ssl_server_name on;
add_header Cache-Control "public, max-age=86400" always;
}
```
---
## Betrieb & Monitoring
### Health-Checks manuell
```bash
# Martin Vektor-Tiles
curl -s http://localhost:9982/health
# tileserver-gl Raster
curl -s http://localhost:9983/health
# Sample-Tile abrufen (PNG)
curl -s "http://localhost:9983/osm-intl/0/0/0.png" -o /tmp/tile.png
file /tmp/tile.png
```
### Performance-Baseline
**Erwartete Antwortzeiten (ohne externen Upstream):**
- Martin `/osm/{z}/{x}/{y}.pbf`: <50ms (cache warm)
- tileserver-gl `/osm-intl/{z}/{x}/{y}.png`: <100ms (cache cold), <20ms (cache warm)
**Caches (auto-warm nach Start):**
- Martin: in-memory (fast)
- tileserver-gl: Disk unter `/tmp/pmtiles-cache` (wächst mit Nutzung)
### Logs
```bash
# Alle Services
docker compose logs
# Nur Fehler
docker compose logs --tail=100 | grep -i error
# Real-time verfolgen
docker compose logs -f
```
---
## Troubleshooting
| Problem | Ursache | Lösung |
|---|---|---|
| `tiles.rd13server.de` liefert 502 | Martin-Container nicht erreichbar | `docker compose ps` + `docker compose logs martin` |
| PNG-Tiles sind grau/leer | tileserver-gl kann Martin nicht erreichen | Docker-Network prüfen: `docker network inspect rd13_tile_server_default` |
| Lange Antwortzeiten | Tile-Cache kalt, erste Render | Normal; nach 1020 Requests warm; monitoring via logs |
| Out of Memory | Cache wächst unbegrenzt | tileserver-gl Speicher-Limit setzen oder Cache-TTL anpassen |
---
## Wartungsplan
| Task | Frequenz | Aufwand |
|---|---|---|
| Tile-Daten aktualisieren | 24×/Jahr | 15 min (Script + Restart) |
| Container-Images patchen | Wöchentlich | 5 min (`docker compose pull && up -d`) |
| Logs archivieren | Monatlich | 5 min (via logrotate) |
| Disk-Space überwachen | Wöchentlich | 2 min (du/df) |
---
## Rollback-Procedure
**Falls neue osm.mbtiles kaputt ist:**
```bash
cd /mnt/services-data/rd13_tile_server
docker compose stop martin tileserver
# Alte Version zurückbringen
rm data/osm.mbtiles
mv data/osm.mbtiles.YYYYMMDD data/osm.mbtiles
docker compose up -d
```
<!-- TODO: Alle relevanten Env-Vars dokumentieren -->

View file

@ -1,53 +1,268 @@
# Maintainer-Handbuch [PROJEKT_NAME]
# Maintainer-Handbuch rd13_tile_server
> Zielgruppe: **Entwickler & Maintainer** Menschen, die den Code verstehen, erweitern oder reviewen.
> Voraussetzung: Entwicklungserfahrung mit dem eingesetzten Stack.
> Voraussetzung: Entwicklungserfahrung mit Docker, MapLibre GL Styles, OSM-Daten.
---
## Architektur
<!-- TODO: Beschreibe die Architektur (C4, Hexagonal, Event-Driven, etc.) -->
## Architektur Martin + tileserver-gl Stack
```
┌──────────────────────────────────────────────────────────┐
│ TODO: Architektur-Diagramm (ASCII oder Mermaid) │
└──────────────────────────────────────────────────────────┘
Internet (HTTPS)
NPM Reverse Proxy (tiles.rd13server.de, Rate Limiting)
├─ /osm/{z}/{x}/{y}.pbf → Martin (Vektor-Tiles)
└─ /osm-intl/{z}/{x}/{y}.png → tileserver-gl (Raster-PNG)
tileserver-gl renders Martin Vektor → PNG
Martin reads osm.mbtiles (Vector Tiles)
/data/osm.mbtiles (OpenFreeMap snapshot)
```
**Wichtige Komponenten:**
**Stack-Komponenten:**
| Komponente | Verantwortlichkeit | Ort |
|---|---|---|
| … | … | `src/…` |
| Komponente | Input | Output | Verantwortung |
|---|---|---|---|
| **Martin** | `osm.mbtiles` | `/{layer}/{z}/{x}/{y}.pbf` | Vektor-API, Tile-Distribution |
| **tileserver-gl** | Martin PBF + `osm-intl.json` | `/{style}/{z}/{x}/{y}.png` | Rendering, PNG-Tile-Cache |
| **NPM** | HTTP Client Requests | HTTPS Responses | Proxy, SSL, Rate-Limit |
**Verwandte ADRs:**
- [ADR-001: Tile Rendering Stack Martin + tileserver-gl](docs/adr/001-martin-tileserver-gl-stack.md)
---
## Projektstruktur
```
[PROJEKT_NAME]/
├── data/ ← Persistente Daten (gitignored)
│ └── <service>/ ← Ein Unterordner pro Service
rd13_tile_server/
├── config/
│ └── martin.yaml ← Martin Server-Konfiguration
├── data/
│ ├── osm.mbtiles ← OpenFreeMap Vector-Snapshot (~94 GB)
│ ├── styles/
│ │ └── osm-intl.json ← MapLibre GL Style für Raster-Rendering
│ ├── fonts/
│ │ ├── Metropolis Black/
│ │ ├── Open Sans Regular/
│ │ └── … (47 weitere Font-Varianten)
│ └── sprites/ ← Marker/Icons (derzeit leer)
├── docs/
│ ├── USER.md ← Endnutzer-Dokumentation
│ ├── ADMIN.md ← Administrator-Dokumentation
│ └── MAINTAINER.md ← Dieses Dokument
├── history/
│ ├── prompts/ ← Vollständige Agent-Konversationen (gitignored)
│ └── summary/
│ └── PROJECT_CONTEXT.md ← Aktueller Projektzustand für Agent-Kontext
└── … ← Source Code
│ ├── adr/
│ │ ├── 000-template.md
│ │ └── 001-martin-tileserver-gl-stack.md
│ ├── history/
│ ├── requirements/
│ │ └── REQUIREMENTS.md
│ ├── USER.md, ADMIN.md, MAINTAINER.md
│ └── … (API-Endpoints, Integration-Guides)
├── docker-compose.yml ← Martin + tileserver-gl
└── scripts/
├── download-data.sh ← OSM-MBTiles fetcher
└── download-satellite.py ← Satellit-MBTiles fetcher (optional)
```
---
## Entwicklungsumgebung aufsetzen
## Style-Customization (osm-intl.json)
<!-- TODO: Wie richtet man die lokale Entwicklungsumgebung ein? -->
Die Kartographer-Karten werden von `data/styles/osm-intl.json` definiert. Das ist eine Standard **MapLibre GL Style** (v8-kompatibel).
### Tipps zum Ändern
**1. Farben anpassen** (einfach)
```json
"layers": [
{
"id": "water-area",
"paint": {
"fill-color": "#b0d4fc" ← Ändere zu z.B. "#0066cc"
}
}
]
```
**2. Layer-Sichtbarkeit nach Zoom-Level** (mittelschwer)
```json
{
"id": "poi-icon",
"minzoom": 14, ← POI-Icons nur ab Zoom 14 sichtbar
"maxzoom": 20 ← Max Zoom-Level
}
```
**3. Label-Schriftart** (mittelschwer)
```json
"text-font": ["Open Sans Bold"] ← Verfügbare Fonts in data/fonts/
```
**Verfügbare Fonts** (aus `/data/fonts/`):
- Metropolis (Black, Bold, Light, Regular, Thin + Italic-Varianten)
- Open Sans (Bold, Regular, Semibold + Italic-Varianten)
- PT Sans (Bold, Caption, Regular, Narrow + Varianten)
- Roboto (Black, Bold, Light, Medium, Regular, Thin + Italic-Varianten)
- Noto Sans (Bold, Italic, Regular)
**4. Linien-Dicke nach Zoom-Level** (fortgeschritten)
```json
"line-width": [
"interpolate", ← Interpolations-Funktion
["linear"], ← Linear interpolieren
["zoom"], ← Input: aktueller Zoom-Level
6, 0.5, ← Bei Zoom 6: 0.5px
20, 6 ← Bei Zoom 20: 6px
]
```
### Style testen (lokal)
```bash
git clone <repo>
# Nach Änderungen an osm-intl.json:
docker compose restart tileserver
# PNG-Tile abrufen
curl -s "http://localhost:9983/osm-intl/10/544/335.png" -o /tmp/test.png
file /tmp/test.png
# In MediaWiki-Kartographer URL testen:
# https://tiles.rd13server.de/osm-intl/{z}/{x}/{y}.png
```
---
## Martin Vector-Source verstehen
Martin liest Vektor-Daten aus `osm.mbtiles` und serviert sie als PBF (Protocol Buffers).
**Available Layers in osm.mbtiles** (abhängig von OpenFreeMap-Schema):
Beispiel: `http://localhost:9982/catalog` zeigt alle Source-Layer.
Typisch enthalten:
- `water` Seen, Flüsse
- `landuse` Wald, Park, Gras, Acker
- `building` Gebäude-Polygone
- `road` Straßen (Motorway, Trunk, Primary, Secondary, etc.)
- `railway` Eisenbahn-Linien
- `place` Orte (City, Town, Village)
- `poi` Points of Interest
**Layer in osm-intl.json updaten:**
```json
"source": "osm", ← Vector-Tile-Source (Martin)
"source-layer": "water", ← Welcher OSM-Layer? (prüfe /catalog)
```
Falls Layer fehlt → `http://localhost:9982/catalog` prüfen, oder mit `layer` in osm-intl.json experimential einen neuen hinzufügen.
---
## Rendering-Performance tunen
**tileserver-gl Cache:**
- **Disk-Cache:** `/tmp/pmtiles-cache` (wächst mit Nutzung; max ~50 GB bei voller Coverage)
- **Memory:** ~500 MB standard, skaliert mit Anfrage-Load
- **Warm-up:** Erste Anfrage zu neuem Tile-Bereich ist langsam (~100200ms), danach <20ms
**Bottleneck identifizieren:**
```bash
# Logs verfolgen (live)
docker compose logs -f tileserver | grep "render"
# Performance der Martin-API
curl -w "Time: %{time_total}s\n" -s http://localhost:9982/osm/0/0/0 | head
```
**Falls zu langsam:**
1. Martin-Worker-Prozesse erhöhen: `config/martin.yaml``worker_processes`
2. tileserver-gl Memory erhöhen: docker-compose.yml → `deploy.resources.limits.memory`
---
## Tests & Validierung
### Tile-Integrität checken
```bash
# Vektor-Tile (PBF) abrufen und inspizieren
curl -s "http://localhost:9982/osm/0/0/0.pbf" | file -
# Should: PBF (Tile protocol buffer)
# Raster-Tile (PNG) abrufen und prüfen
curl -s "http://localhost:8080/osm-intl/0/0/0.png" -o /tmp/t.png
file /tmp/t.png
# Should: image/png
# MediaWiki-Test URL
# https://tiles.rd13server.de/osm-intl/0/0/0.png
```
### Smoke-Test nach Update
```bash
# Nach Tile-Daten-Update (osm.mbtiles):
for zoom in 0 5 10 14; do
curl -s "http://localhost:9983/osm-intl/$zoom/0/0.png" \
-o /tmp/test_z$zoom.png && echo "✓ Zoom $zoom" || echo "✗ Zoom $zoom"
done
```
---
## Deployment & Release
### Checklist vor Push
- [ ] `docker compose logs` prüfen keine Errors
- [ ] Test-Tiles (Zoom 0, 5, 10, 14) erfolgreich
- [ ] REQUIREMENTS.md aktualisiert (falls Anforderung geändert)
- [ ] ADR erstellt (falls architektur-relevant)
- [ ] ADMIN.md / MAINTAINER.md aktualisiert
- [ ] CHANGELOG.md aktualisiert (falls vorhanden)
- [ ] Git commit + push
### Version & Releases
Derzeit: `rolling release` (jeder Commit ist live).
Falls später strikte Versionierung nötig:
- Versioning: Semantic Versioning (MAJOR.MINOR.PATCH)
- Releases: Tag in Git + Changelog
- Example: `v1.0.0` (Martin 0.9.0 + tileserver-gl 4.7.0)
---
## Bekannte Limitations & Future TODOs
**Jetzt (ADR-001):**
- ✅ Martin + tileserver-gl für statische 24×/Year Updates
- ✅ Styles anpassbar (osm-intl.json)
- ✅ Fonts + Sprites nutzbar
**Zukünftig (Falls nötig):**
- [ ] **ADR-002:** Live OSM-Update-Pipeline (OpenMapTiles Server)
- [ ] Sprites/Icons erweitern (derzeit leer)
- [ ] Mehrere Styles pro Kartographie-Kontext (dark mode, print-optimized)
- [ ] Horizontal scaling (Kubernetes / Load Balancer)
---
## Ressourcen
- [Martin Tile Server Documentation](https://maplibre.org/martin/)
- [tileserver-gl Documentation](https://tileserver.readthedocs.io/)
- [MapLibre GL Style Specification v8](https://maplibre.org/maplibre-gl-js-docs/style-spec/)
- [OpenMapTiles Schema](https://openmaptiles.org/schema/)
- [OSM Wiki: Map Tiles](https://wiki.openstreetmap.org/wiki/Tiles)
cd <repo>
# Dependencies installieren
# Dev-Server starten

43
docs/adr/000-template.md Normal file
View file

@ -0,0 +1,43 @@
# ADR Template
Record architectural decisions for the tile server project using this template.
## Status
**Status:** [Proposed | Accepted | Deprecated | Superseded by ADR-XXX]
## Context
Describe the issue or problem that motivated this decision. Include relevant background,
constraints, and the business/technical drivers.
## Decision
State the architectural decision clearly. What are we doing and why?
## Consequences
### Benefits
- Positive outcome 1
- Positive outcome 2
### Drawbacks
- Negative consequence 1
- Negative consequence 2
## Alternatives Considered
### Option A: [Name]
- Pros: ...
- Cons: ...
- Why not chosen: ...
### Option B: [Name]
- Pros: ...
- Cons: ...
- Why not chosen: ...
## References
- Link to related docs, RFCs, or issues
- External references if applicable

View file

@ -0,0 +1,136 @@
# ADR-001: Tile Rendering Stack Martin + tileserver-gl
**Date:** 2026-06-12
**Status:** Accepted
## Context
The tile server must serve map tiles to three external systems:
- **MediaWiki** (Kartographer extension) requires raster PNG tiles for client-side rendering
- **Home Assistant** requires raster PNG tiles for map visualization
- **Nextcloud** may require vector or raster tiles in future
Current state:
- Martin tile server distributes vector tiles (PBF format) from `osm.mbtiles`
- `osm.mbtiles` contains static OSM snapshot (not live-updated data)
- No client-side rendering capability for Kartographer (requires PNG or pre-rendered output)
- Map rendering fails silently in MediaWiki without server-side PNG delivery
Key requirements:
- Maps must display in MediaWiki without external tile server dependency
- Styles and layer definitions must be customizable by team
- Map tile updates are planned 24 times per year (not live)
- Low operational overhead (single-server, docker-compose deployment)
- Horizontal scaling is not a near-term requirement
## Decision
Adopt a **two-tier tile architecture:**
1. **Martin** (stays)
- Role: Vector tile distribution API
- Input: `osm.mbtiles` (static snapshot, updated manually 24×/year)
- Output: PBF tiles via REST API (`/{layer}/{z}/{x}/{y}.pbf`)
- Purpose: Foundation for future MapLibre GL integration, internal vector APIs
2. **tileserver-gl** (new)
- Role: PNG raster tile renderer + server
- Input: Vector tiles from Martin + Style definitions (stored in `/data/styles/`)
- Output: Raster PNG tiles via REST API (`/{style}/{z}/{x}/{y}.png`)
- Purpose: Serve Kartographer and other raster-only clients
**URL routing in Reverse Proxy (NPM):**
- `/osm/{z}/{x}/{y}.pbf` → Martin (internal/API use)
- `/osm-intl/{z}/{x}/{y}.png` → tileserver-gl (Kartographer)
- `/osm-intl/{z}/{x}/{y}@2x.png` → tileserver-gl (retina displays)
**MediaWiki Config:**
- `$wgKartographerMapServer = 'https://tiles.rd13server.de'`
- `$wgKartographerDfltStyle = 'osm-intl'` (raster style)
- No external tile dependency
## Consequences
### Benefits
- ✅ **Minimal operational complexity** Both Martin and tileserver-gl fit in docker-compose, combined ~200MB RAM
- ✅ **No external dependencies** Kartographer renders entirely from local server; no upstream tile requests
- ✅ **Style customization** tileserver-gl uses standard MapLibre GL styles (JSON); team can adjust colors, fonts, layer visibility without code changes
- ✅ **Flexible upgrade path** If live OSM updates become required (2027+), OpenMapTiles Server can be added without disrupting Martin or tileserver-gl
- ✅ **Fast iteration** Update cycle: download new `osm.mbtiles` → docker-compose restart → ~30 seconds downtime
- ✅ **Vector + Raster simultaneously** Future clients (MapLibre GL, Leaflet vector) can coexist with raster-only systems (Kartographer)
### Drawbacks
- ⚠️ **Manual tile refresh cycle** No live OSM updates; requires download and swap of `osm.mbtiles`
- ⚠️ **PNG caching complexity** Raster tiles are pre-rendered, so cached images may lag behind style changes by one tile-generation cycle
- ⚠️ **CPU spike during rendering** tileserver-gl pre-warms cache on startup; brief CPU spike when new zoom level/area is requested for first time
- ⚠️ **Storage** Raster caching will eventually consume disk space (~1GB per zoom level for Europe at full coverage); mitigation: define tile cache TTL
## Alternatives Considered
### Alternative A: OpenMapTiles Server (full-stack replacement)
**Pros:**
- Live OSM data import pipeline
- Central rendering engine with parametrized styling
- Full control over data freshness
**Cons:**
- **Overengineered for current needs** Requires PostGIS, Lua, full data pipeline; 35 GB footprint
- **High setup cost** 35 days to operationalize + ongoing maintenance burden
- **Not needed yet** static 24×/year updates don't justify infrastructure investment
- **Risk** Complex system = more failure modes; Martin is proven production-grade today
**Why not chosen:** Defers to future decision (ADR-002?) when live update requirement becomes real.
### Alternative B: External tile server proxy (OSM.org, Mapbox)
**Pros:**
- Zero infrastructure investment
- Instant map display
**Cons:**
- **Violates ToS** OSM.org tile server prohibits automated/bulk access (Kartographer = repeated requests)
- **No resilience** Dependent on external uptime; tiles fail if upstream is down
- **Violates NFR-001** REQ-001 requires "maps must be independently hostable"
**Why not chosen:** Does not meet architectural requirements.
### Alternative C: Martin + client-side rendering (MapLibre GL only)
**Pros:**
- Simpler stack (one service)
- Full styling flexibility at runtime
**Cons:**
- **Breaks MediaWiki** Kartographer does not support MapLibre GL client-side rendering
- **Blocks requirement** MediaWiki maps would remain broken; unacceptable to stakeholders
**Why not chosen:** Fails functional requirements.
## Implementation Plan
1. **Week 1**
- Add tileserver-gl to docker-compose.yml
- Mount `/data/styles/` and `/data/fonts/` for style/font config
- Create baseline osm-intl style (JSON) for raster rendering
- Configure Nginx reverse proxy routing in NPM
2. **Week 2**
- Test end-to-end: MediaWiki mapframe → PNG delivery
- Verify cache headers and CDN-friendliness
- Document in ADMIN.md: tile update workflow
3. **Week 3**
- Performance testing (p95 response times, cache hit rates)
- Rollback procedure documentation
- Team training on style customization
## Future Decisions
- **ADR-002:** Live OSM update pipeline (if 2027 requirement arises)
- **ADR-003:** Horizontal tile server scaling (if traffic exceeds single-server capacity)
- **ADR-004:** Sprites and fonts CDN distribution
## References
- [Martin Tile Server Docs](https://maplibre.org/martin/)
- [tileserver-gl Docs](https://tileserver.readthedocs.io/)
- [REQ-001: Tile Server öffentlich erreichbar](../requirements/REQUIREMENTS.md#req-001)
- [REQ-004: MediaWiki Integration](../requirements/REQUIREMENTS.md#req-004)

View file

@ -0,0 +1,102 @@
# ADR-002: Kein Selbst-Build für z16-Extrakte und Satelliten-Tiles
**Date:** 2026-06-13
**Status:** Accepted
## Context
Es wurden zwei Erweiterungen evaluiert:
1. **OSM Vektor-Tiles für Europa/Deutschland bis Zoom 16**
Zoom 16 liefert Details wie Hausnummern, Fußwege, einzelne Gebäudeumrisse
deutlich mehr Detail als das vorhandene Planet-MBTiles (z014).
2. **Satelliten-Raster-Tiles für den gesamten Planeten**
Satellitenkarte als zweiter Kartenstil in Kartographer und Home Assistant.
## Decision
**Beide Optionen werden auf der aktuellen Hardware nicht gebaut (Self-Build ausgeschlossen).**
### Begründung
#### z16 Vektor-Europa/Deutschland
| Quelle | Verfügbar? | Anmerkung |
|---|---|---|
| OpenFreeMap Download | ❌ | Nur Planet z014 |
| Protomaps PMTiles | ❌ | Täglich Planet, aber max. z1415 |
| Geofabrik | ❌ | Nur OSM-Rohdaten (PBF), keine Tiles |
| Planetiler Self-Build | ❌ ausgeschlossen | RAM-Anforderung: ≥16 GB (Europa), ≥64 GB (Planet); Buildzeit: 812h Europa |
**Ergebnis:** Kein freier Pre-Build verfügbar. Selbst-Build auf aktueller Hardware nicht möglich.
#### Satelliten-Planet
| Quelle | Verfügbar? | Anmerkung |
|---|---|---|
| Pre-built Planet MBTiles | ❌ | Kein Anbieter bietet komplettes Planet-Satellit-MBTiles |
| EOX Sentinel-2 (tile-by-tile) | ✅ z011 | Download via `download-satellite.py`, ~38 GB, ~24h |
| EOX Sentinel-2 z12 | ⚠️ möglich | ~140 GB, 35 Tage Download, grenzwertig |
| EOX Sentinel-2 z13+ | ❌ | Nicht praktikabel (TB-Bereich) |
**Ergebnis:** Kein Planet-Download verfügbar. Tile-by-tile z011 ist die einzige praktikable Option (~38 GB).
### Hardware-Einschränkung (dokumentiert)
Die aktuelle Hardware (`rd13docker`) erfüllt die Mindestanforderungen für
Planetiler-Builds nicht:
| Anforderung | Planetiler Europa | Aktuell verfügbar |
|---|---|---|
| RAM | ≥ 16 GB | nicht bekannt / begrenzt |
| SSD-Temp | ≥ 500 GB | begrenzt |
| Buildzeit | 812h | akzeptabel, aber RAM ist Blocker |
**Diese Einschränkung ist dauerhaft in diesem ADR festgehalten.**
Kein Agent oder Maintainer soll einen Planetiler-Build auf dieser Hardware starten.
## Consequences
### Was bleibt
- **Vektor-Planet z014** bleibt die Datenbasis (OpenFreeMap, 95 GB, vorhanden)
- **Satelliten-Download z011** kann jederzeit gestartet werden (Script vorhanden)
### Akzeptierte Einschränkungen
- Zoom 1516 (Hausnummern, Einfahrten) nicht verfügbar → akzeptiert
- Satellitkarte maximal z11 (Stadtebene ohne Straßendetail) → akzeptiert
- Kein Live-OSM-Update-Zyklus (separat: ADR-001)
## Zukunft / Upgrade-Pfad
Falls Hardware aufgerüstet wird (≥ 16 GB RAM, ≥ 500 GB SSD-Temp):
```bash
# Deutschland z0-16 (~12 GB Output, ~2h, 8 GB RAM)
docker run --rm -v ./data:/data \
-e JAVA_TOOL_OPTIONS="-Xmx8g" \
ghcr.io/onthegomap/planetiler:latest \
--download --area=germany --output=/data/osm-de.mbtiles
# Europa z0-16 (~80 GB Output, ~12h, 16 GB RAM)
docker run --rm -v ./data:/data \
-e JAVA_TOOL_OPTIONS="-Xmx16g" \
ghcr.io/onthegomap/planetiler:latest \
--download --area=europe --output=/data/osm-eu.mbtiles
```
Satellit z011 (jetzt möglich, Script vorhanden):
```bash
SAT_MAX_ZOOM=11 python3 scripts/download-satellite.py data/satellite.mbtiles
# Schätzung: ~38 GB, ~24h bei 8 Threads
```
## References
- [ADR-001: Martin + tileserver-gl Stack](001-martin-tileserver-gl-stack.md)
- [OpenFreeMap Downloads](https://openfreemap.org) nur Planet z014
- [Protomaps Builds](https://maps.protomaps.com/builds/) nur Planet z014/15
- [EOX Sentinel-2 cloudless](https://s2maps.eu) tile-by-tile via Script
- [Planetiler Hardware Requirements](https://github.com/onthegomap/planetiler)

View file

@ -132,3 +132,24 @@ Relevante Outputs:
5 files changed, 114 insertions(+), 44 deletions(-)
M docs/mediawiki-config.php
```
### Prompt 7
«media wiki sollte aktuell sein, aber commite alle änderungen in diesem repo um die änderung abzuschließen.»
### Antwort 7
Der Arbeitsbaum wurde auf die vereinheitlichten Hostnamen umgestellt, die Projektkontext- und Requirements-Dateien wurden aktualisiert und danach zur Vorbereitung des Commits geprüft.
Ausgefuehrte Kommandos:
```bash
git status --short
ls /mnt/services-data/rd13_tile_server/history/prompts
git diff -- docs/requirements/REQUIREMENTS.md history/summary/PROJECT_CONTEXT.md
```
Relevante Outputs:
```text
M docs/requirements/REQUIREMENTS.md
M history/summary/PROJECT_CONTEXT.md
2026-06-03_tile-server-setup_session.md
2026-06-10_tile-mediawiki-integration_session.md
```

View file

@ -0,0 +1,71 @@
# Session 2026-06-12 Tile-Server Raster-Architektur
## Prompt 1
Ich habe die eigentliche Ursache gefunden: dein Tile-Server liefert nur Vektor-Tiles (PBF) und keine gerenderten PNG-Styles für Kartographer. Ich teste jetzt einen sofort funktionierenden Raster-Fallback-Pfad, damit Karten in der UI wieder erscheinen. Was müssen wir hier tun damit das geht?
## Antwort 1
Diagnostiziert, dass Martin ohne PNG-Rendering nicht für Kartographer ausreicht. Analyzed:
- Martin serviert nur PBF-Vektor-Tiles
- Kartographer erwartet PNG (raster) oder MapLibre GL Styles (mit client-side Rendering)
- osm.mbtiles ist vorhanden, aber keine Style-Definitionen in /data/styles/
Empfohlen: Schneller Fallback mit Nginx-Proxy zum OSM.org Upstream ODER lokales Raster-Rendering mit tileserver-gl.
Dokumentation gelesen: docker-compose.yml, martin.yaml, mediawiki-config.php, api-endpoints.md, REQUIREMENTS.md
## Prompt 2
- Dauerhaft sauber: eigenen Raster-Renderer ergänzen (z. B. tileserver-gl oder openmaptiles-Server) und dieselbe Kartographer-URL-Struktur lokal bedienen. las uns das planen
## Antwort 2
High-Level Plan erarbeitet:
- Martin bleibt für Vektor-API und interne Nutzung
- Raster-Renderer (tileserver-gl) neu hinzufügen
- Reverse Proxy: /osm-intl/{z}/{x}/{y}.png → tileserver-gl
- MediaWiki unabhängig von externen Tile-Servern
Geplant: 6 Arbeitspakete, Abnahmekriterien, Umsetzungsschätzung.
Alternatives Analyzed: OpenMapTiles Server (overengineered, für euch jetzt nicht nötig), tileserver-gl (optimal für static 24×/Year Updates).
## Prompt 3
was ist der unterschied zwischen open map tiles server und martin. Erkläre mir high level warum ich beide brauche was nur der eine und nur der andere kann und warum es besser ist beide zu haben was sind die jeweiligen aufgaben?
## Antwort 3
**Detaillierte Vergleichstabelle erstellt:**
| Aspekt | Martin | OpenMapTiles Server |
|---|---|---|
| Kategorie | Tile-Distributer | OSM-Daten-Pipeline |
| Eingabe | MBTiles/PMTiles (fertig) | Rohe OSM-Daten oder Shapefile |
| Was es macht | Vektor/Raster aus DB auslesen + HTTP servieren | OSM importieren → Vektor-Layer definieren → Rendering-Regeln → Tiles generieren |
| Stack-Größe | ~50 MB, single binary | PostGIS + Rendering-Engine + Tile-Generator → GB |
**Konklusiv:** Martin + tileserver-gl jetzt optimal. OpenMapTiles Server nur nötig wenn Live-OSM-Updates erforderlich (zukünftig, nicht jetzt).
## Prompt 4
ok lass und das im repo als adr festhalten aber wir entscheiden uns für martin und tileservergl für den anfang, wenn ich in dem stack trotzdem stiles und karten anpassen kann und das updaten der karten 2-4 Mal im jahr einfach möglich ist.
## Antwort 4
**Architektur-Entscheidung dokumentiert:**
1. **ADR-001 erstellt: [docs/adr/001-martin-tileserver-gl-stack.md](../docs/adr/001-martin-tileserver-gl-stack.md)**
- Status: Accepted
- Decision: Martin (Vektor) + tileserver-gl (Raster PNG)
- Rationale: niedrige Komplexität, Style-Flexibilität, 24x/Year Update-Zyklus passend
- Alternatives analyzed: OpenMapTiles Server (deferred), External proxy (violates ToS)
- Future ADRs: ADR-002 (Live OSM Pipeline), ADR-003 (Horizontal Scaling)
2. **ADR-Template erstellt: [docs/adr/000-template.md](../docs/adr/000-template.md)**
- Nygard-Format: Status, Context, Decision, Consequences, Alternatives
3. **REQUIREMENTS.md aktualisiert**
- Architektur-Übersicht hinzugefügt
- Link zu ADR-001
- Explizit dokumentiert: Styles anpassbar, 24x/Year Update-Workflow
**Files staged & ready for commit:**
```
A docs/adr/000-template.md
A docs/adr/001-martin-tileserver-gl-stack.md
M docs/requirements/REQUIREMENTS.md
```

View file

@ -0,0 +1,119 @@
# Session 2026-06-13 Tile-Server vollständig in Betrieb
### Prompt 1
Schau jetzt nochmal auf alles drauf und versuche die aktuellen Probleme zu lösen! Ich habe jetzt einen neuen Proxy Caddy unter rd13_system_proxy als repo wenn du es nötig hast kannst du diesen auch in einem git workflow nach belieben anpassen damit das mit dem tile server funktioniert. Je mehr allerdings in dem Tile repo und dem dafür vorgesehenen docker stack gelöst werden kann desto besser.
### Antwort 1
Stack vollständig verifiziert: Martin (healthy), tileserver-gl (healthy), Caddy aktiv.
Smoke-Test bestanden: Zoom 0/5/10/14 HTTP 200 PNG, PBF 200, /health OK.
PROJECT_CONTEXT aktualisiert, committed: `2765bb9`
### Prompt 2
Geh nochmal ins MediaWiki und überprüfe die config und auch ob die config geladen ist.
### Antwort 2
Problem gefunden: `wikidata/LocalSettings.php` (der echte Mount) zeigte noch auf `tile.openstreetmap.org`.
Fix: auf `https://tiles.rd13server.de/styles` + `osm-intl` umgestellt.
Zweites Problem: Font-Fehler bei Zoom ≥10 `glyphs`-URL war relativ, tileserver-gl konnte Fonts nicht laden.
Fix: `glyphs` auf absolute URL `http://192.168.178.6:9983/fonts/{fontstack}/{range}.pbf`.
Commits: `9081ed9` (tile-server), `627a1b1` (mediawiki)
### Prompt 3
ok sieht gut aus, allerdings sehe ich nur orte und gebäude. Ich hätte gerne eine vollständige openstreetmap karte mit allen wanderwegen und bekannten punkten.
### Antwort 3
Alten minimalen Style (15 Layer) durch OSM Bright GL (128 Layer, offizieller OpenMapTiles-Referenz-Style) ersetzt.
Sprites heruntergeladen (4 Dateien: osm-bright.json/png + @2x), Sprite-Pfad-Problem gelöst.
Zoom 12 München: 2.9 KB → 32 KB (11× mehr Inhalt). Commit: `4268b41`
### Prompt 4
Ist hier jetzt die ganze Welt verfügbar offline schon auf meinem Server oder werden Daten noch nachgeladen?
### Antwort 4
Verifiziert: osm.mbtiles = OpenFreeMap, 95 GB, Bounds -180/-85 bis +180/+85 (kompletter Planet), z014.
Keine externen URLs im Style. Vollständig offline.
### Prompt 5
Wenn das einfach geht hätte ich gerne noch Deutschland oder Europa bis Zoom Level 16. Ebenso will ich Satellitenkarten für den ganzen Planeten, was benötigen wir dafür und wie viel SSD Platz bräuchten wir dafür?
### Antwort 5
Analyse: z16 Europa ~6090 GB, Satellit z011 ~38 GB, z12 ~140 GB.
Hardware-Anforderungen dokumentiert.
### Prompt 6
Für das Zoom Level 16 Europa muss ich diese unbedingt selbst bauen oder gibt's die wieder fertig zum download? Selbst bauen will ich ausschließen und im Repo festhalten dass das auf der aktuellen HW nicht möglich ist. Das gilt auch für die Satellitendaten.
### Antwort 6
Recherche: Kein Pre-Built für Europa/DE z16 verfügbar (OpenFreeMap, Protomaps, Geofabrik alle nur bis z14).
Kein Planet-Satellit-MBTiles als Download verfügbar.
ADR-002 erstellt: Hardware-Constraint dauerhaft dokumentiert, kein Agent startet Planetiler-Build.
Commit: `c47f478`
### Prompt 7
Ich könnte die z16 auf meinem Host-Rechner bauen und dann transferieren? Da habe ich genug Ressourcen zur Verfügung. Und die Satellitenkarten stückweise runterladen klingt gut!
### Antwort 7
`scripts/build-host.sh` erstellt: Docker-basierter Planetiler-Build (germany/austria/europe/planet),
mit RAM-Profilen, Validierung und rsync-Transfer auf Server.
Satellit-Download via nohup gestartet aber VS Code Sessions brachen ab.
### Prompt 8
Jedes Mal wenn du versuchst den Download zu starten brechen die VS Code Remote Sessions ab. Sollten wir das vielleicht etwas ordentlicher aufsetzen? Vielleicht ein Python Script das sicher aus einem Container ausgeführt wird?
### Antwort 8
Ursache: `nohup`/`&` in Fish-Shell + VS Code Remote = instabile Session.
Lösung: eigenständiger Docker-Container für Downloads.
`docker-compose.download.yml` erstellt, Script mit `flush=True` robuster gemacht.
Überreste bereinigt: satellite.mbtiles (4 KB stub), satellite.mbtiles-shm/wal, satellite.log.
### Prompt 11
Lass uns gleich planet bauen wenn das geht. Und achte wieder auf Robustheit und Ressourcenverbrauch. Du kannst bis zu 12 CPUs und 32 GB RAM verwenden auf meinem Host-Rechner.
### Antwort 11
Disk-Analyse: Server hat 555 GB frei, Planet z16 bräuchte ~900 GB auf dem Host → nicht empfohlen.
docker-compose.build.yml robuster gemacht: Volumes aufgeteilt (output/sources/tmp getrennt), `--nodemap-type=sortedtable` für weniger RAM, G1GC-JVM-Flags, Disk-Anforderungen in Kommentaren.
### Prompt 12
Ok dann lass uns das nur für Europa in z16 bauen. Wie viel wären es dann?
### Antwort 12
Europa z16: ~25 GB Sources + ~150 GB Temp + ~80 GB Output = ~280 GB auf Host. Server: +80 GB (555 GB frei → kein Problem). Buildzeit: ~812h mit 12 CPUs.
### Prompt 13
Ok ja so bauen wir es!
### Antwort 13
docker-compose.build.yml committed. Startbefehl für Host:
```bash
AREA=europe MAX_ZOOM=16 RAM=28g BUILD_CPUS=12 BUILD_MEM=30g \
docker compose -f docker-compose.build.yml up
```
### Antwort 10
OOM-Ursache identifiziert: Script submittete alle 5,5 Mio Futures gleichzeitig in eine Liste → Memory-Spike beim Aufbau.
Fix: Bounded Queue mit `threading.Semaphore(THREADS * 4)` max 64 Futures gleichzeitig im Speicher.
Threads auf 16 erhöht, RAM-Limit auf 2 GB. Alte korrupte MBTiles entfernt, neu gestartet.
Ergebnis:
```
CPU: 18% RAM: 44 MiB / 2 GiB Threads: 16
data/satellite.mbtiles wächst von 4KB → 5.4MB → ...
```
Container läuft stabil ohne OOM.
### Antwort 9
Service aus `docker-compose.yml` entfernt.
`docker-compose.download.yml` als eigenständiger Stack angelegt.
```
# Starten:
docker compose -f docker-compose.download.yml up
# Status:
docker compose -f docker-compose.download.yml logs -f
# Pause/Resume:
docker compose -f docker-compose.download.yml stop
docker compose -f docker-compose.download.yml up
```
ADMIN.md mit Satelliten-Download-Anleitung ergänzt.

View file

@ -0,0 +1,120 @@
# PROJECT_CONTEXT rd13_tile_server
**Letzte Aktualisierung:** 2026-06-13 Satellit-Download läuft (~372 MB, stabil). Europa z16 Build-Container vorbereitet für Host-Rechner.
---
## Projektziel
Selbst gehosteter Tile Server für MediaWiki (Kartographer), Nextcloud, Home Assistant.
**Stack:** Martin (Vektor-API) + tileserver-gl (Raster PNG) + Caddy (Reverse Proxy).
Tile-Updates manuell 24×/Jahr; Styles anpassbar.
---
## Stack (Architektur ADR-001)
- **Vektor-API:** `ghcr.io/maplibre/martin:latest` (Port 9982 → `/osm/{z}/{x}/{y}`)
- **Raster-Renderer:** `maptiler/tileserver-gl` (Port 9983 → `/styles/osm-intl/{z}/{x}/{y}.png`)
- **Style:** OSM Bright GL (128 Layer, Wanderwege, POIs, vollständige Weltkarte)
- **Proxy:** Caddy (`rd13_system_proxy`) SSL, Routing `/styles/` → 9983, rest → 9982
- **Konfiguration:** `config/tileserver.json`, `config/styles/osm-intl.json`
- **Daten:** `data/osm.mbtiles` (95 GB, Planet z014), `data/sprites/`, `data/fonts/`
- **Satellit:** `docker-compose.download.yml``data/satellite.mbtiles` (~38 GB, z011, noch nicht gestartet)
- **Git Remote:** `ssh://git@192.168.178.6:2222/cschulz/rd13_tile_server.git`
---
## Architektur-Decision: Martin + tileserver-gl
**Rationale:** [siehe ADR-001](../../docs/adr/001-martin-tileserver-gl-stack.md)
| Komponente | Rolle | Input | Output |
|---|---|---|---|
| Martin | Vektor-Tile-API | osm.mbtiles | `/osm/{z}/{x}/{y}.pbf` |
| tileserver-gl | Raster-Renderer | Martin PBF + Styles | `/osm-intl/{z}/{x}/{y}.png` |
| NPM | Proxy + Rate Limit | Requests | HTTPS mit Cache-Headers |
**Kartographer-URL bleibt gleich:**
- `$wgKartographerMapServer = 'https://tiles.rd13server.de'`
- Style `osm-intl` (Raster)
---
## Aktueller Zustand
### Laufende Services
- Martin-Container: **laeuft** und ist **healthy**
- WebUI: **aktiviert** ueber `--webui enable-for-all`
### Vorhandene Daten in `/data/`
| Datei/Ordner | Status |
|---|---|
| `osm.mbtiles` | ✅ wird von Martin erkannt (Source `osm`) |
| `fonts/` | ✅ installiert |
| `styles/` | ✅ vorhanden |
| `satellite.mbtiles` | ❌ noch nicht heruntergeladen |
### Git-Stand
- Branch: `main`
- Letzter Commit: `46ac204` tileserver-gl operational, config in config/
---
## Aktueller Zustand (2026-06-13) VOLLSTÄNDIG IN BETRIEB ✅
| Endpunkt | Status | Antwort |
|---|---|---|
| `https://tiles.rd13server.de/styles/osm-intl/{z}/{x}/{y}.png` | ✅ 200 | PNG 256×256 |
| `https://tiles.rd13server.de/osm/{z}/{x}/{y}` (PBF) | ✅ 200 | application/x-protobuf |
| `https://tiles.rd13server.de/health` | ✅ 200 | OK |
| `rd13_tileserver` Container | ✅ healthy | Port 9983 |
| `rd13_martin` Container | ✅ healthy | Port 9982 |
| Caddy Proxy (`rd13_system_proxy`) | ✅ aktiv | server: Caddy |
**MediaWiki Kartographer Config:**
- `$wgKartographerMapServer = 'https://tiles.rd13server.de/styles'`
- `$wgKartographerDfltStyle = 'osm-intl'`
- → Request-URL: `https://tiles.rd13server.de/styles/osm-intl/{z}/{x}/{y}.png`
---
## Nächste Schritte
## Offene Aufgaben
## Bekannte Fallstricke
- `data/tmp/` und `data/sources/` werden von Docker als `root` erstellt → `sudo rm -rf` nötig
- Planetiler braucht sehr viel Disk (87 GB sources + 264 GB tmp + ~70 GB output = ~420 GB)
**Nicht mehr nötig**, wir nutzen OpenFreeMap MBTiles direkt
- Fish Shell unterstützt kein heredoc `<< 'EOF'` → Python `open().write()` oder `tee` verwenden
- Download von OpenFreeMap: `wget --continue` für Resume-Support bei Abbruch
---
## Erledigte Aufgaben
- [x] Git-Repo initialisiert und auf Forgejo gepusht
- [x] Martin WebUI aktiviert (`--webui enable-for-all`)
- [x] Endpunkte verifiziert (`/health`, `/catalog`, Root 200)
- [x] MediaWiki-Kartographer Doku + LocalSettings konfiguriert
- [x] ADR-001: Martin + tileserver-gl Stack-Entscheidung dokumentiert
- [x] **tileserver-gl** Service in docker-compose.yml (Port 9983, `maptiler/tileserver-gl`)
- [x] `config/tileserver.json` Service-Konfiguration (MBTiles + Styles + Fonts)
- [x] `config/styles/osm-intl.json` MapLibre GL Style für Raster-Rendering
- [x] Caddy-Routing: `/styles/osm-intl/*` → Port 9983, rest → Port 9982
- [x] **End-to-End verifiziert:** PNG-Tiles 200 bei Zoom 0/5/10/14
---
## Offene Aufgaben
- [ ] **MediaWiki in Browser testen** mapframe/maplink auf einer Wiki-Seite
- [ ] **REQ-002 Rate Limiting** Caddy-seitig absichern (rd13_system_proxy)
- [ ] **REQ-002 Rate Limiting** NPM Advanced Config für `tiles.rd13server.de`
- [ ] NPM Proxy-Host für `tiles.rd13server.de` anlegen (manuell im NPM UI)
- [ ] fail2ban optional konfigurieren (REQ-002 Schicht 3)
- [ ] Satellit-Download (optional, REQ-005)
- [ ] Martin mit Satellit-Daten testen

View file

@ -1,6 +1,23 @@
# Anforderungen rd13_tile_server
Letzte Aktualisierung: 2026-06-10
Letzte Aktualisierung: 2026-06-13
---
## Architektur-Entscheidung
**Stack:** Martin (Vektor-API) + tileserver-gl (Raster-Renderer)
Rationale: [siehe ADR-001](../adr/001-martin-tileserver-gl-stack.md)
- Martin: Vektor-Tiles (PBF) API-Foundation für zukünftige MapLibre GL Integration
- tileserver-gl: PNG-Rendering Kartographer + externe Clients (Home Assistant, Nextcloud)
- Tile-Updates: manuell 24×/Jahr (nicht live)
- Styles: Anpassbar über JSON-Style-Definitionen in `config/styles/`
**⛔ Hardware-Einschränkung (dauerhaft):**
Kein Planetiler-Self-Build auf dieser Hardware. Kein Pre-Built für Europa/DE z16 verfügbar.
→ Begründung: [ADR-002](../adr/002-no-highzoom-no-satellite-build.md)
---
@ -12,7 +29,7 @@ Der Tile Server muss über HTTPS unter einer öffentlichen Domain erreichbar sei
damit externe Dienste (MediaWiki, Home Assistant, Nextcloud) Karten laden können.
**Akzeptanzkriterien:**
- Erreichbar unter `https://tile.rd13server.de`
- Erreichbar unter `https://tiles.rd13server.de`
- SSL-Terminierung via NPM
- Health-Endpunkt antwortet mit HTTP 200
@ -22,7 +39,7 @@ damit externe Dienste (MediaWiki, Home Assistant, Nextcloud) Karten laden könne
**Status:** 🔴 offen Implementierung erforderlich vor Live-Gang
Da der Server unter `https://tile.rd13server.de` öffentlich erreichbar ist, muss
Da der Server unter `https://tiles.rd13server.de` öffentlich erreichbar ist, muss
er gegen unautorisierten Massenkonsum und DoS-Angriffe geschützt werden.
### Anforderungen
@ -73,7 +90,7 @@ Da NPM keinen bedingten Block pro Location direkt unterstuetzt, wird ein
zweiter Proxy-Host fuer interne Endpunkte empfohlen:
```
Oeffentlicher Proxy-Host: tile.rd13server.de -> http://HOST:9982
Oeffentlicher Proxy-Host: tiles.rd13server.de -> http://HOST:9982
→ nur Tile-Pfade freigeben, alle anderen 403
→ Rate Limiting aktiv
@ -106,10 +123,10 @@ bantime = 3600
### Reihenfolge der Umsetzung
1. NPM Proxy-Host fuer `tile.rd13server.de` anlegen (falls noch nicht vorhanden)
1. NPM Proxy-Host fuer `tiles.rd13server.de` anlegen (falls noch nicht vorhanden)
2. Rate-Limiting-Block in NPM Advanced Config eintragen
3. NPM Access List „LAN" anlegen und auf `/` + `/catalog` anwenden
4. Test: `ab -n 200 -c 10 https://tile.rd13server.de/osm/10/0/0` → 429 nach Burst
4. Test: `ab -n 200 -c 10 https://tiles.rd13server.de/osm/10/0/0` → 429 nach Burst
5. fail2ban optional installieren und konfigurieren
---
@ -131,7 +148,7 @@ die Last auf den Server zu reduzieren.
**Status:** ✅ umgesetzt (LocalSettings konfiguriert)
Kartographer nutzt `https://tile.rd13server.de` als Tile-Server.
Kartographer nutzt `https://tiles.rd13server.de` als Tile-Server.
---
@ -140,4 +157,13 @@ Kartographer nutzt `https://tile.rd13server.de` als Tile-Server.
**Status:** 📋 dokumentiert, nicht getestet
Satellit-Tiles als Raster-Layer in HA-Map-Card einbindbar.
Wartet auf `satellite.mbtiles` Download.
**Satellit-Download-Entscheidung (2026-06-13):**
- Kein Pre-Built Planet-Satellit-MBTiles verfügbar → [ADR-002](../adr/002-no-highzoom-no-satellite-build.md)
- Download z011 (~38 GB, ~24h) via `scripts/download-satellite.py` möglich wenn gewünscht
- z12+ auf dieser Hardware nicht praktikabel (140 GB+, 35 Tage)
```bash
# Satellit z0-11 starten (optional, bei Bedarf):
SAT_MAX_ZOOM=11 python3 scripts/download-satellite.py data/satellite.mbtiles
```

View file

@ -1,100 +0,0 @@
# PROJECT_CONTEXT rd13_tile_server
**Letzte Aktualisierung:** 2026-06-10 Martin laeuft healthy, WebUI aktiv, MediaWiki integriert, Rate-Limiting-Anforderung aufgenommen
---
## Projektziel
Selbst gehosteter Tile Server auf Basis von **Martin** (Rust), der für MediaWiki,
Nextcloud, Home Assistant und andere Dienste Vektorkarten bereitstellt.
---
## Stack
- **Tile Server:** `ghcr.io/maplibre/martin:latest`
- **Port:** 9982 extern → 3000 intern
- **Proxy:** NPM (Nginx Proxy Manager) extern kein nginx-Sidecar
- **Daten:** MBTiles/PMTiles in `/data/`
- **Git Remote:** `ssh://git@192.168.178.6:2222/cschulz/rd13_tile_server.git`
---
## Aktueller Zustand
### Laufende Services
- Martin-Container: **laeuft** und ist **healthy**
- WebUI: **aktiviert** ueber `--webui enable-for-all`
### Vorhandene Daten in `/data/`
| Datei/Ordner | Status |
|---|---|
| `osm.mbtiles` | ✅ wird von Martin erkannt (Source `osm`) |
| `fonts/` | ✅ installiert |
| `styles/` | ✅ vorhanden |
| `satellite.mbtiles` | ❌ noch nicht heruntergeladen |
### Git-Stand
- Branch: `main`
- Letzter Commit: `0a5f6cd` - WebUI aktiviert und MediaWiki-Integration vorbereitet
- Noch offen (lokal): `docs/mediawiki-config.php`
---
## Nächste Schritte (fuer naechsten Agenten)
1. **REQ-002 Rate Limiting im NPM UI eintragen** (manuell):
- Proxy-Host: `https://tile.rd13server.de``http://HOST:9982`
- Advanced Config: Rate-Limiting-Block aus `docs/requirements/REQUIREMENTS.md` einfügen
- NPM Access List „LAN" für `/` und `/catalog` anlegen
2. **Satellit-Download starten** (optional, ~10 GB, Zoom 010):
```bash
tmux new-session -d -s rd13-sat -c /mnt/services-data/rd13_tile_server \
'python3 scripts/download-satellite.py data/satellite.mbtiles 2>&1 | tee logs/satellite.log'
```
3. **NPM konfigurieren** (manuell im NPM UI):
- Proxy Host: `http://HOST:9982`
- Advanced Config: `add_header Cache-Control "public, max-age=86400, stale-while-revalidate=3600" always;`
---
## Bekannte Fallstricke
- `data/tmp/` und `data/sources/` werden von Docker als `root` erstellt → `sudo rm -rf` nötig
- Planetiler braucht sehr viel Disk (87 GB sources + 264 GB tmp + ~70 GB output = ~420 GB)
**Nicht mehr nötig**, wir nutzen OpenFreeMap MBTiles direkt
- Fish Shell unterstützt kein heredoc `<< 'EOF'` → Python `open().write()` oder `tee` verwenden
- Download von OpenFreeMap: `wget --continue` für Resume-Support bei Abbruch
---
## Erledigte Aufgaben
- [x] Git-Repo initialisiert und auf Forgejo gepusht
- [x] Migration TileServer-GL → Martin
- [x] nginx-Sidecar entfernt (NPM extern)
- [x] Port auf 9982 geändert
- [x] copilot-instructions auf aktuelles Template aktualisiert
- [x] download-data.sh + download-satellite.py erstellt
- [x] History-System (post-commit Hook + _session.md Konvention) in rd13_copilot_setup implementiert
- [x] Docs für MediaWiki, Nextcloud, Home Assistant erstellt
- [x] Planet-MBTiles Download gestartet (OpenFreeMap, 94 GB)
- [x] Martin WebUI aktiviert (`--webui enable-for-all`)
- [x] Endpunkte verifiziert (`/health`, `/catalog`, Root 200)
- [x] MediaWiki-Kartographer Doku in `docs/mediawiki-config.php` erweitert
- [x] MediaWiki-Repo auf Martin umgestellt und committed
- [x] Rate-Limiting-Anforderungen in `docs/requirements/REQUIREMENTS.md` aufgenommen
---
## Offene Aufgaben
- [ ] **REQ-002 Rate Limiting** NPM Advanced Config für `tile.rd13server.de` eintragen (Details: `docs/requirements/REQUIREMENTS.md`)
- [ ] NPM Proxy-Host für `tile.rd13server.de` anlegen (manuell im NPM UI)
- [ ] fail2ban optional konfigurieren (REQ-002 Schicht 3)
- [ ] Satellit-Download (optional, REQ-005)
- [ ] Hook-Verteilung auf alle Repos (install-hooks.sh)
- [ ] Martin mit Satellit-Daten testen

121
scripts/build-host.sh Executable file
View file

@ -0,0 +1,121 @@
#!/usr/bin/env bash
# =============================================================================
# build-host.sh -- OSM Vektor-Tiles auf Host-Rechner bauen + auf Server deployen
#
# Dieses Script läuft auf dem HOST-RECHNER (nicht auf dem Server).
# Voraussetzung: Docker Desktop (macOS/Windows) oder Docker (Linux)
#
# Verwendung:
# ./scripts/build-host.sh [germany|europe|planet] [SERVER_USER@SERVER_HOST]
#
# Beispiele:
# ./scripts/build-host.sh germany rd13server@192.168.178.6
# ./scripts/build-host.sh europe rd13server@192.168.178.6
#
# Das fertige MBTiles wird per rsync auf den Server übertragen und Martin
# automatisch neu gestartet.
#
# Hardware-Anforderungen:
# germany: ≥ 8 GB RAM, ≥ 30 GB freier Disk, ~2h
# europe: ≥ 16 GB RAM, ≥ 200 GB freier Disk, ~8-12h
# planet: ≥ 32 GB RAM, ≥ 500 GB freier Disk, ~24h
# =============================================================================
set -euo pipefail
AREA="${1:-germany}"
REMOTE="${2:-}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
BUILD_DIR="${SCRIPT_DIR}/../data/build"
OUTPUT_FILE="${BUILD_DIR}/${AREA}.mbtiles"
# ── RAM-Profil ───────────────────────────────────────────────────────────────
case "$AREA" in
germany) RAM="8g" ;;
austria) RAM="4g" ;;
europe) RAM="16g" ;;
planet) RAM="32g" ;;
*)
echo "Unbekannte Region: $AREA"
echo "Verfügbar: germany | austria | europe | planet"
exit 1
;;
esac
mkdir -p "$BUILD_DIR"
echo "================================================================"
echo " rd13 Tile-Build | Region: $AREA | RAM: $RAM | $(date)"
echo " Output: $OUTPUT_FILE"
echo "================================================================"
# ── 1. Planetiler bauen ──────────────────────────────────────────────────────
echo ""
echo "[1/3] Starte Planetiler via Docker..."
echo " (OSM-Daten werden automatisch von Geofabrik heruntergeladen)"
echo ""
docker run --rm \
-v "${BUILD_DIR}:/data" \
-e JAVA_TOOL_OPTIONS="-Xmx${RAM}" \
ghcr.io/onthegomap/planetiler:latest \
--download \
--area="${AREA}" \
--output="/data/${AREA}.mbtiles" \
--maxzoom=16 \
--force
echo ""
echo "[1/3] Build fertig: $(ls -lh "$OUTPUT_FILE" | awk '{print $5}')"
# ── 2. Validieren ────────────────────────────────────────────────────────────
echo "[2/3] Validiere MBTiles..."
python3 - <<PYEOF
import sqlite3, sys
conn = sqlite3.connect("${OUTPUT_FILE}")
meta = dict(conn.execute("SELECT name, value FROM metadata").fetchall())
maxzoom = meta.get("maxzoom", "?")
bounds = meta.get("bounds", "?")
count = conn.execute("SELECT COUNT(*) FROM tiles").fetchone()[0]
print(f" Name: {meta.get('name','?')}")
print(f" MaxZoom: {maxzoom}")
print(f" Bounds: {bounds}")
print(f" Tiles: {count:,}")
if int(maxzoom) < 14:
print("FEHLER: MaxZoom < 14 Build unvollständig?")
sys.exit(1)
conn.close()
print(" ✓ Validierung OK")
PYEOF
# ── 3. Transfer auf Server ───────────────────────────────────────────────────
if [[ -n "$REMOTE" ]]; then
REMOTE_PATH="/mnt/services-data/rd13_tile_server/data/osm-${AREA}.mbtiles"
echo ""
echo "[3/3] Übertrage nach ${REMOTE}:${REMOTE_PATH}..."
echo " (rsync bricht/fortsetzt bei Verbindungsunterbrechung)"
rsync -avh --progress \
--partial --partial-dir="${BUILD_DIR}/.rsync-partial" \
"${OUTPUT_FILE}" \
"${REMOTE}:${REMOTE_PATH}"
echo ""
echo "[3/3] Starte Martin auf Server neu..."
ssh "${REMOTE}" "cd /mnt/services-data/rd13_tile_server && docker compose restart martin"
echo ""
echo "✓ Fertig! Neue Source verfügbar unter:"
echo " https://tiles.rd13server.de/osm-${AREA}"
echo ""
echo " Füge in config/styles/osm-intl.json eine zweite Source hinzu:"
echo " \"osm-${AREA}\": { \"type\": \"vector\", \"url\": \"mbtiles://{osm-${AREA}}\" }"
else
echo ""
echo "[3/3] Kein Remote angegeben Transfer übersprungen."
echo " MBTiles liegt unter: ${OUTPUT_FILE}"
echo ""
echo " Manueller Transfer:"
echo " rsync -avh --progress ${OUTPUT_FILE} USER@SERVER:/mnt/services-data/rd13_tile_server/data/osm-${AREA}.mbtiles"
fi
echo ""
echo "================================================================"
echo " Build abgeschlossen: $(date)"
echo "================================================================"

View file

@ -74,30 +74,41 @@ def do_tile(conn, z, x, y):
stats["err"] += 1
def log(msg):
print(msg, flush=True)
def main():
total = sum(4 ** z for z in range(MAX_ZOOM + 1))
print("[Sat] Output: %s" % OUTPUT)
print("[Sat] Zoom: 0-%d | Threads: %d | Tiles: %d" % (MAX_ZOOM, THREADS, total))
print("[Sat] Quelle: Sentinel-2 cloudless 2021 (EOX, CC BY 4.0)")
log("[Sat] Output: %s" % OUTPUT)
log("[Sat] Zoom: 0-%d | Threads: %d | Tiles: %d" % (MAX_ZOOM, THREADS, total))
log("[Sat] Quelle: Sentinel-2 cloudless 2021 (EOX, CC BY 4.0)")
log("[Sat] Resume: bereits heruntergeladene Tiles werden übersprungen")
conn = init_db(OUTPUT)
t0 = time.time()
futs = [
do_tile
for z in range(MAX_ZOOM + 1)
for x in range(1 << z)
for y in range(1 << z)
]
n = 0
# Bounded queue: max THREADS*4 Futures gleichzeitig im Speicher
# verhindert OOM bei 5.5 Mio Tasks
sem = threading.Semaphore(THREADS * 4)
def submit_tile(ex, z, x, y):
sem.acquire()
fut = ex.submit(do_tile, conn, z, x, y)
fut.add_done_callback(lambda _: sem.release())
return fut
with ThreadPoolExecutor(max_workers=THREADS) as ex:
submitted = [ex.submit(do_tile, conn, z, x, y)
for z in range(MAX_ZOOM + 1)
for x in range(1 << z)
for y in range(1 << z)]
for n, f in enumerate(as_completed(submitted), 1):
futs = []
for z in range(MAX_ZOOM + 1):
for x in range(1 << z):
for y in range(1 << z):
futs.append(submit_tile(ex, z, x, y))
for n, f in enumerate(as_completed(futs), 1):
f.result()
if n % 1000 == 0 or n == total:
dt = time.time() - t0 or 0.001
eta = (total - n) / n * dt / 3600
print("[Sat] %5.1f%% %d/%d ok=%d skip=%d err=%d %.0ft/s ETA=%.1fh" % (
log("[Sat] %5.1f%% %d/%d ok=%d skip=%d err=%d %.0ft/s ETA=%.1fh" % (
n / total * 100, n, total,
stats["ok"], stats["skip"], stats["err"],
n / dt, eta))
@ -105,11 +116,11 @@ def main():
if pending:
conn.executemany("INSERT OR REPLACE INTO tiles VALUES(?,?,?,?)", pending)
conn.commit()
print("[Sat] Erstelle Index...")
log("[Sat] Erstelle Index...")
conn.execute("CREATE UNIQUE INDEX IF NOT EXISTS idx ON tiles(zoom_level, tile_column, tile_row)")
conn.execute("ANALYZE")
conn.close()
print("[Sat] Fertig %.1fh -- %s" % ((time.time() - t0) / 3600, OUTPUT))
log("[Sat] Fertig %.1fh -- %s" % ((time.time() - t0) / 3600, OUTPUT))
if __name__ == "__main__":