Search code examples
javascriptleafletopenstreetmaposrm

Too Many Branching Routes From OSRM Match Service


I have location values for every driving trip fetched from our servers. I need to generate a single route that is matched to roads, with given locations. I’m using Leaflet Javascript Map Library, OpenStreetMap Tile Map Service and OSRM API Match Service.

I send 100 (parameter limit) locations(latitudes and longitudes) per request, then I append a list with every geometry returned as a response from OSRM Match API. Then I decode every single geometry, result is a list of lats and longs. I combine the result then draw it on a map.

P.S. Time interval between consecutive locations is 1 second.

Some of the locations are well matched to roads as exactly I want.

1

2

But most of the trip is shown on the map with a lot of branching, unrelated lines;

3

4

There are even unmatched, recurring shapes;

5

Here is the explanation of Match Service which is found in official OSRM documentation;

6

Is there a way to generate a single, properly drawn line that’s matched to roads, using these services?


Solution

  • Here a jsFiddle demo, which draws map matched response by OSRM as a blue line:

    OpenStreetMap with OSRM map matched route

    My Javascript code is below and I have even tested it with about 200 locations recorded by a driving car, after increasing the osrm-routed limit by adding the start parameter --max-matching-size 1000:

    'use strict';
    
    function processOsrmReply(data) {
    
      if (data.code !== 'Ok') {
        clearMap('Error code: ' + data.code);
        return;
      }
    
      data.matchings.forEach(function(matching) {
        matchesGroup.addData(matching.geometry);
      });
      
      myMap.flyToBounds(matchesGroup.getBounds());
    }
    
    function sendOsrmRequest(lngLats) {
      // create an array of radiuses, same length as lngLats array
      var radiuses = lngLats.map(lngLat => 49);
    
      var url = 'https://router.project-osrm.org/match/v1/driving/' +
        lngLats.join(';') +
        '?overview=simplified' +
        '&radiuses=' +
        radiuses.join(';') +
        '&generate_hints=false' +
        '&skip_waypoints=true' +
        '&gaps=ignore' +
        '&annotations=nodes' +
        '&geometries=geojson';
    
      var request = new XMLHttpRequest();
      request.open('GET', url, true);
      request.onload = function() {
        if (this.status >= 200 && this.status < 400) {
          var data = JSON.parse(this.response);
          processOsrmReply(data);
        } else {
          clearMap('Error status: ' + this.status);
        }
      };
      request.send();
    }
    
    function processMapClick(ev) {
      // get the count of currently displayed markers
      var markersCount = markersGroup.getLayers().length;
      if (markersCount < MARKERS_MAX) {
        L.marker(ev.latlng).addTo(markersGroup);
        matchesGroup.clearLayers();
        return;
      }
    
      // get the count of currently displayed matches
      var linesCount = matchesGroup.getLayers().length;
      if (linesCount >= 1) {
        clearMap();
        return;
      }
    
      // create an array of string: "lng,lat" with 6 digits after comma
      var lngLats = markersGroup.getLayers().map(marker =>
        parseFloat(marker.getLatLng().lng).toFixed(6) + ',' +
        parseFloat(marker.getLatLng().lat).toFixed(6)
      );
    
      sendOsrmRequest(lngLats);
    }
    
    function clearMap(str = '') {
      var myStatus = document.getElementById('myStatus');
      myStatus.textContent = str;
    
      matchesGroup.clearLayers();
      markersGroup.clearLayers();
    }
    
    var MARKERS_MAX = 4;
    var startPosition = [40.0005, 32.8722];
    var myMap = L.map('myMap').setView(startPosition, 14).on('click', processMapClick);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(myMap);
    
    var markersGroup = L.layerGroup();
    myMap.addLayer(markersGroup);
    var matchesGroup = L.geoJSON();
    myMap.addLayer(matchesGroup);
    html, body { 
      margin: 0;
      padding: 0;
    }
    
    #myMap {
      position: absolute;
      z-index: 1;
      width: 100%;
      height: 100%;
    }
    
    #myStatus {
      text-align: center;
      position: absolute;
      z-index: 2;
      width: 100%;
    }
    <link href="https://cdn.jsdelivr.net/npm/leaflet@1/dist/leaflet.min.css" type="text/css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/leaflet@1/dist/leaflet-src.min.js"></script>
    
    <div id="myStatus">Click 5x at the map to send OSRM map matching request</div>
    <div id="myMap"></div>