Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect user-initiated pan/zoom operations on Leaflet

I have a Leaflet map which is used for location-sharing. When a user shares their location, a marker displaying their location is added to the map for all other users to see. It auto-fits the map to display all the markers whenever one is added, moved or deleted. I've also added a custom control that can toggle the auto-fit behavior on and off. This all works fine, but I would also like to make the map smart enough to automatically turn off the auto-fit behavior if the user pans or zooms the map.

This turns out to be pretty difficult because I can't find a good way to distinguish whether or not a pan/zoom operation is initiated by the user or by an auto-fit. I was initially listening for the panstart and zoomstart events, but these are also triggered by the auto-fit. I figured that I could set a flag to tell it not to turn off auto-fit when the zoom/pan is caused by auto-fitting. I check this flag first before turning off auto-fit in response to panstart and zoomstart, then clear it when panend and zoomend are received.

This appears to work fine until an auto-fit occurs which does not result in a pan or zoom. Let's suppose that we have a large auto-fitted cluster of markers, and one of the ones in the middle is removed. Since the bound box is unchanged, no pan or zoom is triggered, and therefore the flag telling it not to turn off auto-fit never gets cleared. The next time the user pans or zooms the map, it doesn't turn off auto-fit like it should because it thinks it's still in the middle of an auto-fit operation.

How do I make it so that I can reliably turn off auto-fitting when the user pans or zooms the map directly, but leave it on when it is panned or zoomed by other means?

Here is the relevant code:

var markers = [];        // Map markers are stored in this array.
var autoFit = true;      // Whether auto-fit is turned on
var lockAutoFit = false; // Temporarily lock auto-fit if true
var map;                 // Leaflet map object

function initMap() {
    // Leaflet map initialized here
    map.on('movestart zoomstart', function() {
        if (!lockAutoFit) {
            autoFit = false;
        }
    });
    map.on('moveend zoomend', function() {
        lockAutoFit = false;
    });
}

function toggleAutoFit() {
    autoFit = !autoFit;

    if (autoFit) {
        lockAutoFit = true;
        fitMap();
    }
}

function addOrUpdateMarker(marker, coords) {
    lockAutoFit = true;
    // do the marker update here
    fitMap();
}

function removeMarker(marker) {
    lockAutoFit = true;
    // remove the marker here
    fitMap();
}

// Pans and zooms the map so that all markers fit in the map view.
// Invoked whenever a marker is added, moved or deleted, or when
// the user turns on auto-fit.
function fitMap() {
  if (!autoFit || !markers.length) {
    return;
  }

  map.fitBounds(new L.featureGroup(markers).getBounds());
}
like image 755
Robert J. Walker Avatar asked Aug 13 '15 15:08

Robert J. Walker


1 Answers

I ended up setting a flag around my fitBounds and setView calls e.g.:

isProgramaticZoom = true
map.fitBounds(new L.featureGroup(markers).getBounds());
isProgramaticZoom = false

Then the code to turn off auto-fit:

map.on('zoomstart', function() {
  if (!isProgramaticZoom) {
    //turn off auto-fit
  }
})

map.on('dragstart', function() {
  //turn off auto-fit
})

Unfortunately, still not ideal but should do the trick

like image 121
Heinrich Filter Avatar answered Sep 17 '22 11:09

Heinrich Filter