3
votes

How can one exampt a marker with open popup from collapsing into a cluster when zooming out?

I am using leaflet and markercluster as set up in this example:

HTML:

<div id="map"></div>

CSS:

html,
body {
  height: 100%;
}

#map {
  height: 100%;
}

JS:

const map = L.map('map', {
    zoom: 5,
    center: [0,0],
    maxZoom: 18
});
const clustered = L.markerClusterGroup();
map.addLayer(clustered);

const m1 = L.marker(L.latLng(0,0));
m1.addTo(clustered);
m1.bindPopup('one');

const m2 = L.marker(L.latLng(0,1));
m2.addTo(clustered);
m2.bindPopup('two');

const m3 = L.marker(L.latLng(1,0));
m3.addTo(clustered);
m3.bindPopup('three');

I would like to temporarily exempt a marker from collapsing into a cluster as long as its popup is open. For the example, this would mean:

  1. Zoom in until you see the individual markers.

  2. Click one to open a popup.

  3. Zoom out again.

The “popped up” marker should be visible, together with the open popup. The remaining markers should collapse.

  1. When the popup is closed, the marker should disappear into the cluster.

I've tried to temporarily move the marker to the map (and back) on popupopen (and popupclose), but this does not work:

map.on('popupopen', function(e) {
    const m = e.popup._source;
    clustered.removeLayer(m);
    map.addLayer(m);
});
map.on('popupclose', function(e) {
    let m = e.popup._source;
    map.removeLayer(m);
    clustered.addLayer(m);
});

Any ideas?

3

3 Answers

1
votes

Now this seems to be working. I had to add a separate layer unclustered, and handle popupopen only in the clustering layer, and popupclose only in the unclustered layer

const unclustered = L.markerClusterGroup(); // NOTE
map.addLayer(unclustered);
clustered.on('popupopen', function(e) {
    console.log('open');
    const m = e.popup._source;
    clustered.removeLayer(m);
    unclustered.addLayer(m);
    m.openPopup();
});
unclustered.on('popupclose', function(e) {
    console.log('close');
    let m = e.popup._source;
    unclustered.removeLayer(m);
    clustered.addLayer(m);
    m.closePopup();
});

NOTE: I'm not happy with using L.markerClusterGroup for the unclustered layer. But I would not know what else. As long as there's only one marker in that layer, it will work. But to exempt multiple markers from collapsing into a cluster, a different layer must be used. Which one? L.layerGroup does not work.

1
votes

The immediate reason why your first attempt does not work, is that when you try removing your Marker m from your clustered MarkerClusterGroup (MCG) (clustered.removeLayer(m)), it closes the Marker popup, hence fires the map "popupclose" event, which in turn re-adds your Marker into your MCG.

This easily explains why your 2nd attempt (in your answer) works: you now listen to the "popupclose" event on another MCG instead of on the map, therefore when your remove the Marker from the initial MCG, it does not fire the event on that other MCG.

The fact that replacing the other MCG by a Layer Group does not work is simply because the latter does not handle event delegation, on the contrary of a Feature Group. Therefore you could simply use a Feature Group as your other Group, in order to make sure your Markers will never be able to cluster.

Updated JSFiddle: https://jsfiddle.net/sghL4z96/69/

0
votes

This is by no means comprehensive, but you should call clustered.refreshClusters() at the end of each event handler. This will inform the cluster to update given all the markers it has in it's layer. There's some errors being thrown in your fiddle which still need some resolution, but this should get you closer to your goal.

e.g.

map.on('popupopen', function(e) {
  const m = e.popup._source;
  clustered.removeLayer(m);
  map.addLayer(m);

  // update the cluster now that layer data has changed
  clustered.refreshClusters();
});