Files

4.6 KiB
Raw Permalink Blame History

GTFS-RAPTOR-Verbindungssuche: Praxis & Pitfalls (2026-08-19, urlaub.perfekt-ohne-en.de)

Echte Nahverkehrs-Verbindungs-Suche (RE/RB/S-Bahn, kein ICE) aus einer GTFS-Datei (Nutzer lieferte r.und.s.bahnen.zip). Löst die Aufgabe "komplette Haltestellenliste in einer PDF + konsistente Fahrzeiten für viele Zielverbindungen".

Warum RAPTOR statt Dijkstra

Einfacher Dijkstra auf einem statischen Graphen ist bei großen Knoten instabil: benachbarte Ziele ergaben absurde Differenzen (Mainz→Berlin 11h vs. Nachbar Wiesbaden 3h). RAPTOR (Round-Based Public Transit Optimized Route) erweitert die Reichweite Runde um Runde über echte Trips und behandelt "Zug nehmen → aussteigen → umsteigen" natürlich. Für Zeit-abhängiges Transit-Routing ist RAPTOR der Standard.

GTFS-Import — Spaltenreihenfolge ist NICHT selbsterklärend!

Vor dem Import IMMER head -1 <datei>.txt lesen, nie annehmen:

  • stops.txt: stop_name, parent_station, stop_id, stop_lat, stop_lon, … stop_id ist Spalte 3, parent_station Spalte 2. Ein Bahnhof hat oft ZWEI Zeilen (parent-'Hbf' + stop_id-Knoten); die stop_id ist fürs Joinen zu stop_times gedacht.
  • stop_times.txt: trip_id, arrival_time, departure_time, stop_id, stop_sequence, … stop_id Spalte 4.
  • trips.txt: route_id, service_id, trip_id — trip_id Spalte 3, nicht 0. Import-Bug hier: Spalte 0 (route_id) genommen → nur 964 statt 110.087 Trips.
  • stop_times.txt ist riesig (82MB); als SQLite laden mit Index auf (trip_id, seq).

Bahnhof = mehrere stop_ids, nur 1 aktiv in st

Große Knoten haben MEHRERE stop_ids (Saarbrücken Hbf = 6, Köln Hbf = 2), aber nur einige kommen in stop_times vor. SELECT stop_id WHERE stop_name='Köln Hbf' kann eine "tote" ID mit 0 Trips liefern. Lösung: Geo-Merge — Stops <350m Abstand (Haversine) in eine Gruppe fassen; Routing löst Start/Ziel über die GANZE Gruppe auf und nimmt MIN über alle Ziel-IDs. Sonst schlägt die Verbindung fehl oder wird absurd.

resolve("Berlin")-Substring-Falle

name.lower() in stop_name.lower() matcht ALLE Stops mit "berlin" im Namen — auch kleine Orte (z.B. ein Ort "Berlin" unweit Trier → unter 5h statt quer durchs Land!). Für mehrdeutige Städte IMMER eindeutigen Bahnhof angeben ("Berlin Hbf") und die Site-DB-nach-Werte darauf normieren. Umlaute: Name-basiertes Matching braucht Umlaut-Rücktranskription (ue/oe/ae/ss ↔ ü/ö/ä/ß).

RAPTOR: Dauer UND Pfad (für PDF-Haltestellenliste)

Nur Dauer reicht nicht — die PDF soll die komplette Haltestellenliste zeigen. RAPTOR muss pro Stop den parent speichern (mit welchem trip + Einstieg-Stop erreicht) und nach Ziel-Erreichen den Pfad rückwärts rekonstruieren: je Segment alle Stops von Einstieg bis Ausstieg → echte GTFS-Haltefolge (Trier→Konz→Saarburg→…→Saarbrücken). Für Lesbarkeit [von] + halteliste + [nach] als Rahmen.

PDF-Route: Pfad-Cache statt Live-RAPTOR (sonst 504)

Live-RAPTOR bei jedem PDF-Aufruf ist zu langsam (bis 200s → nginx 504 Gateway Timeout). Lösung: Pfad-Cache vorab bauen (pfade_cache.json: key von|nach → seg + dauer_h) und die PDF-Route liest nur JSON → PDF <1s. ⚠️ Cache auf die Kern-Ebenen (1+2) begrenzen: alle Paare wären ~7h Bauzeit, die Kernmenge ~35min.

Start ohne Bahnhof (Radevormwald) → Remap + Bus-Vorsatz

Ein Startort ohne Schienenanschluss hat KEINEN Stop im Feed → Routing fehlt für die wichtigste Ebene 1. Lösung: auf nächstgelegenen Bahnhof remappen (Radevormwald → Remscheid-Lennep Bf) und einen Bus-Vorsatz (~20min) als erstes "Segment" + zur Dauer addieren. Solche Fälle explizit als START_REMAP = {ort: (bahnhof, bus_min)} führen, sonst fehlt die Ebene-1-PDF komplett.

Unsichere/absurde Fernzeiten → "prüfen", nie falsch anzeigen

Auch RAPTOR liefert auf manchen komplexen Fernstrecken falsche Werte (z.B. >10h, wenn Routing fehlschlug; real Regional 6–9h). Ehrlich handeln: plausible Werte behalten, unsichere (>10h / 0.0h / leer) als 'prüfen' mit Kategorie unsicher (vor Fahrt prüfen) markieren, und das Template zeigt „vor Fahrt prüfen" statt einer Zahl. Das Template muss jede unsichere Kategorie in seiner Gruppen-Map führen, sonst verschwinden die Zeilen still.

Empirische Referenz-Anker (Plausibilitäts-Check, Regional only)

  • Trier→Saarbrücken ~55–60min (RE 1 direkt)
  • Köln→Aachen ~35–50min (RE 1/RE 9)
  • Köln→Frankfurt regional ~2,5–3h MIT Umstiegen (~1,5h wäre nur ICE = NICHT korrekt)
  • Radevormwald→Frankfurt ~5,5h (Bus + Remscheid-Lennep + Regional)
  • Verifizieren mit diesen Ankern, NIE dem Algorithmus blind vertrauen.