Routenplanung

Mit Hilfe dieser Klasse können Sie Routenberechnungen durchführen.

Anleitung

Für die Implementierung eines Formulars, welches Routenberechnung mit Geokodierung auf der SmartMaps-Karte ermöglicht, wird vorerst im body-Tag des HTML-Skriptes ein div-Element mit der ID map-wrapper hinzugefügt. 

  <div id="map-wrapper">
     ...
  </div>.

Der map-wrapper beinhaltet in sich ein weiteres div-Element mit der Klasse geocoder-form. In diesem wird die Benutzeroberfläche für das Eingeben der Adressinformationen des Start- und des Endpunktes implementiert. Beispielsweise kann die Geokodierung des Start- und Endpunktes in einem Formular (ID=route-single-slot-form) durch jeweils ein Eingabefeld realisiert werden. Die vom Nutzer angegebenen Daten werden dynamisch verarbeitet und es wird automatisch erkannt, um welche Adressinformationen es sich handelt.
Das Eingabefeld des Startpunktes bekommt die ID RouteStart und das des Endpunktes die ID RouteEnd.

   <div class="geocoder-form" style="z-index:1000">
     <form id="route-single-slot-form">
       <fieldset>
      <input type="text" name="RouteStart" id="RouteStart" value="CAS-Weg 1 76131 Karlsruhe" placeholder="RouteStart">
         <input type="text" name="RouteEnd" id="RouteEnd" value="76228" placeholder="RouteEnd">
         <button type="submit">send</button>
       </fieldset>
     </form>
   </div>

Für die Anzeige der SmartMaps-Karte, beinhaltet der map-wrapper ein leeres div-Element mit der ID map.  In diesem wird zur Laufzeit die Karte geladen. Für das div-Element kann man durch das style-Attribut eine feste Höhe und Breite für die Karte festlegen.

   <div id="map" style="height: 400px; width: 500px;"></div>

