Leaflet / Leaflet.markercluster

Marker Clustering plugin for Leaflet
MIT License
3.98k stars 1k forks source link

Programmatic spiderfy fails if cluster already spiderfied #952

Open matburnham opened 5 years ago

matburnham commented 5 years ago

How to reproduce

  1. Expand cluster by clicking with mouse
  2. Programatically expand same cluster.
  3. Note remaining spider lines
  4. Click again to get error in error console.

What behaviour I'm expecting and which behaviour I'm seeing

I would expect the library to handle this without client code having to check cluster status beforehand. This line of suggests similar, but does not appear to work as expected.

https://github.com/Leaflet/Leaflet.markercluster/blob/93bf5408ad84b74ea42f55ccb6ca0450daa41427/src/MarkerCluster.Spiderfier.js#L18

I understand insufficient internals to debug much further, despite trying.

Minimal example reproducing the issue

See https://jsfiddle.net/j49gurdv/5/ for a minimal example.

Reference

For search purposes, this is the error raised by the minimal example if you continue to step 4.

DomUtil.js:230 Uncaught TypeError: Cannot set property '_leaflet_pos' of null
    at setPosition (DomUtil.js:230)
    at NewClass._setPos (Marker.js:290)
    at NewClass._animationUnspiderfy (MarkerCluster.Spiderfier.js:320)
    at NewClass.unspiderfy (MarkerCluster.Spiderfier.js:48)
    at NewClass._unspiderfy (MarkerCluster.Spiderfier.js:448)
    at NewClass.spiderfy (MarkerCluster.Spiderfier.js:28)
    at NewClass._zoomOrSpiderfy (MarkerClusterGroup.js:854)
    at NewClass.fire (Events.js:190)
    at NewClass.fire (MarkerClusterGroup.js:798)
    at NewClass._propagateEvent (Events.js:262)
setPosition @ DomUtil.js:230
_setPos @ Marker.js:290
_animationUnspiderfy @ MarkerCluster.Spiderfier.js:320
unspiderfy @ MarkerCluster.Spiderfier.js:48
_unspiderfy @ MarkerCluster.Spiderfier.js:448
spiderfy @ MarkerCluster.Spiderfier.js:28
_zoomOrSpiderfy @ MarkerClusterGroup.js:854
fire @ Events.js:190
fire @ MarkerClusterGroup.js:798
_propagateEvent @ Events.js:262
fire @ Events.js:199
_propagateEvent @ Events.js:262
fire @ Events.js:199
_fireDOMEvent @ Map.js:1439
_handleDOMEvent @ Map.js:1396
handler @ DomEvent.js:79
matburnham commented 5 years ago

Looks like #525 may relate, and this may be an embodiment of #892.

ghybs commented 5 years ago

Hi,

What happens in your specific example is that you progrmmatically try to spiderfy a Cluster which is at a different zoom level (18) than the one that is currently spiderfied (by user click) (13).

var addressPoints = [
  // Markers at exact same position => cluster spiderfies at current zoom
  [52.002, 0.02, "b1"],
  [52.002, 0.02, "b2"],
  [52.002, 0.02, "b3"],
];
var map = L.map('map', {center: latlng, zoom: 13, layers: [tiles]}); // <= user click siperfies cluster at zoom 13
addr['b2'].__parent.spiderfy(); // <= retrieve closest parent cluster, i.e. for zoom 18, and try to spiderfy it

By making a small utility function to retrieve the parent cluster at current zoom, you properly get rid of this issue:

// Utility to retrieve the parent cluster corresponding to current zoom
function getParentAtCurrentZoom(marker) {
  var currentZoom = map.getZoom();
  while (marker.__parent && marker.__parent._zoom >= currentZoom) {
    marker = marker.__parent;
  }
  return marker;
}

Updated JSFiddle: https://jsfiddle.net/0sfz2uc9/

Then what happens when you try to spiderfy the cluster for zoom 18, is that the parent cluster for zoom 13 tries to unspiderfy; since it is animated, the unspiderfication uses a timeout, which removes the Markers, whereas they were supposed to remain for the cluster 18 spiderfication... Then the error message you report is due to these missing Markers.

That being said, a potential improvement would be ensure the cluster to be spiderfied is visible on map.