4.0 KiB
name, description, version, author, platforms, models, related_skills, license
| name | description | version | author | platforms | models | related_skills | license | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| gtfs-timetable-routing | Use when: echte Zugfahrzeiten (Nahverkehr) braucht. | 1.0.0 | mehlzoerwer |
|
|
|
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
- GTFS-Datei beschaffen. Nutzer liefert
.zip(z.B.r.und.s.bahnen.zip, „ohne ICE/IC"). Enthältstops.txt,trips.txt,stop_times.txt,calendar*.txt,routes.txt. Aus /user-cache-Dokument-Pfad kopieren. - 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). - Router bauen/ausführen (
scripts/gtfs_suche.py, zeitbasierter Dijkstra): vorladendeps_by_stopundstops_by_trip, dann Dijkstra mitdist[stop]=beste Ankunft+ Ziel-Abbruch. - Verifizieren gegen eine bekannte echte Strecke (z.B. Aachen Hbf→Köln Hbf ≈ 1h 12min, 1 Umstieg). Wenn plausibel → alle Ziele neu berechnen.
- Website/DB aktualisieren mit den echten Zeiten (nicht erfinden).
⚠️ Kritische Pitfalls (alle beim echten Lauf getroffen)
- stops.txt-Spaltenreihenfolge ist
stop_name, parent_station, stop_id, stop_lat, stop_lon, ...— stop_id ist Spalte 2, NICHT Spalte 0;parent_stationist Spalte 1 (echte Gruppen-ID). Bei falschem Mapping kommen Stops mit leeren IDs / falschen Namen. - trips.txt-Header ist
route_id, service_id, trip_id— trip_id ist Spalte 2. Import NICHT an Spalte 0 hängen. - Bahnhof hat mehrere stop_ids (z.B. Köln Hbf = 222681 UND 452576), aber nur eine ist aktiv in stop_times.
stops.txt-ID ≠ notwendigerweisestop_times-ID. → Beim Auflösen: die ID wählen, dieCOUNT(*) IN st > 0hat. st-Tabelle kann mehr eindeutige trips enthalten alstrips.txt(110k vs 964 war ok —stist Wahrheit). Zähler-Ausgaben prüfen, nicht dem trips.txt-Import trauen.- Zeit-Parser: HH:MM und HH:MM:SS tolerant parsen (
split(":"), defaults). - 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.