Um das Routing und die Geokodierung auf der Oberfläche zu ermöglichen, ist die Einbindung von JavaScript-Code notwendig. Dieser kann, wie in diesem Beispiel, anhand eines script-Tags im HTML-Skript oder in einer separaten .js-Datei eingebunden werden. Alle Variablen und Funktionen werden implementiert in der ym.ready-Methode, da diese Abhängigkeiten auflöst, um die eigentliche Kartenanwendung zu starten.
Um auf die vom Nutzer getätigten Eingaben Zugriff zu haben, wird vorerst das Adressformular     route-single-slot-form in der Variable routeSingleSlotForm gelagert. Für die Routenberechnung der geokodierten Adresse wird ein Objekt der Klasse ym.services.Routing() erstellt und in der Variable routing gespeichert.

    ym.ready(function(modules) {
      var routeSingleSlotForm = document.getElementById('route-single-slot-form');
      var routing = new ym.services.Routing();

Der Karteninhalt wird geregelt durch die Variable stationLayer, welches ein Objekt der Klasse ym.layerGroup ist.  Diese stellt eine Layergruppe dar, welche dazu dient die SmartMaps-Karte und deren Objekte anhand von Ebenen zu verwalten. Ebenfalls wird eine Array-Variable waypoints initialisiert, welche mit den Wegpunkten einer Route gefüllt wird.

      // Karteninhalt definieren.
      var stationLayer = ym.layerGroup();
      var waypoints = [];

Zusätzlich dazu wird noch ein Routen-Layer benötigt, welcher die komplette Logik für das Einzeichnen der Route auf der Karte beinhaltet. Dabei handelt es sich um eine GeoJSON-Objekt, in der die Polylinie und das Aussehen für die Waypoints der Route definiert sind.

      // Routen-Layer festlegen. Dies ist die komplette Logik, um eine Route mit allen 
      // ihren Stationen einzuzeichnen.
      routingLayer = ym.geoJson(null, {
        style: function(feature) {
          // Polyline einzeichnen
          if (feature.geometry.type === "LineString") {
            return {
              weight: 7,
              opacity: 0.8
            };
          }
        },
        pointToLayer: function(feature, latlng) {
          // Stationen mit Informationen einzeichnen.
          var waypointMarker = ym.circleMarker(latlng);
          waypointMarker.bindPopup(feature.properties.description);
          return waypointMarker;
        }
      });

Für das Anzeigen der SmartMaps-Karte wird ein Objekt der Klasse ym.map angelegt. Als Parameter wird dabei die ID des div-Elementes übergeben, in dem die Karte eingezeichnet werden soll, und die gewünschten Karten-Optionen (Startposition, Startzoom-Level, usw.). Um die Karte weiterhin im Code zu verwalten, wird das Objekt in eine Variable map gelagert. Die zuvor angelegte Layergruppe und der definierte Routen-Layer werden im Nachhinein zur Karte hinzugefügt.

      // Karte definieren.
      var map = ym.map("map", {
        center: ym.latLng(48.991897, 8.435568),
        zoom: 13
      });
      // Routen-Layer einzeichnen.
      map.addLayer(routingLayer);
      map.addLayer(stationLayer);

Im nächsten Schritt erfolgt nun erstmal die Geokodierung der vom Nutzer eingegeben Adressdaten. Es wird ein Funktionsablauf definiert für den Status onsubmit der Variable routeSingleSlotForm. Wird das Formular vom Nutzer losgeschickt, werden erstmal alle Inhalte der karte geleert. Danach werden die Input-Felder des Formulars ausgelesen, um anhand der Adressdaten, welche diese als Werte in sich tragen, zu geokodieren.

      routeSingleSlotForm.onsubmit = function(e) {
        // Alten Karteninhalt verwerfen.
        waypoints = [];
        if (routingLayer) {
          routingLayer.clearLayers();
        }
        if (stationLayer) {
          stationLayer.clearLayers();
        }
        e.preventDefault();
        modules.geocodeString(document.getElementById('RouteStart').value);
        modules.geocodeString(document.getElementById('RouteEnd').value);
      };

Um die Geokodierung zu verfolgen, nutzt man für die geoCoder-Klasse, welche die Geokodierung auf der SmartMaps-Karte ermöglicht, einen EventEmitter. Dieser ermöglicht durch seine on-Methode, dass ein Listener hinzugefügt wird. Der Listener führt Funktionen bei bestimmten Events durch und kann somit genutzt werden um zwischen einer erfolgreichen und nicht erfolgreichen Geokodierung zu unterscheiden. In dem Falle ist die geoCoder-Klasse mit einen EventEmitter schon ausgestattet und kann direkt auf die on-Methode zugreifen.
Im Falle einer erfolgreichen Geokodierung ('success') wird an der geokodierten Adresse auf der Karte ein Marker gesetzt, welcher beim anklicken ein Popup-Fenster mit den Adressdaten anzeigt. Der Marker wird als Layer in die SmartMaps-Karte eingefügt. Die geokodierte Adresse wird zum Schluss für die Routenberechnung in das waypoints-Array gelagert. Sobald sich in dem Array mehr als ein Eintrag befinden, wird die calcRoute()-Methode mit dem waypoints-Array als Parameter für die Routenberechnung aufgerufen. Die calcRoute()-Methode ist in der ym.services.Routing()-Klasse definiert, welche als Objekt in der Variable routing gelagert worden ist.

      ym.services.geoCoder.on('success', function(req, res) {
        var geoJSONCoords = [];
        var geoJson = ym.geoJson(res.body, {
          onEachFeature: function(feature, layer) {
            layer.setIcon(ym.provider.Icon.Default());
            var popUpContent = "";
            if (feature.properties.street) {
              popUpContent += feature.properties.street + ", "
            }
            if (feature.properties.zip) {
              popUpContent += feature.properties.zip + " "
            }
            if (feature.properties.city) {
              popUpContent += feature.properties.city + " "
            }
            if (feature.properties.cityPart) {
              popUpContent += feature.properties.cityPart
            }
            layer.bindPopup(popUpContent);
  geoJSONCoords.push(ym.latLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]));
          }
        });
        stationLayer.addLayer(geoJson);

        // Falls mehr als ein Ergebnis in der Liste mit den geokodierten Adressen vorliegt, 
        // wird das erste verwenden.
        waypoints.push(geoJSONCoords[0]);

        if (waypoints.length > 1) {
          routing.calcRoute(waypoints);
        }
      });

Die ym.services.Routing()-Klasse besitzt ebenfalls die on-Methode der EventEmitter-Klasse, um bei einer erfolgreichen Routenberechnung ('success') die Route in die Karte einzuzeichnen.

      routing.on("success", function(request, response) {
        routingLayer.addData(response.body);
        map.fitBounds(routingLayer.getBounds());
      });

