openlayers icon indicating copy to clipboard operation
openlayers copied to clipboard

Select Interaction doesn't work well on Cluster Source

Open ger-benjamin opened this issue 3 years ago • 4 comments

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

  1. Create a cluster layer
  2. Add some points feature in it.
  3. Add a Select interaction on it (no config needed).

To Reproduce (1st way)

  1. Zoom-in to see clusters feature without aggregation
  2. Select one point
  3. Zoom-out to aggregate this cluster feature with another one.
  4. Zoom-in again to see points without aggregation => feature has changed.
  5. 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)

  1. Zoom-in to see clusters feature without aggregation
  2. Select one point
  3. 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
  4. 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.

ger-benjamin avatar May 10 '22 14:05 ger-benjamin

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.

jeromeBor avatar May 19 '22 12:05 jeromeBor

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.

ahocevar avatar May 19 '22 12:05 ahocevar

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.

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 avatar May 19 '22 12:05 jeromeBor

@jeromeBor Then I think your case is different. Would be good to provide the code with context that makes you run into your issue.

ahocevar avatar May 19 '22 13:05 ahocevar

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:

  1. Restoring the style of a feature once it's not in a cluster anymore
  2. Doing Feature.setStyle() on all features of a cluster in createCluster causes an infinite loop (Cluster.refresh -> createCluster -> Feature.setStyle -> change event 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.busy flag to avoid reentering Cluster.clear or Cluster.refresh during 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.

erwinkn avatar Jan 01 '23 19:01 erwinkn

I recommend using layer styles instead of feature styles. Like almost everywhere else, it also makes the use cases described here easier.

ahocevar avatar Jan 02 '23 13:01 ahocevar