openlayersopenstreetmapgraphhopper

Use OpenLayers for Draw Route from GraphHpper


I'm new in use map GraphHopper, OpenStreetMap, and others library. I want to draw route which generated by GraphHopper Routing Engine (actually in Java language). Is that possible for draw route from GH using OpenLayer?
I have read this GWT-OpenLayer, but I can't find how to write a route in real road like this GraphHopper Routing Example
I hope openlayers API can cover my need

  1. GH Routing Engine generate route from A (long, lat) to B (long, lat)
  2. OpenLayers GWT draw every route from GH

Any help will appreciate :)


Solution

  • The GraphHopper routing API is very similar to many other routing APIs, it can be fetched directly from a JavaScript application such as OpenLayers and the result contains a polyline as used in this example https://openlayers.org/en/main/examples/feature-move-animation.html (but the polyline can contain elevations and has a standard ie5 scale factor). Changing the OpenLayers example to use the route in your GraphHopper example (I copied the API key but for production you should get your own) the result is:

    <!doctype html>
    <html lang="en">
      <head>
        <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/css/ol.css" type="text/css">
        <style>
          html, body, .map {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
          }
          #map {
            position: relative;
          }
          #controls {
            z-index: 1;
            position: absolute;
            bottom: 0;
            left: 0;
         }
        </style>
        <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.5.0/build/ol.js"></script>
      </head>
      <body>
        <div id="map" class="map">
          <div id="controls">
            <label for="speed">
              speed:&nbsp;
              <input id="speed" type="range" min="10" max="999" step="10" value="60">
            </label>
            <button id="start-animation">Start Animation</button>
          </div>
        </div>
        <script type="text/javascript">
    
    const center = [-5639523.95, -3501274.52];
    const map = new ol.Map({
      target: document.getElementById('map'),
      view: new ol.View({
        center: center,
        zoom: 10,
        minZoom: 2,
        maxZoom: 19,
      }),
      layers: [
        new ol.layer.Tile({
          source: new ol.source.OSM(),
        }),
      ],
    });
    
    // The polyline string is read from a JSON similiar to those returned
    // by directions APIs such as Openrouteservice and Mapbox.
    
    const api_key = 'ab642f98-3b45-4643-981d-9b80f58239a3';
    const startLonLat = [111.352111, 0.136299];
    const endLonLat = [111.299744, -0.255431];
    
    fetch('https://graphhopper.com/api/1/route' +
      '?point=' + startLonLat.slice().reverse().join(',') +
      '&point=' + endLonLat.slice().reverse().join(',') +
      '&type=json&locale=en-US&key=' + api_key +
      '&elevation=true&profile=car'
    ).then(function (response) {
      response.json().then(function (result) {
    
        const polyline = result.paths[0].points;
    
        const route = new ol.format.Polyline({
          factor: 1e5,
          geometryLayout: 'XYZ'
        }).readGeometry(polyline, {
          dataProjection: 'EPSG:4326',
          featureProjection: 'EPSG:3857',
        });
    
        map.getView().fit(route);
    
        const routeFeature = new ol.Feature({
          type: 'route',
          geometry: route,
        });
        const geoMarker = new ol.Feature({
          type: 'geoMarker',
          geometry: new ol.geom.Point(route.getCoordinateAt(0)),
        });
        const startMarker = new ol.Feature({
          type: 'icon',
          geometry: new ol.geom.Point(route.getCoordinateAt(0)),
        });
        const endMarker = new ol.Feature({
          type: 'icon',
          geometry: new ol.geom.Point(route.getCoordinateAt(1)),
        });
    
        const styles = {
          'route': new ol.style.Style({
            stroke: new ol.style.Stroke({
              width: 6,
              color: [237, 212, 0, 0.8],
            }),
          }),
          'icon': new ol.style.Style({
            image: new ol.style.Icon({
              anchor: [0.5, 1],
              src: 'https://openlayers.org/en/main/examples/data/icon.png',
            }),
          }),
          'geoMarker': new ol.style.Style({
            image: new ol.style.Circle({
              radius: 7,
              fill: new ol.style.Fill({color: 'black'}),
              stroke: new ol.style.Stroke({
                color: 'white',
                width: 2,
              }),
            }),
          }),
        };
    
        let animating = false;
    
        const vectorLayer = new ol.layer.Vector({
          source: new ol.source.Vector({
            features: [routeFeature, geoMarker, startMarker, endMarker],
          }),
          style: function (feature) {
            // hide geoMarker if animation is active
            if (animating && feature.get('type') === 'geoMarker') {
              return null;
            }
            return styles[feature.get('type')];
          },
        });
    
        map.addLayer(vectorLayer);
    
        let speed, startTime;
        const speedInput = document.getElementById('speed');
        const startButton = document.getElementById('start-animation');
    
        function moveFeature(event) {
          const vectorContext = ol.render.getVectorContext(event);
          const frameState = event.frameState;
    
          if (animating) {
            const elapsedTime = frameState.time - startTime;
            const distance = (speed * elapsedTime) / 1e6;
    
            if (distance >= 1) {
              stopAnimation(true);
              return;
            }
    
            const currentPoint = new ol.geom.Point(route.getCoordinateAt(distance));
            const feature = new ol.Feature(currentPoint);
            vectorContext.drawFeature(feature, styles.geoMarker);
          }
          // tell OpenLayers to continue the postrender animation
          map.render();
        }
    
        function startAnimation() {
          if (animating) {
            stopAnimation(false);
          } else {
            animating = true;
            startTime = new Date().getTime();
            speed = speedInput.value;
            startButton.textContent = 'Cancel Animation';
            // hide geoMarker
            geoMarker.changed();
            // just in case you pan somewhere else
            map.getView().fit(route);
            vectorLayer.on('postrender', moveFeature);
            map.render();
          }
        }
    
        function stopAnimation(ended) {
          animating = false;
          startButton.textContent = 'Start Animation';
    
          // if animation cancelled set the marker at the beginning
          const coord = route.getCoordinateAt(ended ? 1 : 0);
          geoMarker.getGeometry().setCoordinates(coord);
          // remove listener
          vectorLayer.un('postrender', moveFeature);
        }
    
        startButton.addEventListener('click', startAnimation, false);
      });
    });
    
        </script>
      </body>
    </html>