Oder wie ich lernte, Umsteigen zu lieben.

Ich mag Umsteigeverbindungen. Sie erfordern das richtige Maß an sportlichem Elan und exaktem Timing. Kommt es doch (in Dresden) ab und an vor, dass man der Anschlussverbindung nur königlich hinterherwinken darf. Dann heißt es zehn Minuten auf die nächste Bahn warten.

Da höre ich schon eine Stimme aus dem Hintergrund rufen: “Was zehn Minuten? Wir waren früher froh, wenn der Bus überhaupt kam.”

Über die letzten Jahre hat sich dabei durch Zufall ein System entwickelt. Sogar bis zu dem Punkt, wo ich von meinen Mitreisenden beim Einsteigen erwartungsvoll angesehen werde, um den richtigen Einstiegspunkt zu verkünden. Dabei muss man eigentlich nur alle relativen Positionen der Umsteigepunkte in Dresden kennen. Und wie schwer kann das denn schon sein? Relativer Umsteigeweg heißt die Strecke zwischen Ausstiegspunkt aus der ankommenden Linie und Einstiegspunkt in die abfahrende Linie. Bei Haltestellen mit mehreren Steigen pro Kreuzung können dies schon mal einige Meter Laufweg sein. Gerade Touristen fällt es schwer, sich auf solchen Haltestellen zu orientieren.

Konzept

Das Konzept ist einfach. Fährt der Anschluss ‘hinter’ der ankommenden Linie ab, lohnt es sich hinten auszusteigen und vice versa. Wobei zu beachten ist, dass hiermit die ankommende Linie gemeint ist. Die gewonnenen Meter können sich als anschlussgarantierend erweisen.

Beispielszenario: Man kommt mit der 61 (Bus) von der Haltestelle Caspar-David-Friedrich-Straße und möchte am Zelleschen Weg in die 11 (Bahn) umsteigen. Auf einer Karte sieht das dann wie folgt aus:

Umstieg von der 61 in die 11 am Zelleschen Weg Es ist klar zu erkennen, dass wenn man in der 61 hinten aussteigt, der Laufweg kürzer ist.

Mathematisch ist der Sachverhalt ein wenig komplexer. Befinden sich die zwei Punkte des Umsteigeweges doch im Koordinatensystem der Erde ohne Informationen was ‘hinten’ oder ‘vorne’ ist. Die naheliegende Lösung wäre den Vektor zwischen Ankunft und Abfahrt zu berechnen. Dabei würde jedoch die generelle Richtung fehlen. Es würden aufwendige Kartentransformationen notwendig. Der Trick ist nun nicht nur den Abstandsvektor zu betrachten, sondern auch den Vektor der ankommenden Linie.

Vektorabbild (Die Vektoren sind aufgrund der Kartentransformation perspektivisch verzerrt.)

Die eigentliche ‘Magie’ passiert im nächsten Schritt. Es wird der Winkel zwischen Ankunftsvektor und Umsteigeweg berechnet. Dieser Winkel ist ein Maß für die die Position der abfahrenden Linie im Bezug auf die Blickrichtung. Wobei Blickrichtung hier die Fahrtrichtung der ankommenden Linie ist. Dazu nimmt man die unverzerrten Vektoren und stellt sie im normalisierten Koordinatensystem dar. Man berechnet den inneren Winkel \(\alpha\) über

$$ \alpha = arccos\left(\dfrac{\vec a \cdot \vec b}{|\vec a| \cdot |\vec b|}\right)$$

Vektorabbild Vektorabbild

Zum Schluss muss man nur noch entscheiden, ob der Winkel größer oder kleiner als 90° ist. Ist er größer, liegt das Ziel ‘hinter’ dem Ausstiegspunkt, ansonsten ‘vor’ dem Ausstiegspunkt. Die einzelnen Winkel können im Polarkoordinaten- system dargestellt werden. Man sieht (bei Blickrichtung nach 0°), dass Werte über 90° im Rücken liegen. Dementsprechend werden die Empfehlungen gewählt. Polarabbild

Umsetzung

Die folgende Implementierung ist in dvbpy enthalten.
Es kann keine Garantie für die Korrektheit der Daten übernommen werden.

Für die Abfrage der DVB-API liefert dvbpy die Route inklusive Fahrweg und Umsteigepunkte aus.

