Select Interaction doesn't work well on Cluster Source
Hello, I've a "bug" here, and my solution would be to simply update the documentation but I think that can be useful to describe the issue here.
Describe the bug If you try to listen the Select interaction on a Vector layer with a Cluster source, the behaviors of the selected/deselected object received in the event is inconsistent.
To Reproduce - setup
- Create a cluster layer
- Add some points feature in it.
- Add a Select interaction on it (no config needed).
To Reproduce (1st way)
- Zoom-in to see clusters feature without aggregation
- Select one point
- Zoom-out to aggregate this cluster feature with another one.
- Zoom-in again to see points without aggregation => feature has changed.
- click on this point => you can select it again (because this is not the same object as the previously selected one)
To Reproduce (2nd way)
- Zoom-in to see clusters feature without aggregation
- Select one point
- Set a property in one of the features of the received selected clusterFeature (like
feature.get('features')[0].set('foo', Math.random())); => feature has changed - click on this point => you can select it again (because this is not the same object as the previously selected one).
Expected behavior The selection behavior must be consistent.
Proposed solution
The problem is on the includes here: https://github.com/openlayers/openlayers/blob/v6.14.1/src/ol/interaction/Select.js#L530 we compare object, and it's not possible with Cluster feature because features are changing.
I think it's not reasonably possible to do something in the code (except maybe be able to providing a includes function as option but it would be... strange). It's more a "dead end" than an issue.
But the documentation should warn the user to avoid trying to use the Select interaction (and loose time on it). I provide a PR here https://github.com/openlayers/openlayers/pull/13668 to update the doc.
I can testify that I also have issues with select Interaction on my projet. The function below does not always trigger, for unknow reasons.
function.on(['select'], function (e) {
console.log('I got selected !')
}
The console does not always print the message, as if the function wasnt called / triggered by the click, but the feature is clearly selected and clicked.
I think we should simply document that the Select interaction does not work well with Cluster sources. Working directly with layer.getFeatures() or map.getFeaturesAtPixel() is almost always better than using a Select interaction anyway.
I think we should simply document that the Select interaction does not work well with Cluster sources. Working directly with
layer.getFeatures()ormap.getFeaturesAtPixel()is almost always better than using a Select interaction anyway.
Yes, but note that map.getFeaturesAtPixel() select ALL the feature if they are stacked to the same place which can be inconvenient in some cases (like mine)
Ps : also, I don't have any cluster feature, but stacked features. Having same issue, through
@jeromeBor Then I think your case is different. Would be good to provide the code with context that makes you run into your issue.
We also encountered difficulties while trying to make Cluster work with interactions, including Select.
What we found is that the approach of Cluster replacing the original source makes it hard to style and add interactions. We wanted to have the clusters and original features in two separate layers, which would solve all those problems.
The issue is, we wanted to hide the original features while clustered, which meant updating their style. There are two problems we couldn't solve with the official Cluster implementation:
- Restoring the style of a feature once it's not in a cluster anymore
- Doing
Feature.setStyle()on all features of a cluster increateClustercauses an infinite loop (Cluster.refresh->createCluster->Feature.setStyle->changeevent on the source ->Cluster.refresh...)
I ended up rewriting a custom version of Cluster for this purpose. It solves the two problems above by:
- Applying a style to all clustered features (default: empty style, to hide them) and keeping track of their original style
- Restoring the original style in
Cluster.clear() - Using a
Cluster.busyflag to avoid reenteringCluster.clearorCluster.refreshduring their execution
The code can be found here: https://github.com/Kayoo-asso/Topogether/blob/db006b55a3d01b1a55094d05f0668bdfae3cbc1d/components/openlayers/extensions/ClusterSource.ts
If this is of interest, I'd be happy to contribute it back to OpenLayers. Also very open to better ways of tackling this problem.
My main concern would be that when the original source has a lot of features, calling Feature.setStyle on all of them in Cluster.refresh would lead to a lot of events and impact performance. This could be solved by only changing the styles of features added & removed from clusters in Cluster.refresh.
I recommend using layer styles instead of feature styles. Like almost everywhere else, it also makes the use cases described here easier.