In unserem vorigen Beitrag zum Routing mit Leaflet haben wir eine Anwendung gezeigt, die das Navigieren für nur eine Fortbewegungsart erlaubte: entweder nur Auto oder nur Fahrrad oder nur Fußgänger. Nach langem Studium von umfangreicher Literatur und Dokumentationen konnte ich jetzt erstmals eine (Web-) Anwendung programmieren, die es dem Benutzer ermöglicht, zwischen unterschiedlichen Fortbewegungsarten durch einfachen Mausklick auf entsprechende Kartensymbole auszuwählen (s. Beitragsbild oben). Dies möchten wir hier am Beispiel für eine Routinganwendung für das Energie-Museum Berlin demonstrieren.
Demo zum Routing
Bedient wird die Navigationsappapp, indem man in der rechts oben auf der Karte befindlichen Dialogbox in das obere Feld die Startadresse eingibt und mit der Eingabetaste abschließt. Sollte die eingegebene Adresse eindeutig sein, wird sie vom Routing-Programm sofort verwendet, anderenfalls wird entweder eine Liste mit ähnlichen Adressen angezeigt, aus der man die richtige auswählen kann, oder es wird gar nichts angezeigt, dann existiert die eingegebene Adresse nicht – zumindest nicht in der abgefragten Geodatenbank, sie könnte aber auch falsch geschrieben sein…
Probieren Sie es aus!
Programmlogik
In der hier vorgestellten Beispielapp ist das Routing für das Auto voreingestellt. Eine Änderung auf ein anderes Verkehrsmittel lässt sich leicht anpassen. Hat man eine Startadresse eingegeben, wird als erstes also die Route für das Auto berechnet. Klickt man nun auf einen der links stehenden anderen Buttons (Auto, Fahrrad, Fußgänger), wird sofort die neu berechnete Route für diesen Anwendungsfall angezeigt. Die Routenberechnung kann je nach Entfernung und Schwierigkeitsgrad – für Fußgänger muss mehr gerechnet werden als für Autos – unterschiedlich lang dauern. Wir haben es ausprobiert: ein Radler braucht zwischen 86 und 89 Stunden für die ca. 1000 km lange Strecke, ein Fußgänger 192 Stunden, ein Autofahrer benötigt ca. 12 Stunden.
Die Web-App funktioniert “Ereignis-gesteuert”. Das heißt, jedes Mal, wenn – wie in diesem Fall ein so genanntes Maus-Ereignis durch Mausklick hervorgerufen wird, wird ein bestimmter Programmteil ausgeführt. Die von uns besonders zu beobachtenden Ereignisse beschränken sich auf die Mausklicks auf die drei Bildchen unter dem Kartenzoom-Werkzeug.
Alle anderen Ereignisse (Zoomen, Karte verschieben), die auf der Karte stattfinden können, müssen wir nicht selbst bis in die letzte Ecke ausprogrammieren, weil eine Vielzahl von (Programm-) Bibliotheken zur Verfügung stehen; noch besser: die Bibliotheken kosten nichts!
Zutaten
Wie bereits beschrieben (s. hier) ist es recht einfach, Landkarten individuell gestaltet auf einer Website darzustellen. Kommerzielle Geodatendienste kann man bei Google, Bing und Yahoo nutzen, in sehr bescheidenem Maße auch bei öffentlichen Stellen. Am Besten ist das offene Netzwerk von OpenStreetMap nutzbar, das die Nutzung seiner Geodaten über die Datenbankschnittstelle Nominatim und diverser Programmierschnittstellen – so genannter API (Application Programming Interface) bereit stellt. Mittels API kann man auch auf die Anbieter anderer Geo-Dienstleistungen zugreifen, es ist jedoch oft schwierig, an die entsprechenden Dokumentationen heranzukommen.
Der Nachteil bei der Nutzung der kommerziellen Geodatendienste (Google, Bing usw.) liegt darin, dass sie bisweilen – auch ohne Vorankündigung – ihre API erneuern, austauschen oder einfach ändern; für den Anwendungsprogrammierer ist das ärgerlich, weil er möglicherweise alles neu programmieren muss. Dazu muss man allerdings erst einmal die (hoffentlich vorliegende) Dokumentation verinnerlicht haben.
Wir haben auf folgende Werkzeugkisten zugegriffen:
OpenStreetMap | Kartendienst |
LeafLet | Bibliothek zum Zugriff auf Geodaten-Anwendungen |
Nominatim | Werkzeug zum Auffinden eines Ortes auf der Erde über den Namen, um ihn in entsprechende Geokoordinaten umzuwandeln, die in anderen Bibliotheken verwendbar sind. |
Font Awesome Icons |
Frei skalierbarer Font mit Icons, die wie ein Schriftsatz behandelt werden (mehr Informationen hier). |
Leaflet Routing Machine |
Bibliothek zum erleichterten Zugriff auf die API unterschiedlicher Routing Server (Mapbox, Grashopper, OSRM, Google usw.) |
EasyButton |
Bibliothek zum leichten Einfügen von Schaltflächen, die zur Programmsteuerung verwendet werden können. |
Das HTML-Dokument
Wie auch bei den bisher vorgestellten Kartenprogrammier-Projekten (OpenLayers, Leaflet) wird die Kartendarstellung durch ein HTML-Dokument vorgenommen, in dem sich zur Realisierung des dynamischen und/oder interaktiven Erscheinungsbilds auch Javascript-Code befindet. Das HTML-Dokument besteht aus einem head– und einem body-Teil.
Im head-Teil werden die (CSS-) Stylesheets und Javascript-Bibliotheken in das Dokument eingebunden, die zur Funktionalität der Webseite benötigt werden. Hier muss man eine Entscheidung treffen: Entweder, man installiert die Stylesheets und die Javascript-Bibliotheken auf dem eigenen Server oder von einer externen Quelle, die man aus der Dokumentation von Leaflet und den jeweiligen Plugins erfahren kann.
Installiert man die Plugins auf dem eigenen Server, muss man sich die Quellen herunterladen und auf dem eigenen Server in eine geeignete Ordnerstruktur einpassen. Man kann aber auch durch externe Links auf die Bibliotheken dasselbe Ergebnis erreichen. Beide Möglichkeiten haben Vor- und Nachteile: installiert man auf dem eigenen Server, verbraucht man Speicherplatz, und man muss sehr diszipliniert mit der Einrichtung einer geeigneten Ordnerstruktur umgehen.
Der head-Teil
<!DOCTYPE HTML> <html> <!-- ======================================================================================= --> <head> <title>Suchen Sie Ihren Weg...</title> <meta charset="utf-8" /> <!-- Bereich des Browserfensters für die Darstellung der Inhalte festlegen, folgender Tag unterstützt responsives Layout. --> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> <!-- Stylesheet und Leaflet-Bibliothek einbinden --> <link rel="stylesheet" href="https://unpkg.com/leaflet@latest/dist/leaflet.css" /> <script src="https://unpkg.com/leaflet@1.3.4/dist/leaflet.js"></script> <!-- Stylesheet und Plugin des Geocoders einbinden --> <link rel="stylesheet" href="Control.Geocoder.css" /> <script src="Control.Geocoder.js"></script> <!-- Stylesheet und Plugin der Leaflet Routing Machine einbinden --> <link rel="stylesheet" href="leaflet-routing-machine.css" /> <script src="leaflet-routing-machine.js"></script> <!-- Plugin von Font Awesome einbinden --> <script src="https://kit.fontawesome.com/<dein Schlüssel>.js" crossorigin="anonymous"></script> <!-- Stylesheet und Plugin von EasyButton einbinden --> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.css"> <script src="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.js"></script> <style> body {padding: 0;margin: 0;} html, body, #mapid {height: 100vh;width: 100vw;} </style> </head>
Der Dokumenten-body
<!-- ======================================================================================= --> <body> <!-- Das Landkartenobjekt erstellen --> <div style="height: 800px; width=800px" id="landkarte"></div> <script> // Layer mit den unterschiedlichen Kartenansichten deklarieren: // - OSM Mapnik Straßenansicht // - Thunderforest Fahrradkarte // - ÖPNV-Karte var osmMap = L.tileLayer('https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png', {maxZoom: 18,attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'}), cycleMap = L.tileLayer('https://{s}.tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=<dein Schlüssel>', {attribution: '© <a href="https://openstreetmap.org">OpenStreetMap</a> Contributors © <a href="https://thunderforest.com/">Thunderforest</a>"'}), opnv = new L.tileLayer('http://tile.memomaps.de/tilegen/{z}/{x}/{y}.png', { minZoom: 3, maxZoom: 18, attribution: 'Map data © <a href="http://memomaps.de/">ÖPNV Karte</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>' } ); // Kartenobjekt mit Kartenmittelpunkt. Hier 52.44338, 13.33057 // OSM Straßenansicht ist Standardansicht var landkarte = L.map('landkarte', {layers: [osmMap]}) // Nur ein Layer angeben! .setView([ 52.4433752, 13.3301968], 13); // Basislayer für die Layerkontrolle var baseLayers = { "Straßenkarte": osmMap, "Fahrrad": cycleMap, "ÖPNV": opnv }; // Umschalter für die unterschiedlichen Kartenlayer L.control.layers(baseLayers).addTo(landkarte); // Routenkontroller für die Eingabe von Start- und Endpunkt // hier: Startpunkt wird nicht gesetzt, muss vom Anwender eingegeben werden, // Endpunkt ist Energie-Museum Berlin. // Linieneigenschaften für die Routenlinie samt Alternativrouten. // Karte wird auf die gesamte wird auf gesamte Länge der Route gezoomt. // Mapbox wir als Router festgelegt (Key erforderlich), // Routerprofil: Standard ist das Routen für Autos unter Berücksichtigung der Verkehrssituation (Stauvermeidung), // Sprache der Navigationsanweisungen ist Deutsch, // var routingcontrol = L.Routing.control({waypoints: [L.latLng(),L.latLng(52.4433752, 13.3301968),], lineOptions: { styles: [ {color: 'magenta', opacity: 1, weight: 5}, {color: 'magenta', opacity: 1, weight: 5}, {color: 'magenta', opacity: 1, weight: 5}]}, fitSelectedRoutes : 'true', router: L.Routing.mapbox('<dein Schlüssel>', {profile: 'mapbox/driving-traffic', language: 'de', alternatives:'true'}), geocoder: L.Control.Geocoder.nominatim({}) }).addTo(landkarte); // Schaltknöpfe für das Auslösen von Routing-Aktionen // Nach jedem Klick auf die unterschiedlichen Knöpfe wir ein entsprechendes Ereignis ausgelöst. // Das Verfahren zum Suchen des Wegs zwischen Start- und Endpunkt wird im Profil des // Routenkontrollers geändert. Anschließend wird der Routen-Suchalgorithmus neu angestoßen. L.easyButton( 'fa-walking', function(){ routingcontrol.getRouter().options.profile = 'mapbox/walking'; routingcontrol.route(); }).addTo(landkarte); L.easyButton( 'fa-bicycle', function(){ routingcontrol.getRouter().options.profile = 'mapbox/cycling'; routingcontrol.route(); }).addTo(landkarte); L.easyButton( 'fa-car', function(){ routingcontrol.getRouter().options.profile = 'mapbox/driving-traffic'; routingcontrol.route(); }).addTo(landkarte); </script> </body> </html>
Zu beachtende Voraussetzungen
An einigen Stellen zeigen sich Klippen, die man beachten sollte: für bestimmte Dienste muss man sich vorher registrieren.
Ähnlich wie bei der Nutzung der Landkarten-API bei Google muss man sich bei MapBox ebenfalls registrieren lassen, bevor man mittels der API auf die Dienste des Geoportals zugreifen kann. MapBox bietet neben zu bezahlenden Diensten auch kostenlosen Zugriff auf die Funktionen seines Portals an, der jedoch nur eingeschränkte Möglichkeiten bietet: 100.000 Zugriffe/Monat auf den Routingdienst für private Zwecke sind kostenlos. Die können übrigens schnell erreicht sein, denn schon geringe Änderungen an einer Routenanfrage können eine Vielzahl von Zugriffen auf die Geodatenbasis hervorrufen.
Wer vermeiden möchte, dass bei der Darstellung der Fahrradkarte von Thunderforest auf dem Kartenhintergrund der Schriftzug “API key required” erscheint, lässt sich auch tunlichst einen Gratisaccount nach Registrierung erstellen. Das mag einen verwundern, denn wir erinnern uns noch gut, dass noch vor einiger Zeit der Begriff OpenCycleMap gebräuchlich war – den es aber in der freien Form nicht mehr gibt. Wir sind dieser Geschichte einfach einmal nachgegangen, weil uns verwundert hat, wie aus einem ursprünglich freien Projekt plötzlich ein kommerzielles werden kann. Hinter Thunderforest steht die Firma Gravitystorm Ltd. aus England, und hinter der letztgenannten agiert Andy Allen, einer der Protagonisten hinter der Fahrradkarte. Auf Anfrage schrieb er mir folgendes:
“I set up Gravitystorm Limited when I started working as an OpenStreetMap
consultant and freelance developer in 2010. This is normal since working
through a company often makes the paperwork involved in consulting and
freelancing much easier. In the meantime, the OpenCycleMap and Transport maps that I’d already
developed continued to grow and become more popular with website and
application developers, and several companies wanted support and a
reliable business partner. So I started offering commercial services
through my company, and everything has being growing quickly since.”
Der Zugriff auf die frei zu skalierenden Icons von Font Awesome funktioniert auch nicht ohne Registrierung. Auch hier gibt es unterschiedliche Angebote – für kommerzielle Anwendungen muss man bezahlen, das freie Angebot bietet nicht die volle Palette der darzustellenden Icons.
Resumée
Mein Wunsch war schon lange, eine Web-App zum Navigieren zu programmieren. Leaflet lieferte mit seiner schlanken Programmbibliothek schon den geeigneten Rahmen, es fehlten jedoch noch die geeigneten Zutaten. Auch wenn es einige Zeit dauerte, die entsprechenden Ergänzungen, Plugins und – vor allem – Dokumentationen zu finden, hat sich der Aufwand gelohnt. Ich habe jedoch festgestellt, dass es notwendig ist, sich mit CSS und Javascript vertraut zu machen, um dies alles zu verstehen. Immerhin ist ein ganzes Jahr ins Land gegangen, bis ich die vorläufig letzte Version meiner Web-Navigationsapp vorstellen konnte. Es geht aber noch weiter…