5

I have been trying to reconcile the coordinates returned by the matching geometry and the nodes in each leg when doing a match call with annotations=true overview=full and geometries=geojson.

I would like to understand how to align the node IDs with the lat/lon of a coordinate geometry.

My understanding is that there is a 1:1 mapping between the coordinates and the nodes in the legs.

I have tried "simplifying" the node IDs returned in the annotations by doing the following:

  1. Append the first leg annotations to your result.

  2. Repeat the following for each extra leg:

a. Trim the first two nodes from the start of the next leg annotation to append

b. Append the trimmed leg annotation to your result

Then I remove the locations returned by the waypoints from the coordinates in the geometry. After, I try to line up the simplified node IDs with the remaining coordinates. However, this method end up with a couple (and sometimes a few) extra points which we aren't able to explain or figure out how to filter out.

Anyone have a more reliable method for solving this? Appreciate all the help. Thanks!

user1077071
  • 901
  • 6
  • 16
  • 29

1 Answers1

0

To retrieve the map matched node coordinates, you could accompany the OSRM query by Overpass API queries - one per each leg - as in the jsFiddle demo:

OpenStreetMap with OSRM and Overpass responses

After the nodes are parsed from Overpass API calls, you could match their coordinates against the matching.geometry.coordinates in the Javascript code:

'use strict';

function processOsrmReply(data) {

  if (data.code !== 'Ok') {
    clearMap('OSRM error code: ' + data.code);
    return;
  }

  data.matchings.forEach(function(matching) {
    osrmGeometryGroup.addData(matching.geometry);

    matching.legs.forEach(function(leg) {
      var nodes = leg.annotation.nodes;
      sendOverpassRequest(nodes);
    });
  });

  myMap.flyToBounds(osrmGeometryGroup.getBounds());
}

function sendOsrmRequest() {
  // get the currently displayed markers
  var markers = markersGroup.getLayers();

  // create an array of string: "lng,lat" with 6 digits after comma
  var lngLats = markers.map(marker =>
    parseFloat(marker.getLatLng().lng).toFixed(6) + ',' +
    parseFloat(marker.getLatLng().lat).toFixed(6)
  );

  // create an array of radiuses, same length as lngLats array
  var radiuses = lngLats.map(lngLat => 50);

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

  console.log('Sending OSRM map matching query to the URL ' + url);

  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('OSRM error status: ' + this.status);
    }
  };
  request.send();
}

function processOverpassReply(data) {

  // get the coordinates of currently displayed markers to avoid repeated drawing
  var latLngs = overpassNodesGroup.getLayers().map(marker => marker.getLatLng());

  for (var n = 0; n < data.elements.length; n++) {
    var element = data.elements[n];

    if (element.type === 'node') {
      var nodeId = element.id;
      var latLng = L.latLng(element.lat, element.lon);

      if (latLngs.find(pos => pos.equals(latLng))) {
        console.log('The node ' + nodeId + ' is on the map already');
        continue;
      }

      L.marker(latLng)
        .bindPopup('Node id: ' + nodeId + '<br>' + latLng)
        .addTo(overpassNodesGroup);
    }
  }
}

function sendOverpassRequest(nodes) {
  var query = 'node(id:' + nodes.join(',') + '); out geom;'
  console.log('Sending Overpass API query: ' + query);

  var url = 'https://overpass-api.de/api/interpreter?data=[out:json];' + query;
  var request = new XMLHttpRequest();
  request.open('GET', url, false);
  request.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      var data = JSON.parse(this.response);
      processOverpassReply(data);
    } else {
      clearMap('Overpass error status: ' + this.status);
    }
  };
  request.send();
}

function processMapClick(ev) {
  // get the currently displayed markers
  var markers = markersGroup.getLayers();

  if (markers.length < MARKERS_MAX) {
    L.marker(ev.latlng)
      .bindPopup('User marker: ' + markers.length + '<br>' + ev.latlng)
      .addTo(markersGroup);
    return;
  }

  // get the count of currently displayed lines
  var linesCount = osrmGeometryGroup.getLayers().length;
  if (linesCount >= 1) {
    clearMap();
    return;
  }

  sendOsrmRequest();
}

function clearMap(str = '') {
  var myStatus = document.getElementById('myStatus');
  myStatus.textContent = str;

  osrmGeometryGroup.clearLayers();
  overpassNodesGroup.clearLayers();
  markersGroup.clearLayers();
}

var MARKERS_MAX = 4;
var myMap = L.map('myMap').setView([51.4661, 7.2491], 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 osrmGeometryGroup = L.geoJSON();
myMap.addLayer(osrmGeometryGroup);
var overpassNodesGroup = L.layerGroup();
myMap.addLayer(overpassNodesGroup);

var overlays = {
  'Show <b>markers</b> placed by user': markersGroup,
  'Show <b>geometry</b> returned by OSRM': osrmGeometryGroup,
  'Show <b>nodes</b> returned by Overpass': overpassNodesGroup
};

L.control.layers(null, overlays, {
  collapsed: false
}).addTo(myMap);
html,
body {
  padding: 0;
  margin: 0;
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
}

#myMap {
  flex-grow: 1;
}

#myStatus {
  text-align: center;
}
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1/dist/leaflet.min.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet@1/dist/leaflet-src.min.js"></script>

<p id="myStatus">Click the map 5x to draw the map matched nodes!</p>

<div id="myMap"></div>
Alexander Farber
  • 21,519
  • 75
  • 241
  • 416