Files
mesh-configs/skills/devops/gtfs-timetable-routing/SKILL.md
T

57 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: gtfs-timetable-routing
description: "Use when: echte Zugfahrzeiten (Nahverkehr) braucht."
version: 1.0.0
author: mehlzoerwer
platforms: [linux]
models:
default: mimo-v2-flash
related_skills: [web-hosting-deployment, pocketbase-deployment]
license: MIT
---
# 🚂 GTFS-Timetable-Routing — echte Zugverbindungen aus Fahrplandaten
## When to Use
Nutze diesen Skill, wenn **echte, verlässliche Nahverkehrs-Fahrzeiten** (von→nach per Regional-/S-Bahn, kein ICE) benötigt werden — z.B. für eine Zugverbindungs-Webseite, Produkt-Planung oder Fahrzeit-Angaben. **Kein** Luftlinien-Raten, **kein** Erfinden von Zeiten.
**Anlass (Nutzer-Korrektur):** Die Urlaub-Webseite (urlaub.perfekt-ohne-en.de) zeigte Fahrzeiten aus Luftlinien-Schätzung — komplett falsch (2. Ebene halbe Zeit, bis 3× daneben). Der Nutzer verlangte reale Zeiten (max ~1h daneben). Lösung: GTFS-Fahrplandatei lokal verarbeiten.
## Die harte Wahrheit: öffentliche Fahrplan-APIs sind zu
Bevor Zeit mit Scraping/APIs verbrannt wird — getestet (2026-08), alle scheitern programmatisch:
| Quelle | Ergebnis |
|--------|----------|
| DB-API / bahn.de | Bot-Block / OPS_BLOCKED / 403 |
| api.transport.rest | DNS/000 |
| transport.rest v6 | 503 |
| reiseauskunft.de | toter Pfad |
| VRR-EFA (efa.vrr.de V2) | 405 (nginx blockt GET+POST) |
| WebSearch-Tool | nicht konfiguriert (Firecrawl-Key fehlt) |
**Verlässlicher Weg = eine GTFS-Datei** (Regional+S-Bahn, kein ICE ist ideal: genau Deutschlandticket-Nahverkehr). Datei lokal in SQLite → Dijkstra-Router → echte Zeiten. Kosten 0 $, schnell (<1s/Verbindung nach Build).
## Arbeitsablauf
1. **GTFS-Datei beschaffen.** Nutzer liefert `.zip` (z.B. `r.und.s.bahnen.zip`, „ohne ICE/IC"). Enthält `stops.txt`, `trips.txt`, `stop_times.txt`, `calendar*.txt`, `routes.txt`. Aus /user-cache-Dokument-Pfad kopieren.
2. **Extrahieren + in SQLite laden** (`scripts/build_gtfs_db.py`). 3 Tabellen: `stops(stop_id,stop_name,lat,lon)`, `trips(trip_id,route_id,service_id)`, `st(stop_id,trip_id,seq,arr,dep)` + Indizes auf st(stop_id), st(trip_id), st(trip_id,seq).
3. **Router bauen/ausführen** (`scripts/gtfs_suche.py`, zeitbasierter Dijkstra): vorladen `deps_by_stop` und `stops_by_trip`, dann Dijkstra mit `dist[stop]=beste Ankunft` + Ziel-Abbruch.
4. **Verifizieren** gegen eine bekannte echte Strecke (z.B. Aachen Hbf→Köln Hbf ≈ 1h 12min, 1 Umstieg). Wenn plausibel → alle Ziele neu berechnen.
5. **Website/DB aktualisieren** mit den echten Zeiten (nicht erfinden).
## ⚠️ Kritische Pitfalls (alle beim echten Lauf getroffen)
1. **stops.txt-Spaltenreihenfolge** ist `stop_name, parent_station, stop_id, stop_lat, stop_lon, ...` — **stop_id ist Spalte 2, NICHT Spalte 0**; `parent_station` ist Spalte 1 (echte Gruppen-ID). Bei falschem Mapping kommen Stops mit leeren IDs / falschen Namen.
2. **trips.txt-Header** ist `route_id, service_id, trip_id` — trip_id ist Spalte 2. Import NICHT an Spalte 0 hängen.
3. **Bahnhof hat mehrere stop_ids** (z.B. Köln Hbf = 222681 UND 452576), aber nur **eine ist aktiv in stop_times**. `stops.txt`-ID ≠ notwendigerweise `stop_times`-ID. → Beim Auflösen: die ID wählen, die `COUNT(*) IN st > 0` hat.
4. **`st`-Tabelle kann mehr eindeutige trips enthalten als `trips.txt`** (110k vs 964 war ok — `st` ist Wahrheit). Zähler-Ausgaben prüfen, nicht dem trips.txt-Import trauen.
5. **Zeit-Parser**: HH:MM und HH:MM:SS tolerant parsen (`split(":")`, defaults).
6. **Dijkstra-Effizienz**: Bei vielen Trips (110k) explodiert naive Expansion → nutze `dist[stop]` + Ziel-Abbruch + `visited`. Erst nach Effizienz-Fix: Aachen→Köln in Sekunden statt Hänger.
## Bauen
Die Referenzdatei `references/gtfs-build-notes.md` enthält Schema + Dijkstra-Kern + SQL. Die Skripte `scripts/build_gtfs_db.py` und `scripts/gtfs_suche.py` sind direkt ausführbar (Fahrzeit von→nach, `<von_stop_id> <nach_stop_id> [HH:MM]`).
**Verwandt:** Bei PocketBase als Ziel echte Zeiten in DB schreiben → `pocketbase-deployment` (Schema + Filter-Quirks). Bei Web-Frontend-Einbindung → `web-hosting-deployment`.