0

Is there a way to constrain a marker so that when it is dragged, it moves along a given polyline in Leaflet JS?

In my page I have route that is presented as a Polyline and a Marker. I want to be able to drag the marker along the line to be able to display information about that part of the route.

Looking at the documentation here, it is not clear how I would go about that:

https://leafletjs.com/reference-1.6.0.html#marker

Note, I am using leaflet by means of vue2leaflet, though a solution that works for standard leaflet JS would be okay.

The vue 'template':

      <l-map
        class="leaftlet-map"
        :zoom="map.zoom"
        :center="map.center"
        @update:zoom="zoomUpdated"
        @update:center="centerUpdated"
        @update:bounds="boundsUpdated"
      >
        <l-tile-layer :url="map.url" :attribution="map.attribution" />
        <l-marker
          v-if="positionMarker"
          :lat-lng="positionMarker.center"
          draggable
          @move="markerMoved"
        />
        <l-polyline
          v-if="polyline.latlngs && polyline.latlngs.length > 0"
          :lat-lngs="polyline.latlngs"
          :color="polyline.color"
        />
      </l-map>
  • 2
  • The one issue I see with Leaflet.Snap is that it allows 'unsnapping' which isn't ideal. I did see Leaflet.AnimatedMarker, but that looks unmaintained. I'll take a look at Leaflet.GeometryUtil and see if there is anything there that could help. – Andre M Feb 10 at 14:31
  • What about adjusting snapDistance option? – TomazicM Feb 10 at 14:49
0

This is a quick hack just to test the concept. Since img element is draggable by itself, L.divIcon is used for draggable marker so that draggable="false" attribute could be set. For keeping marker glued to polyline, polyline method closestLayerPoint is used to locate closest point to mouse position on polyline.

CSS:

html, body {
  height: 100%;
  padding: 0;
  margin: 0;
}
#map {
  width: 100%;
  height: 100%;
}
.leaflet-div-icon {
  border: none;
  background-color: transparent;
}

HTML:

<div id="map"></div>
<div style="display:none;">
  <img id="icon" draggable="false" src="lib/leaflet/1.6/images/marker-icon.png">
</div>

JS:

var osm = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: 'Map data &copy; OpenStreetMap contributors',
});

var map = L.map('map').addLayer(osm);

var path = L.polyline([[43, 1],[43, 1.5],[43.5, 2],[44, 3],[43.5, 4]], {
    color: 'red',
    opacity: 1.0
}).addTo(map);

map.fitBounds(path.getBounds());

var myIcon = L.divIcon({
  html: document.getElementById('icon'),
  iconSize: [25, 41],
  iconAnchor: [12.5, 41],
});

var myMarker = L.marker([43, 1], {icon: myIcon}).addTo(map);

myMarker.on({
  mousedown: function () {
    map.on('mousemove', function (e) {
      map.dragging.disable();
      var point = path.closestLayerPoint(map.latLngToLayerPoint(e.latlng))
      myMarker.setLatLng(map.layerPointToLatLng(point));
    });
  }
}); 
map.on('mouseup',function(e){
  map.removeEventListener('mousemove');
  map.dragging.enable();
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.