trip = {'arrival': '18:42',
   'departure': '18:29',
   'duration': '00:13',
   'interchange': 1,
   'nodes': [{'arrival': {'coords': '13745527,51028586',
      'stop': 'Zellescher Weg',
      'time': '18:31'},
     'departure': {'coords': '13754579,51026671',
      'stop': 'Caspar-David-Friedrich-Straße',
      'time': '18:29'},
     'direction': 'Dresden Tharandter Straße',
     'line': '61',
     'mode': 'Stadtbus',
     'path': [[13.754564, 51.026653],
      [13.751199, 51.027387],
      [13.748994, 51.027896],
      [13.747537, 51.028197],
      [13.746562, 51.028418],
      [13.745925, 51.028544],
      [13.745527, 51.028586]]},
    {'arrival': {'coords': '13742474,51023562',
      'stop': 'Räcknitzhöhe',
      'time': '18:42'},
     'departure': {'coords': '13745738,51028125',
      'stop': 'Zellescher Weg',
      'time': '18:40'},
     'direction': 'DD Zschertnitz Münzmeisterstr.',
     'line': '11',
     'mode': 'Straßenbahn',
     'path': [[13.745738, 51.028125],
      [13.745206, 51.026847],
      [13.744846, 51.026007],
      [13.744283, 51.024667],
      [13.744205, 51.024507],
      [13.743771, 51.023569],
      [13.743596, 51.023473],
      [13.742474, 51.023571]]}]}

Nun werden aus den einzelnen Stops die Umstiege ermittelt.

interchanges = [# Vorletzter Punkt in Fahrt der ankommenden Linie
                (incoming['path'][-3],
                # Haltepunkt der ankommenden Linie
                [int(x) / 1000000 for x in incoming['arrival']['coords'].split(',')],
                # Haltepunkt der abfahrenden Linie
                [int(x) / 1000000 for x in outgoing['departure']['coords'].split(',')])
                for incoming, outgoing in zip(trip[:-1], trip[1:])]

Transformation von Kartenkoordinaten (in diesem Fall WGS84) zu x-y-Koordinaten.

import pyproj

proj = pyproj.Proj(init='epsg:4326')
interchanges = [[proj(*point) for point in path] for path in interchanges]

Endlich kann man den Winkel berechnen.

import numpy as np

for interchange in interchanges:
    # Ankunftsvektor
    a = np.subtract(interchange[1], interchange[0])
    # Umsteigeweg
    b = np.subtract(interchange[2], interchange[1])

    # Sonderfall, kein Umsteigeweg vorhanden
    if not np.any(b):
        print("mittig", 0.0)
    else:
        b = b / np.linalg.norm(b)
        a = a / np.linalg.norm(a)

        # Winkelberechnung
        deg = np.degrees(np.arccos(np.dot(a,b)))

        # 90°-Entscheidung
        if deg > 90:
            print("hinten", deg)
        else:
            print("vorne", deg)

# für das Beispiel Zellescher Weg 
>>> hinten 120.6

Man beachte den Sonderfall, indem der Umsteigeweg ein Nullvektor ist. Damit ist kein Umsteigen nötig und man kann ‘mittig’ in das Fahrzeug einsteigen. Dies kommt oft an Doppelhaltestellen vor.

Zusammenfassung

Es wurde ausführlichst gezeigt, wie man eine rudimentäre Umsteigeempfehlungen auf Basis von Vektorwinkeln umsetzen könnte. Das dargelegte Konzept bildet aber (noch nicht) nicht alle Sonderfälle ab. So zum Beispiel der Sonderfall von Umsteigetreffen im Nachtverkehr. Da diese an Doppelhaltestellen stattfinden, ist der Umsteigeweg gleich 0. Ausstiegs- und Einstiegspunkt sind auf der selben Position auf der Karte. Trotzdem kann dies physisch nicht möglich sein, weil mindestens zwei Linien gleichzeitig an der Haltestelle stehen. Hier wäre es vonnöten die Ankunftszeiten der Linien zu vergleichen. Daraus ließe sich die Standposition jeder Linie an der Haltestelle ableiten und damit auch der Umsteigeweg.

Meine Hoffnung ist es, dass man in Zukunft irgendeine (nicht unbedingt die hier vorgestellte) Umsteigeempfehlung in die Verbindungsauskunft aufnimmt (looking at you DVB/VVO). In New York kann man für die U-Bahn schon Abteilempfehlungen bekommen. Damit gebe ich ab an Richard Hammond und James May, Experten in Sachen ÖPNV.

New York App

Kartenmaterial von OSM. Erstellt mit Jupyter Notebooks.
powered by numpy and pyproj