SINTEF-9012 / PruneCluster

Fast and realtime marker clustering for Leaflet
MIT License
553 stars 133 forks source link

Force decluttering of markers at a certain zoom level #52

Open pmusaraj opened 9 years ago

pmusaraj commented 9 years ago

I'm coming to PruneCluster from Leaflet markerCluster, and the performance is much, much better. What I am building is a simple mobile app with about 1000 markers, and the difference in performance is very noticeable on phones/tablets.

But there is one feature of markerCluster that I can't yet find a solution to in PruneCluster: the disableClusteringAtZoom option. That is, I would like to be able to have the map disable clustering after a certain zoom level.

How would I go about doing this?

fungiboletus commented 9 years ago

Hello,

A disableClusteringAtZoom feature is not available. The easiest solution is removing the PruneCluster layer and adding the Leaflet markers directly.

It's also possible to do that in the ProcessView method, but this method is already huge so I think a separate component is better.

I will add this feature on the TODO list, I think it can be useful in some cases. But if you want something working before a long time, I suggest removing the PruneCluster layer and adding the leaflet markers directly when zoomed. It's not the fastest solution, but with 1000 markers it should be ok.

simison commented 9 years ago

:+1: I'd also need this feature already with two projects.

fungiboletus commented 9 years ago

You can also reduce the size of the grid when zoomed. I will try this approach first.

If you really want to display all the markers on the map, you might need something like this : https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet

pmusaraj commented 9 years ago

Thanks @yellowiscool for the quick feedback. Reducing the size of the grid can probably do this, that's what I was thinking at first.

My scenario is the following: I have a search tool on the map, once users click on a search result, I map.setView() to center the map to the selected marker and zoom to it. In most cases, this works fine, but in some, the selected marker is inside of a cluster with 2 or 3 markers.

Which makes me think of yet a different approach: maybe disable clusters if there are less than x markers in it (2 or 3 in my case)?

fungiboletus commented 9 years ago

Yes selecting a marker is not satisfactory in the current version. In my application when a marker is selected I remove (or filter) it from PruneCluster and add it again directly as a Leaflet object. But it's not perfect because it can hide the cluster just below.

I need to think about how to select a marker easily, even when it's inside a cluster. The selected marker should also be draggable, be able to open popups…

Disabling the cluster when it has 2 or 3 markers is a good idea, but it's not a perfect solution. If you still have a cluster with a small grid and a high zoom level, it's often because the markers are exactly at the same position or are very close. Also the current algorithm doesn't allow this without a big performance cost or a lot of changes (everything is a cluster in the current implementation).

pmusaraj commented 9 years ago

Quick update: for now, I have fixed this issue by changing the maxZoom on the map. It used be set at 19, which was a little too deep, and now it's at 17. PruneCluster respects it. Some markers that are too close, get the spiderfied treatment.

turbobuilt commented 9 years ago

+1. I'd really like to see this feature too.

valerio-bozzolan commented 9 years ago

+1 ;)

AbelVM commented 9 years ago

First of all, you may want to set zoomAnimation to false to avoid evil users clog your app. Then:

map.on('zoomend', function(){
 var oldsize = pruneCluster.Cluster.Size;
 pruneCluster.Cluster.Size = (map.getZoom()>= mymaxclusterzoom)? -1 : oldsize;
 pruneCluster.ProcessView(); // looks like it works OK without this line
})
fungiboletus commented 9 years ago

Does it work for you? You set -1 as grid size, which is a value I haven't think about when I wrote the algorithm. I quickly tried it and it doesn't work with 100 random elements.

AbelVM commented 9 years ago

You are right, old value is overwritten this way. My project uses a hardcoded value of 120 (default one), and it works fine this way:

map.on('zoomend', function(){
 pruneCluster.Cluster.Size = (map.getZoom()>= mymaxclusterzoom)? -1 : 120;
 pruneCluster.ProcessView(); // looks like it works OK without this line
})

Tried to post a more generic version of it and messed the code :grimacing:

To use a user selected size, we should store the value outside the event function:

// somewhere else
 mysize = 120;

map.on('zoomend', function(){
 pruneCluster.Cluster.Size = (map.getZoom()>= mymaxclusterzoom)? -1 : mysize;
 pruneCluster.ProcessView(); // looks like it works OK without this line
})
turbobuilt commented 9 years ago

Here's a similar workaround that works as long as there are no clusters with exactly the same latlng.

var decluster = false;
function clusterMarkers() {
    if(pruneCluster){
        pruneCluster.Cluster.Size = .0000000000001;
        pruneCluster.Cluster.Margin = .000000000001;
        pruneCluster.ProcessView();
    }
}
function declusterMarkers() {
    if(pruneCluster){
        pruneCluster.Cluster.Size = 35;
        pruneCluster.Cluster.Margin = 10;
        pruneCluster.ProcessView();
    }
}

leafletMainMap.on('zoomend', function () {
    if (leafletMainMap.getZoom() > 9) {
        if (decluster === false) {
            clusterMarkers()
            decluster = true;
        }
    }else{
        if (decluster === true) {
            declusterMarkers()
            decluster = false;
        }
    }
});
kasparsklavins commented 8 years ago

@AbelVM solution isnt suitable for large number of markers. When going from declustered to clustered views, there's a noticable lag since a lot of markers are being rendered while the switch hasnt been made yet.

TimoRuetten commented 8 years ago

Looking forward for this nice feature.

dtheb commented 7 years ago

any news on this feature?