Im Falle einer nicht erfolgreichen Geokodierung ('error') wird ein Eintrag in die Konsole geschrieben und es kommt nicht zu einer Routenberechnung.

      ym.services.geoCoder.on('error', function(req, res, errorType) {
        console.log(arguments);
      });

Die Routenberechnung ist nun erfolgreich eingebunden.

HTML-Dokument

Codebeispiel: Routenplanung

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title>Routenplanung</title>
</head>
<body>
  <div id="map-wrapper">
    <div class="geocoder-form" style="z-index:1000">
      <form id="route-single-slot-form">
        <fieldset>
          <input type="text" name="RouteStart" id="RouteStart" value="CAS-Weg 1 76131 Karlsruhe" placeholder="RouteStart">
          <input type="text" name="RouteEnd" id="RouteEnd" value="76228" placeholder="RouteEnd">
          <button type="submit">send</button>
        </fieldset>
      </form>
    </div>
    <div id="map" style="width: 600px; height: 600px;"></div>
  </div>
  <!-- SmartMaps-API -->
  <script
    src="https://www.yellowmap.de/api_rst/api/loader?libraries=free-3&apiKey={API_KEY}"></script>
  <script>
    ym.ready(function (modules) {
      var routeSingleSlotForm = document.getElementById('route-single-slot-form');
      var routing = new ym.services.Routing();
      // Karteninhalt definieren.
      var stationLayer = ym.layerGroup();
      var waypoints = [];
      // Routen-Layer festlegen. Dies ist die komplette Logik, um eine Route mit allen 
      // ihren Stationen einzuzeichnen.
      routingLayer = ym.geoJson(null, {
        style: function (feature) {
          // Polyline einzeichnen
          if (feature.geometry.type === "LineString") {
            return {
              weight: 7,
              opacity: 0.8
            };
          }
        },
        pointToLayer: function (feature, latlng) {
          // Stationen mit Informationen einzeichnen.
          var waypointMarker = ym.circleMarker(latlng);
          waypointMarker.bindPopup(feature.properties.description);
          return waypointMarker;
        }
      });
      // Karte definieren.
      var map = ym.map("map", {
        center: ym.latLng(48.991897, 8.435568),
        zoom: 13
      });
      // Routen-Layer einzeichnen.
      map.addLayer(routingLayer);
      map.addLayer(stationLayer);

      routeSingleSlotForm.onsubmit = function (e) {
        // Alten Karteninhalt verwerfen.
        waypoints = [];
        if (routingLayer) {
          routingLayer.clearLayers();
        }
        if (stationLayer) {
          stationLayer.clearLayers();
        }
        e.preventDefault();
        modules.geocodeString(document.getElementById('RouteStart').value);
        modules.geocodeString(document.getElementById('RouteEnd').value);
      }
      ym.services.geoCoder.on('success', function (req, res) {
        var geoJSONCoords = [];
        var geoJson = ym.geoJson(res.body, {
          onEachFeature: function (feature, layer) {
            layer.setIcon(ym.provider.Icon.Default());
            var popUpContent = "";
            if (feature.properties.street) {
              popUpContent += feature.properties.street + ", "
            }
            if (feature.properties.zip) {
              popUpContent += feature.properties.zip + " "
            }
            if (feature.properties.city) {
              popUpContent += feature.properties.city + " "
            }
            if (feature.properties.cityPart) {
              popUpContent += feature.properties.cityPart
            }
            layer.bindPopup(popUpContent);
            geoJSONCoords.push(ym.latLng(feature.geometry.coordinates[1], feature.geometry.coordinates[0]));
          }
        });
        stationLayer.addLayer(geoJson);
        // Falls mehr als ein Ergebnis in der Liste mit den geokodierten Adressen vorliegt, 
        // wird das erste verwenden.
        waypoints.push(geoJSONCoords[0]);

        if (waypoints.length > 1) {
          routing.calcRoute(waypoints);
        }
      });
      routing.on("success", function (request, response) {
        routingLayer.addData(response.body);
        map.fitBounds(routingLayer.getBounds());
      });
      ym.services.geoCoder.on('error', function (req, res, errorType) {
        console.log(arguments);
      });
    });
  </script>
</body>
</html>