Files

61 lines
3.4 KiB
Markdown
Raw Permalink 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.
# GTFS Echt-Fahrplan-Routing (Nahverkehr, kein ICE)
Erfahrungen aus urlaub.perfekt-ohne-en.de (3-Ebenen-Zugsuche, Nutzer lieferte
`r.und.s.bahnen.zip` = Regional + S-Bahn, Deutschlandticket-tauglich).
Ziel war: echte Fahrzeiten statt Luftlinien-Schätzung, max ~1h Abweichung.
## Modellierung & GTFS-Gotchas
- **stops.txt kann Spalten in unerwarteter Reihenfolge/CSV haben.** Hier war der
Header: `stop_name,parent_station,stop_id,stop_lat,stop_lon,...` und `parent_station`
ist die **echte ID**, `stop_id` die Sekundär-ID. Immer per Header/Index lösen,
nie fest annehmen — die Spaltenreihenfolge weicht von der GTFS-Konvention ab.
- **Ein Bahnhof = viele stop_ids, nur EINE aktiv in stop_times.** Beispiel:
"Saarbrücken Hbf" hat 6 IDs, "Köln Hbf" 2, nur jeweils 1 davon taucht in `st`
(den Routen) auf. Löse Stops über Geo-Cluster (<350 m) zu einer Gruppe zusammen
(`parent_station` nutzen, wenn befüllt).
- **Orte ohne Bahnhof** (Radevormwald) haben keine Trips → über den nächstgelegenen
Bahnhof (Remscheid-Lennep) routen + Bus-Vorsatz (20 min) als eigenes Hinweis-Segment.
- **Namens-Matching ist fragil:** DB-Name "Freiburg im Breisgau" ≠ GTFS
"Freiburg Hauptbahnhof". Robust: kleinsten gemeinsamen Teilstring + von den Kandidaten
den mit den meisten Trips (größter Stop) wählen.
## Algorithmus: RAPTOR statt Dijkstra
Einfacher **zeit-basierter Dijkstra ist bei großen Knoten instabil** und liefert
inkonsistente Zeiten (Mainz→Berlin 11h vs. Nachbar Wiesbaden 3h). Standard-Lösung:
**RAPTOR** (Round-Based) — erweitert Reichweite Runde um Runde über echte Trips.
Kern-Ablauf RAPTOR:
1. `earliest[stop] = dep0` an allen Start-Stops (eigene Anfangszeit, KEIN Umstiegsbuffer am Einstieg).
2. Je Runde: von markierten Stops alle abfahrenden Trips nach `earliest` nehmen,
entlangfahren, `earliest[sid]` verbessern.
3. Ziel erreicht → abbrechen; sonst neue markierte Menge = neue Runde.
4. **Umstiegsbuffer** (4 min) nur bei Umstieg > dep0, nicht am Start.
### Performance
- Pro-Paar-Routing mit 17–33 Profil-Abfahrtszeiten ist langsam (Sekunden bis Minuten).
- **2x Beschleunigung:** ein einziger "Ausbreitungs"-Dijkstra/RAPTOR je **Start-Bahnhof**
erreicht alle Ziele zugleich (5000 Paare in Minuten statt Stunden).
- **Pfad-Cache:** einmalig berechnete `(von|nach) -> Segmente` als JSON cachen
(`pfade_cache.json`), damit die Web-Route/PDF nur noch liest (0,5 s statt 504-Timeout).
## Pfad-Rekonstruktion für Haltestellenliste
RAPTOR so ertüchtigen, dass er `parent[stop] = (trip_id, einstieg_stop)` merkt und
rückwärts den kompletten Weg (alle Zwischenhalte je Zugsegment) aufbaut. Benötigt,
um in der PDF die vollständige Haltestellenliste zu zeigen (Nutzerwunsch: nicht nur
Start/Ziel, sondern alle Halte aus der GTFS-Datei).
## Fallstricke & Lektionen
- **Kein Vertrauen in eine einzige Verifikation:** Zeiten immer gegen 2–3 bekannte
Referenzstrecken prüfen (Trier→Saarbrücken ~1 h, Köln→Aachen ~50 min, Köln→Frankfurt
Regional ~2,5–3 h). "Skurrile" Werte (11 h für Nachbarstädte) deuten auf einen
Routing-/Auflösungs-Fehler, nicht auf echte Fahrzeit.
- **Fehlende/fehlerhafte Werte nicht erfinden,** sondern ehrlich als
"vor Fahrt prüfen" markieren. Eine Webseite mit "prüfen"-Labels ist besser als eine
mit falschen Zahlen (Ehrlichkeits-Regel, siehe deutsche-bahn-timetable-data.md).
- **Batch-Schreibroutinen:** erst validieren/löschen, dann neu einfügen (idempotent);
DB vor jedem Lauf sichern (Backup-Regel).