Making polyline snap to roads in leaflet

I am loading markers from a database and then drawing a polyline between markers. I am using the polyline to calculate overall distance instead of having to calculate the distance from marker-a to marker-b to marker-c and so on.

My distance is however inaccurate because if two markers are around a curved road, the polyline just connects them instead of drawing it along the road.

I know this is possible in Google Maps API but the usage restrictions would not suit me which is why I decided to use leaflet.

My markers are not so far apart, because my GPS device sends location every 10 seconds.

I found the leaflet-routing-machine plugin and I was wondering if I can use this to make my polyline snap to the road?

This is how I am adding markers to my map:

function getlocationsfromdb(){
      group.clearLayers();
      latlngArray.length=0;
      var deviceid = $("#selectid").val();

        $.ajax({
            type: "POST",
            url: "functionhandlers/getlocations.php",
            data: {deviceid:deviceid,start:start,end:end},
            dataType: 'json',
            cache: false,
        })
        .success(function(response) {   
            $('input').removeClass('error').next('.errormessage').html('');
            if(!response.errors && response.result) {

                $.each(response.result, function( index, value) {
                    var latlng = L.latLng(value[7], value[8]);
                    var marker = L.circleMarker(latlng,{radius:2}).addTo(group);    
                    latlngArray.push(latlng);   

               });
                  var polyline = L.polyline(latlngArray, {color: '#605ca8'}).addTo(group);  
                  map.fitBounds(group.getBounds());
                  var distancetravelled=polyline.measuredDistance();
                  $("#distancetravelled").html(distancetravelled);


            } else {
                $.each(response.errors, function( index, value) {
                    // add error classes
                    $('input[name*='+index+']').addClass('error').after('<div class="errormessage">'+value+'</div>')
                });
            }
        }); 
}

Can someone please point me in the right direction?

Answers:

Answer

This can be done rather easily with leaflet-routing-machine. You can just set the waypoints to your latlngArray when you initialize the routing control:

var control = L.Routing.control({
  waypoints: latlngArray,
  show: false,
  waypointMode: 'snap',
  createMarker: function() {}
}).addTo(map);

Here, show: false keeps the control from displaying on the map, and the empty createMarker function overrides the default markers that routing machine creates, instead doing nothing (though the markers would be removed when we remove the control, this just keeps them from flashing on the screen when a route is found).

You can extract all the vertices of the routing machine results by listening for the routeselected event, which will return an IRoute object that contains all the directions and geometries for the route. Placing the .route.coordinates in a new L.polyline object will keep the route around, so we can then just get rid of the routing control:

control.on('routeselected', function(e) {
  L.polyline(e.route.coordinates).addTo(group);
  map.removeControl(control);
});

Placing the above code blocks within your .success callback function right after you populates your latlngArray should give you the route you want. Here's a fiddle showing this at work:

http://fiddle.jshell.net/nathansnider/ygktexbj/

Also, if you're not using the routing control for anything else and want to keep it from showing up entirely (a small white control box may still appear while the route is being calculated), you can simply hide it in CSS:

.leaflet-routing-container {
  display:none;
}
Answer

I realize that this solution is a bit roundabout, since it creates a control, and then half the code is just preventing that control from showing up on the map

You actually don't have to add it to the map. Furthermore you can attack inner router's route function directly.

var routingControl = L.Routing.control({
  waypointMode: 'snap'
});

then

routingControl._router.route(latLngCoordinates, function(err, waypoints) {
  var a = waypoints;
});

Be careful, it's raw copy/paste: - waypoints fits internal format (inspect it) - latLngCoordinates must be in {lat:, lng:} format - it might require some cleaning because the url generated encapsulate some very long "hints" data.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.