Erklärung der Funktionen von RecyclRoute
In diesem Abschnitt werden die Funktionen und Interaktionen des RecyclRoute-Planner beschrieben.
Funktionen Planner:
Nach der Wahl der Planer-Page auf der Startpage wird man auf die in Grüntönen gehaltene Planner-Page weitergeleitet. Auf dieser Seite können die Standartfunktionen “Aktuelle Position”, “ nach Norden ausrichten” und “Layer-Manager” (Punkte eines Projektes anzeigen), ebenfalls genutzt werden. Die Hauptfunktion ist jedoch das Erstellen und Bearbeiten von Projekten und dem dazugehörigen Erstellen von Routen sowie dem Navigieren durch die Route.
Projektmanager
Um den Projektmanager aufrufen zu können, muss auf den Button in der Fusszeile unten rechts geklickt werden und es öffnet sich ein neues Popup-Fenster. Hier sieht man alle Projekte, welche in der Datenbank gespeichert sind. Durch Klicken auf ein bestimmtes Projekt öffnet sich ein neues Popup, in dem man die einzelnen Funktionen im ausgewählten Projekt ausführen kann. Insgesamt hat es vier Buttons, die jeweils folgende Funktionen ausführen:
- Route berechnen - Neue Berechnung der Route
- Routing starten - Navigation durch eine bestehende Route starten
- Statistiken - Erstellen von diversen Statistiken über das Projekt
- Projekt löschen - Das ausgewählte Projekt und die dazugehörigen Reports werden gelöscht
Neues Projekt erstellen
Im Projektmanager ganz unten gibt es den Button Neues Projekt erstellen. Hier kann ein neues Projekt erstellt werden. Es erscheint ein neues Popup, in welchem man den Projektnamen, die Gemeinde, sowie ein Sammeldatum eingeben kann. Man kann nur zum nächsten Schritt weitergeleitet werden, wenn man alle Felder ausgefüllt hat. Mit dem Klick auf “Weiter” werden die eingegebenen Werte gespeichert und man wird zur Definition des Polygons (Gebiet der Sammlung) weitergeleitet und die Karte zoomt zur eingegebenen Gemeinde.
Polygon definieren & Startpunkt setzen
Nun muss auf der Karte ein Polygon gezeichnet werden, welches das ganze gewünschte Gebiet komplett umschliesst. Mit Doppelklick oder Klick auf den Button Perimeter speichern bestätigt man die Eingabe und man wird nach Eingabe des Startpunktes für die Route gefragt. Der Startpunkt muss zwingend innerhalb des Polygons definiert werden. Mit Klick auf die Karte wird der Startpunkt direkt gesetzt und die Route wird im Hintergrund berechnet. Während der Berechnung erscheint ein Popup, welches bei erfolgreichem Berechnen verschwindet.
Nachfolgend ist ein Kurzvideo, welches den kompletten Ablauf der Erstellung eines neuen Projekts aufzeigt.
Navigationpage
Nach der Berechnung kommt man auf die Navigationseite, auf welcher man durch die Navigation der berechneten Route geführt wird. Dabei wird man Schritt für Schritt durch die Navigation geleitet.
Geplant ist auch, dass die Abschnitte nach Status (erledigt, aktueller Abschnitt, als nächster folgend, als übernächster folgend) unterschiedlich eingefärbt werden, damit der Benutzer möglichst einfach durch die Route geführt wird.
Routing- und Optimierungs-Tool für vollständige Wegenetzbefahrung
Das Tool ist in einem Python-Skript aufgebaut und berechnet automatisiert eine optimale Route durch ein definiertes Strassennetz. Ziel ist es, alle relevanten Strassen in einem Gebiet möglichst effizient und ohne doppelte Befahrung abzufahren – ähnlich wie das sogenannte Chinese Postman Problem.
Eingaben
Für die Ausführung des Skripts werden folgende Daten benötigt:
- Ein JSON, das das Polygon des Zielgebiets definiert (EPSG:4326)
- Eine GeoPackage-Datei, die mit einem weiteren Skript (Preprocessing) vorbereitet wird und die Geometrien sowie Eigenschaften der Strassenachsen enthält (SwissTLM3D)
- Die Startkoordinaten, die den Ausgangspunkt der Route festlegen (EPSG:3857, aus der Webkarte)
Ausgaben
Das Skript erzeugt verschiedene Dateien, die direkt verwendet werden können oder für zukünftige Anwendungen vorgesehen sind:
- route_full.geojson: Die gesamte berechnete Route als GeoJSON
- navigation.geojson: Eine Datei mit Navigationsanweisungen (turn-by-turn)
- trace_route.json: Rohdaten der Valhalla-Routenantwort
- Temporäre Dateien: Für Debugging und Weiterverarbeitung
Verwendete Module
Kategorie | Bibliotheken/Technologien | Verwendungszweck |
---|---|---|
Geodaten | geopandas, shapely, pyproj | Geometrieoperationen, Koordinatentransformation |
Graphentheorie | networkx, scipy.spatial.cKDTree | Netzwerkaufbau, Knotenanalyse |
Optimierung | ortools (Google OR-Tools) | TSP-Lösung, Matching-Algorithmen |
Routing | requests, Valhalla (lokal) | Routing-Anfragen |
Allgemein | numpy, json, os | Datenverarbeitung, Dateioperationen |
Ablauf und Theoretische Grundlagen
- Datenvorbereitung und Koordinatentransformation
Die Startkoordinaten werden von EPSG:3857 (Standard von MapLibre) ins Schweizer Landeskoordinatensystem EPSG:2056 transformiert (translate_start_coord(coord_3857)). Das Strassennetz im Zielgebiet wird aufbereitet, indem der GeoPackage-Layer eingelesen und irrelevante Strassen gefiltert werden (filter_street_data(gdf)).
Ausgeschlossen werden u.a.:- Objektarten wie Ausfahrt, Einfahrt, Autobahn, Raststätte, Zufahrt, Dienstzufahrt, Autozug, Fähre, Autostrasse, Klettersteig, Provisorium
- Wanderwegtypen wie Wanderweg, Bergwanderweg, Alpinwanderweg
- Verkehrsbeschränkungen wie Allgemeines Fahrverbot, Fussweg, Fussgängerzone, Radweg, Reitweg, Panzerpiste, Teststrecke, Gesperrt
- Belagsarten wie Natur
-
Netzwerkaufbau und -bereinigung
Die Liniengeometrien der verbleibenden Strassen werden analysiert. Punkte entlang der Linien werden mittels Punkt-Snapping (Toleranz 0.5m) zusammengeführt. Gerade Strassenabschnitte mit einem Winkel von mehr als 170° zwischen den Segmenten werden automatisch zusammengeführt (merge_straight_segments(graph)).
Aus den Segmenten entsteht ein ungerichteter Multigraph (Knoten = Punkte, Kanten = Strassenabschnitte). Zu kleine oder isolierte Komponenten (<3 Knoten) werden entfernt (remove_disconnected_components(graph)). - Routenoptimierung
Das Chinese Postman Problem wird gelöst:- Knoten mit ungeradem Grad werden identifiziert und so gepaart, dass die zusätzlichen Verbindungen eine minimale Gesamtlänge haben (gewichtetes Matching).
- Der resultierende Graph enthält nur noch gerade Knotengrade und ist somit eulerisierbar – ein Eulerkreis kann gefunden werden (generate_chinese_postman_coordinates(graph, start_node_coord)), berechnet mit dem Hierholzer-Algorithmus.
- Optional kann die Koordinatenfolge gefiltert werden, um Punkte mit zu geringem Abstand zu entfernen (deaktiviert).
- Weitere Optimierung und Routing
- Eine Distanzmatrix mit realen Wegstrecken (über Valhalla, nicht Luftlinie) wird erstellt (build_dist_matrix(coords, batch_size=50, max_workers=25)). Das Skript erkennt automatisch, ob Auto- oder Fuss-Routing verwendet werden muss.
- Mit dieser Matrix wird das Travelling Salesman Problem (TSP) gelöst (solve_tsp(dist_matrix)), um die Reihenfolge der Punkte mit minimaler Gesamtreiselänge zu bestimmen (Google OR-Tools, PATH_CHEAPEST_ARC).
- Die optimierten Koordinaten werden in Teilblöcke unterteilt und jeweils bei Valhalla angefragt (get_valhalla_route_chunked), um turn-by-turn Navigation zu erhalten. Pro Block werden JSON- und GeoJSON-Dateien erzeugt, die zu einer Gesamtroute zusammengefügt werden. Parallel werden detaillierte Navigationsanweisungen ausgegeben.
Inhaltsverzeichnis Funktionsdokumentation
- translate_start_coord(coord_3857)
- prepare_graph(input_json, input_gpkg)
- filter_street_data(gdf)
- create_graph(gdf, polygon)
- build_graph(roads)
- clean_graph(graph)
- remove_disconnected_components(graph, min_nodes=3)
- merge_straight_segments(graph)
- calculate_angle(p1, p2, p3)
- vector(a, b)
- generate_chinese_postman_coordinates(graph, start_node_coord)
- chinese_postman(graph, start_node=None)
- try_sources_to_targets(sources, targets, last_costing)
- build_dist_matrix(coords, batch_size=50, max_workers=25)
- fetch_distances(i)
- solve_tsp(dist_matrix)
- distance_callback(from_idx, to_idx)
- decode_polyline6(encoded)
- get_valhalla_route_chunked(coords, order, chunk_size=100, output_dir=”output”)
- save_geojson(valhalla_json, filename=”route.geojson”)
- save_route_with_directions(valhalla_routes, filename=”navigation.geojson”)
- save_navigation_geojson_from_shape_indices(valhalla_routes, filename=”navigation.geojson”)
- save_trace_route_json(valhalla_routes, filename=”trace_route.json”)
- merge_geojson_parts(part_filenames, output_file=”route_full.geojson”)
- run_routing(input_json, input_gpkg, start_node_coord_3857, output_dir=”output”)
Funktionsdokumentation
translate_start_coord(coord_3857)
Zweck:
Transformiert eine Koordinate vom Web-Mercator-Projektionssystem (EPSG:3857, z.B. aus einer Webkarte) ins Schweizer Landeskoordinatensystem LV95 (EPSG:2056).
Funktionsweise:
Verwendet pyproj.Transformer, um die Koordinate exakt umzuwandeln.
Input:
Tupel (x, y) in EPSG:3857
Output:
Tupel (x, y) in EPSG:2056
prepare_graph(input_json, input_gpkg)
Zweck:
Erzeugt aus einem Polygon (vom Webfrontend) und einem Strassenlayer (SwissTLM3D) ein bereinigtes, routingfähiges Strassennetzwerk.
Funktionsweise:
- Liest den Strassenlayer aus einer GeoPackage-Datei.
- Filtert ungeeignete Strassen mit
filter_street_data
. - Liest das Polygon aus der JSON-Datei und transformiert es ins passende Koordinatensystem.
- Schneidet die Strassen auf das Polygon zu und erstellt mit
create_graph
den Netzwerkgraphen.
Input:
- JSON mit Polygon (“perimeter”) in EPSG:4326
- GPKG-Datei mit Layer tlm_strassen_strasse
Output:
Netzwerkgraph als networkx.MultiGraph
filter_street_data(gdf)
Zweck:
Entfernt Strassen, die für die Routenplanung ungeeignet sind (z.B. Fusswege, Zufahrten).
Funktionsweise:
Prüft relevante Attribute (z.B. objektart, wanderweg, belagsart) und filtert alle Objekte, die in Ausschlusslisten enthalten sind.
Input:
GeoDataFrame mit Strassenattributen
Output:
Gefilterter GeoDataFrame
create_graph(gdf, polygon)
Zweck:
Schneidet den Strassenlayer mit dem Polygon und baut daraus einen Netzwerkgraphen.
Funktionsweise:
- Schneidet die Geometrien auf das Polygon zu.
- Erstellt den Graphen mit
build_graph
. - Bereinigt das Netzwerk mit
clean_graph
.
Input:
- Gefilterter GeoDataFrame
- Polygon (shapely-Objekt)
Output:
Netzwerkgraph
build_graph(roads)
Zweck:
Wandelt Liniengeometrien in einen Netzwerkgraphen um und sorgt dafür, dass nahe beieinanderliegende Punkte zusammengefasst werden.
Funktionsweise:
- Erstellt Knoten und Kanten aus Linien.
- Nutzt ein Punkt-Snapping, um Doppelknoten zu vermeiden.
Input:
GeoDataFrame mit Linien
Output:
networkx.MultiGraph
clean_graph(graph)
Zweck:
Bereinigt das Netzwerk, indem kleine isolierte Komponenten entfernt und geradlinige Segmente zusammengeführt werden.
Funktionsweise:
- Entfernt kleine Teilgraphen mit “remove_disconnected_components”.
- Führt Knoten mit Grad 2 zu längeren Kanten zusammen mit
merge_straight_segments
.
Input:
networkx.MultiGraph
Output:
Bereinigter networkx.MultiGraph
remove_disconnected_components(graph, min_nodes=3)
Zweck:
Entfernt kleine, isolierte Teilgraphen mit weniger als min_nodes
Knoten.
Funktionsweise:
- Identifiziert zusammenhängende Komponenten.
- Entfernt alle Komponenten, die zu klein sind.
Input:
networkx.MultiGraph, min_nodes (Standard: 3)
Output:
Bereinigter Graph
merge_straight_segments(graph)
Zweck:
Führt fast geradlinige Strassenabschnitte zu einer Kante zusammen, um die Komplexität des Netzwerks zu reduzieren.
Funktionsweise:
- Sucht Knoten mit Grad 2.
- Prüft mit
calculate_angle
, ob die angrenzenden Kanten nahezu gerade verlaufen. - Verschmilzt die Kanten und entfernt den Zwischenknoten.
Input:
networkx.MultiGraph
Output:
Modifizierter Graph
Hinweis:
Das Verfahren basiert auf der Winkelberechnung zwischen Vektoren. Siehe Wikipedia: Line Simplification für mathematische Hintergründe.
calculate_angle(p1, p2, p3)
Zweck:
Berechnet den Winkel zwischen zwei Liniensegmenten, die sich in p2 treffen.
Funktionsweise:
- Erzeugt Richtungsvektoren von p2 nach p1 und von p2 nach p3 mit
vector
. - Berechnet den Winkel über das Skalarprodukt.
Input:
p1, p2, p3 als (x, y)-Tupel
Output:
Winkel in Grad (0°–180°)
Mathematischer Hintergrund:
Siehe Wikipedia: Skalarprodukt und Winkel zwischen Vektoren.
vector(a, b)
Zweck:
Berechnet den Richtungsvektor von Punkt a zu Punkt b.
Funktionsweise:
- Subtrahiert die Koordinaten von a von b.
Input:
a, b als (x, y)-Tupel
Output:
(dx, dy) als Richtungsvektor
generate_chinese_postman_coordinates(graph, start_node_coord)
Zweck:
Berechnet eine Route, die alle Kanten mindestens einmal abfährt (Chinese Postman Problem).
Funktionsweise:
- Findet ungerade Knoten und paart sie optimal (Matching).
- Ergänzt das Netzwerk, sodass ein Eulerkreis möglich ist.
- Nutzt chinese_postman, um den Eulerkreis zu berechnen.
- Gibt die Route als Liste von Koordinaten (EPSG:4326) zurück.
Input:
- Netzwerkgraph
- Startkoordinate (EPSG:2056)
Output:
Liste von [lon, lat]-Koordinaten (EPSG:4326)
Theoretischer Hintergrund:
Das Chinese Postman Problem ist ein klassisches Optimierungsproblem der Graphentheorie. Siehe Wikipedia: Chinese Postman Problem.
chinese_postman(graph, start_node=None)
Zweck:
Löst das Chinese Postman Problem auf dem Netzwerkgraphen.
Funktionsweise:
- Identifiziert ungerade Knoten.
- Findet ein Minimum Weight Matching.
- Ergänzt das Netzwerk und berechnet den Eulerkreis.
Input:
networkx.MultiGraph, optionaler Startknoten
Output:
Liste von Knoten in Eulerkreis-Reihenfolge
try_sources_to_targets(sources, targets, last_costing)
Zweck:
Fragt Entfernungen zwischen mehreren Punkten bei der Valhalla-API ab, mit Fallback auf ein alternatives Routingprofil bei Fehlern.
Funktionsweise:
- Baut eine Anfrage an die Valhalla-API.
- Nutzt ggf. ein alternatives Profil („pedestrian“), falls das gewünschte Routingprofil fehlschlägt.
Input:
- sources: Liste von Koordinaten
- targets: Liste von Koordinaten
- last_costing: Routingprofil
Output:
Antwort der Valhalla-API (JSON)
build_dist_matrix(coords, batch_size=50, max_workers=25)
Zweck:
Erstellt eine Distanzmatrix zwischen allen Koordinatenpunkten für das TSP.
Funktionsweise:
- Teilt die Abfragen in Batches und Threads auf.
- Nutzt intern
try_sources_to_targets
undfetch_distances
.
Input:
Liste von [lon, lat]-Koordinaten
Output:
2D-Numpy-Matrix mit Distanzen in Metern
fetch_distances(i)
Zweck:
Hilfsfunktion für build_dist_matrix
, berechnet eine Zeile der Distanzmatrix.
Input:
Index i (Zeile)
Output:
Liste von Distanzen für Zeile i
solve_tsp(dist_matrix)
Zweck:
Bestimmt eine optimale Besuchsreihenfolge für die Koordinatenpunkte (Travelling Salesman Problem).
Funktionsweise:
- Nutzt Google OR-Tools zur Lösung des TSP.
- Verwendet die Strategie „PATH_CHEAPEST_ARC“.
Input:
Distanzmatrix
Output:
Liste von Indexpositionen (optimale Reihenfolge)
Theoretischer Hintergrund:
Das TSP ist ein NP-schweres Problem. Siehe Wikipedia: Travelling Salesman Problem.
distance_callback(from_idx, to_idx)
Zweck:
Callback-Funktion für die Distanzberechnung im TSP-Löser.
Input:
from_idx, to_idx: Indizes
Output:
Distanzwert
decode_polyline6(encoded)
Zweck:
Dekodiert eine Valhalla-Polyline (mit 6 Dezimalstellen) in eine Liste von GPS-Koordinaten.
Funktionsweise:
- Wandelt den kodierten String in eine Liste von [lon, lat]-Paaren um.
Input:
encoded: String
Output:
Liste von [lon, lat]-Koordinaten
get_valhalla_route_chunked(coords, order, chunk_size=100, output_dir=”output”)
Zweck:
Ruft bei Valhalla segmentierte Routenabfragen ab und speichert die Ergebnisse je Teilroute.
Funktionsweise:
- Teilt die Koordinaten in Blöcke (Chunks).
- Fragt für jeden Block die Route ab.
- Speichert die Ergebnisse als JSON und GeoJSON.
Input:
- coords: Liste von Koordinaten
- order: Reihenfolge der Indizes
- chunk_size: Grösse der Blöcke
- output_dir: Zielverzeichnis
Output:
Routenblöcke als .json und .geojson
save_geojson(valhalla_json, filename=”route.geojson”)
Zweck:
Speichert die Liniengeometrie aus einer Valhalla-Route als GeoJSON-Datei.
Input:
- valhalla_json: Valhalla-Antwort
- filename: Dateiname
Output:
GeoJSON-Datei
save_route_with_directions(valhalla_routes, filename=”navigation.geojson”)
Zweck:
Speichert alle Fahranweisungen als GeoJSON mit Schrittinformationen aus den Valhalla-Maneuvern.
Input:
- valhalla_routes: Liste von Valhalla-Routen
- filename: Dateiname
Output:
GeoJSON mit Navigationsanweisungen
save_navigation_geojson_from_shape_indices(valhalla_routes, filename=”navigation.geojson”)
Zweck:
Exportiert Manöver auf Basis der shape_indices (Indexbereiche im Linienzug) als GeoJSON.
Input:
- valhalla_routes: Liste von Valhalla-Routen
- filename: Dateiname
Output:
GeoJSON-Datei
save_trace_route_json(valhalla_routes, filename=”trace_route.json”)
Zweck:
Speichert alle Anweisungen und Geometrien als JSON-Liste (kein GeoJSON).
Input:
- valhalla_routes: Liste von Valhalla-Routen
- filename: Dateiname
Output:
JSON-Datei
merge_geojson_parts(part_filenames, output_file=”route_full.geojson”)
Zweck:
Fasst mehrere GeoJSON-Dateien zu einer Gesamtdatei zusammen.
Input:
- part_filenames: Liste von Dateinamen
- output_file: Zieldatei
Output:
Gesamte GeoJSON-Datei
run_routing(input_json, input_gpkg, start_node_coord_3857, output_dir=”output”)
Zweck:
Steuert den gesamten Routing-Workflow von der Eingabe bis zur Ausgabe.
Funktionsweise:
- Transformiert den Startpunkt ins richtige Koordinatensystem.
- Bereitet das Strassennetzwerk auf.
- Berechnet den chinesischen Postbotenpfad.
- Erstellt die Distanzmatrix und löst das TSP.
- Fragt die Route segmentweise bei Valhalla ab.
- Speichert die Ergebnisse als GeoJSON und JSON.
Input:
- input_json: Polygon für das Sammelgebiet
- input_gpkg: Strassenlayer
- start_node_coord_3857: Startpunkt aus Webkarte
- output_dir: Ausgabeordner
Output:
Alle Routing- und Navigationsdaten als Dateien im Output-Ordner