Routing

You can use this class to perform route calculations.

Instructions

For the implementation of a form that enables route calculation with geocoding on the SmartMaps map, a div element with the ID map-wrapper is first added to the body tag of the HTML script. 

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

The map-wrapper contains another div element with the class geocoder-form. In this element the user interface for the input of the address information of the start and end point is implemented. For example, the geocoding of the start and end point in a form (ID=route-single-slot-form) can be realized by one input field each. The data entered by the user is processed dynamically and it is automatically recognized which address information is involved.
The input field of the start point gets the ID RouteStart and the input field of the end point gets the 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="76131" placeholder="RouteStart">
         <input type="text" name="RouteEnd" id="RouteEnd" value="76228" placeholder="RouteEnd">
         <button type="submit">send</button>
       </fieldset>
     </form>
   </div>

To display the SmartMaps map, the map-wrapper contains an empty div element with the ID map. In this div-element the map is loaded at runtime. For the div-element you can set a fixed height and width for the map with the style-attribute.

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

To enable routing and geocoding on the user interface, JavaScript code must be included. As in this example, this code can be included in the HTML script or in a separate .js file using a script tag. All variables and functions are implemented in the ym.ready method, because this method resolves dependencies to start the actual map application.
In order to have access to the user input, the address form route-single-slot-form is stored in the variable routeSingleSlotForm for the time being. For the route calculation of the geocoded address an object of the class ym.services.Routing() is created and stored in the variable routing.

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

The map content is controlled by the variable stationLayer, which is an object of the class ym.layerGroup.  This represents a layer group, which is used to manage the SmartMaps map and its objects by layers. Also an array variable waypoints is initialized, which is filled with the waypoints of a route.

      // Define map content.
      var stationLayer = ym.layerGroup();
      var waypoints = [];

Additionally a route layer is needed, which contains the complete logic for drawing the route on the map. This is a GeoJSON object in which the polyline and appearance for the waypoints of the route are defined.

      // Set route layer. This is the complete logic to create a route with all 
      // their stations to draw.
      routingLayer = ym.geoJson(null, {
        style: function(feature) {
          // Drawing a polyline
          if (feature.geometry.type === "LineString") {
            return {
              weight: 7,
              opacity: 0.8
            };
          }
        },
        pointToLayer: function(feature, latlng) {
          // Draw in stations with information.
          var waypointMarker = ym.circleMarker(latlng);
          waypointMarker.bindPopup(feature.properties.description);
          return waypointMarker;
        }
      });

To display the SmartMaps map, an object of the class ym.map is created. The ID of the div element in which the map is to be drawn and the desired map options (start position, start zoom level, etc.) are passed as parameters. To continue managing the map in code, the object is stored in a variable map. The previously created layer group and the defined route layer will be added to the map afterwards.

      // Define map.
      var map = ym.map("map", {
        center: ym.latLng(48.991897, 8.435568),
        zoom: 13
      });
      // Draw in route layer.
      map.addLayer(routingLayer);
      map.addLayer(stationLayer);

In the next step, the address data entered by the user is geocoded. A function sequence is defined for the status onsubmit of the variable routeSingleSlotForm. When the form is sent by the user, all contents of the map are emptied. Afterwards the input fields of the form are read out to geocode the adress data, which are contained as values.

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

To track the geocoding, an EventEmitter is used for the geoCoder class, which enables geocoding on the SmartMaps map. This enables by its on-method that a listener is added. The listener performs functions on certain events and can be used to distinguish between successful and unsuccessful geocoding. In this case the geoCoder class is already equipped with an EventEmitter and can directly access the on-method.
In case of a successful geo-coding ('success') a marker is set at the geo-coded address on the map, which shows a popup window with the address data when clicked. The marker is inserted as a layer in the SmartMaps map. Finally the geocoded address is stored in the waypoints array for route calculation. As soon as there is more than one entry in the array, the calcRoute() method is called with the waypoints array as parameter for the route calculation. The calcRoute() method is defined in the ym.services.Routing() class, which has been stored as an object in the variable routing.

      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);

        // If there is more than one result in the list with the geocoded addresses, 
        // it will use the first one.
        waypoints.push(geoJSONCoords[0]);

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

The ym.services.Routing() class also has the on method of the EventEmitter class to draw the route on the map in case of a successful route calculation ('success').

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

In case of an unsuccessful geocoding ('error') an entry is written to the console and no route calculation is performed.

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

The route calculation is now successfully integrated.

HTML document

Code example: Routing

<!DOCTYPE html>
<html>
<head lang="en">
  <meta charset="UTF-8">
  <title>Routing</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="76131" 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();

      // Define map content.
      var stationLayer = ym.layerGroup();
      var waypoints = [];
      // Set route layer. This is the complete logic to create a route with all 
      // their stations to draw.
      routingLayer = ym.geoJson(null, {
        style: function (feature) {
          // Drawing a polyline
          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;
        }
      });
      // define map
      var map = ym.map("map", {
        center: ym.latLng(48.991897, 8.435568),
        zoom: 13
      });
      // Draw in route layer.
      map.addLayer(routingLayer);
      map.addLayer(stationLayer);

      routeSingleSlotForm.onsubmit = function (e) {
        // Discard old card content.
        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);
        // If there is more than one result in the list with the geocoded addresses, 
        // it will use the first one.
        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>