Routing-Logik
Diese Datei beschreibt die Routingmodelle, die Kostenberechnung und die Verwendung von Wetterdaten in der Routenbewertung.
Überblick
Die Wetter Routing App berechnet Fahrradrouten auf Basis von OpenStreetMap-Graphen. Die Anwendung bewertet Kanten nicht nur nach ihrer Länge, sondern kann zusätzlich Niederschlagsdaten aus MeteoSwiss-Forecasts berücksichtigen. Dadurch werden Streckenabschnitte mit ungünstigen Wetterbedingungen höher gewichtet.
Die Routinglogik ist Teil der FastAPI-Verarbeitung. Eine Anfrage kommt über den Endpunkt /WAPapi/v1/route ins Backend, wird in backend/api.py vorbereitet und anschliessend an eines der Routingmodelle übergeben.
Die wichtigste Datei für die Routingmodelle ist:
backend/utils_routingmodels.py
Zusätzlich werden Funktionen aus folgenden Dateien verwendet:
backend/utils_forecast.py
backend/utils_graph.py
backend/utils_nc_file.py
Weitere Einordnung zum gesamten Ablauf befindet sich in:
Einordnung in den API-Ablauf
Bevor die eigentliche Routenberechnung startet, bereitet das Backend mehrere Daten vor:
- Start- und Zielpunkt werden aus Adresse oder Koordinaten in WGS84-Koordinaten umgewandelt.
- Die Geschwindigkeit wird von km/h in m/s konvertiert.
- Über
get_nc_file(start_time)wird eine passende NetCDF-Wetterdatei ausbackend/data/NC/ausgewählt. - Aus Start und Ziel wird eine Bounding Box berechnet.
- Ein passender OSM-Graph wird aus
backend/data/graphs/geladen oder über OSMnx neu erstellt. - Start- und Zielkoordinaten werden den nächstgelegenen Nodes im Graphen zugeordnet.
- Abhängig vom Parameter
routingmodelwirdeinfachoderadvancedausgeführt.
Das Ergebnis der Routingfunktion ist eine Sequenz von Node-IDs. Diese wird danach mit OSMnx in ein GeoJSON-ähnliches Format umgewandelt und an das Frontend zurückgegeben.
Routingmodelle
Aktuell stehen zwei Routingmodelle zur Verfügung:
| Routingmodell | Beschreibung |
|---|---|
einfach |
Bewertet alle Kanten einmalig mit dem Forecast zur Startzeit. |
advanced |
Bewertet Kanten zeitabhängig anhand der erwarteten Ankunftszeit. |
Beide Modelle verwenden dieselbe Grundidee für die Wetterbewertung: Pro Kante wird ein Forecast-Wert gelesen und daraus mit compute_rain_adjusted_cost ein wetterabhängiger Kostenwert berechnet.
Einfaches Routingmodell: einfach
Das Modell einfach verwendet einen statischen Dijkstra-Ansatz.
Implementierung im Code:
static_djikstra
Datei:
backend/utils_routingmodels.py
Grundidee
Bei diesem Modell werden alle Kanten vor der eigentlichen Wegsuche einmalig bewertet. Der Forecast wird für alle Kanten zur Startzeit der Route gelesen.
Danach wird mit OSMnx der kürzeste Pfad anhand der berechneten Kosten gesucht. Die Route bleibt dadurch einfach nachvollziehbar und ist schneller berechnet als beim zeitabhängigen Modell.
Ablauf
1. Routingmodell starten
2. Start- und Zielnode übernehmen
3. Forecast zur Startzeit bestimmen
4. Für jede Edge:
- Forecast-Wert lesen
- Regen-Penalty berechnen
- Kosten (`cost`) setzen
- Reisezeit (`travel_time`) setzen
5. Kürzesten Pfad mit OSMnx berechnen
- Gewichtung: `cost`
6. Node-Pfad zurückgeben
Vorteile
- einfach nachvollziehbar
- schneller als das zeitabhängige Modell
- gut geeignet für kurze Routen
- geringe Komplexität
Grenzen
- Wetter wird nur zur Startzeit berücksichtigt
- spätere Wetteränderungen entlang der Route werden nicht berücksichtigt
- bei längeren Routen weniger realistisch
Erweitertes Routingmodell: advanced
Das Modell advanced verwendet einen zeitabhängigen Dijkstra-Ansatz.
Implementierung:
time_dependent_dijkstra
Datei:
backend/utils_routingmodels.py
Grundidee
Bei diesem Modell wird nicht nur betrachtet, welche Kante befahren wird, sondern auch zu welchem Zeitpunkt diese Kante voraussichtlich erreicht wird. Dadurch kann dieselbe Kante je nach Ankunftszeit unterschiedliche Kosten erhalten.
Im Unterschied zum einfachen Modell wird der Forecast während der Suche laufend anhand der erwarteten Ankunftszeit abgefragt. Zwischen den Forecast-Zeitschritten wird interpoliert.
Ablauf
1. Advanced-Routingmodell starten
2. Start-State initialisieren
3. Priority Queue erstellen
4. Startnode mit Kosten `0` einfügen
5. Solange die Priority Queue nicht leer ist:
- State mit den geringsten Kosten entnehmen
- Prüfen, ob das Ziel erreicht wurde
- Falls ja:
- Pfad rekonstruieren
- Node-Pfad zurückgeben
- Falls nein:
- Ausgehende Kanten prüfen
- Travel Time der Kante berechnen
- Ankunftszeit am Nachbarn bestimmen
- Forecast zur Ankunftszeit interpolieren
- Regen-Penalty berechnen
- Neue Gesamtkosten berechnen
- Prüfen, ob ein besserer Pfad gefunden wurde
- Falls ja:
- `dist` und `parent` aktualisieren
- Neuen State in die Priority Queue einfügen
6. Falls die Priority Queue leer ist:
- Keine Route gefunden
Vorteile
- realistischere Wetterbewertung
- geeignet für längere Routen
- berücksichtigt zeitliche Änderungen im Forecast
- bewertet Regen dort, wo die Route ihn voraussichtlich erreicht
Grenzen
- rechenintensiver als
einfach - komplexere Zustandsverwaltung
- mehr Forecast-Abfragen während der Suche
Kantenbewertung
Für jede Kante wird eine Kostenfunktion berechnet. Die Kosten basieren auf der Kantenlänge und einem wetterabhängigen Zuschlag.
Die Berechnung erfolgt über:
compute_rain_adjusted_cost
Datei:
backend/utils_forecast.py
Grundformel:
rain_amount = max(forecast, 0)
cost = length * (1 + multiplier * rain_amount^exponent)
Wenn kein Regen vorhanden ist:
cost = length
Für die höchste Regenempfindlichkeit gilt zusätzlich:
cost = infinity
Damit werden Kanten mit Regen vollständig vermieden, sofern eine alternative Route existiert.
Der berechnete Wert wird als cost an der Kante gespeichert und für die Pfadsuche verwendet. Zusätzlich wird aus Länge und Geschwindigkeit eine travel_time berechnet.
Regenempfindlichkeit rainresistence
rainresistence steuert, wie stark Regen die Kosten einer Kante erhöht. Der Parameter wird über die API übergeben und an compute_rain_adjusted_cost weitergereicht.
rainresistence |
multiplier |
exponent |
Wirkung |
|---|---|---|---|
highest |
- | - | Regen hat keinen Einfluss auf die Kosten. |
high |
25.0 | 1.0 | Regen erhöht die Kosten leicht. |
medium |
100.0 | 1.2 | Regen wird deutlich vermieden. |
low |
400.0 | 1.4 | Regenabschnitte werden stark bestraft. |
lowest |
- | - | Kanten mit Regen werden vollständig vermieden. |
Bei highest bleibt cost = length, auch wenn Regen vorhergesagt ist. Bei lowest wird für Kanten mit Regen cost = infinity gesetzt.
Graphen
Die Routingmodelle arbeiten auf OSMnx-Graphen. Ein Graph enthält Nodes, Kanten, Kantenlängen und Geometrien. Beim Erstellen eines neuen Graphen werden fehlende Kantengeometrien ergänzt und die Kanten anschliessend mit Wetterzellen verknüpft.
Gespeicherte Graphen befinden sich im Ordner:
backend/data/graphs/
Typische Dateien:
*.graphml: gecachte und aufbereitete Graphenindex.json: Verzeichnis der verfügbaren Graphen
Wenn ein passender Graph bereits vorhanden ist, wird er aus dem Cache geladen. Falls kein passender Graph vorhanden ist, wird ein neuer Graph über OSMnx erstellt, mit Wetterzellen ergänzt und gespeichert.
Wetterzellen auf Kanten
Damit eine Kante mit Wetterdaten bewertet werden kann, besitzt sie Verweise auf eine Zelle im NetCDF-Wetterraster:
cell_icell_jcell_id
Die Zuordnung wird beim Erstellen eines neuen Graphen berechnet und im gespeicherten Graphen abgelegt. Dadurch kann das Routing später direkt den passenden Forecast-Wert pro Kante lesen.
Die detaillierte Beschreibung der Rasteraufbereitung und Zellzuordnung befindet sich in Wetterdaten.
Wetterdaten im Routing
Im Routing wird die geöffnete NetCDF-Datei nur noch abgefragt. Die Aufbereitung, Aktualisierung, Auswahl der Datei und Zuordnung der Wetterzellen ist in Wetterdaten beschrieben.
Beim einfachen Modell wird der Forecast ohne zeitliche Interpolation zur Startzeit gelesen. Beim erweiterten Modell wird der Forecast zur erwarteten Ankunftszeit interpoliert.
Ergebnis
Das Ergebnis der Routenberechnung ist ein Node-Pfad. Dieser wird anschliessend in backend/api.py mit OSMnx in ein GeoJSON-ähnliches Format umgewandelt.
Die Antwort enthält pro Routensegment unter anderem:
osmidlengthcosttravel_timegeometry