Ricardo
Ricardo

Reputation: 195

getFeaturesAtPixel() to include decluttered (hidden) features

Is there any way to get all the features in one layer at one specific pixel, including the hidden ones due to decluttering? Currently, when calling Map.getFeaturesAtPixel() (or Map.forEachFeatureAtPixel()) those features are omitted.

Upvotes: 0

Views: 1964

Answers (2)

Ricardo
Ricardo

Reputation: 195

For the posterity. I think in most cases you don’t need the result to include the hidden features due to decluttering, because that could lead to a non-empty result where the cursor is in an empty area.

What finally did was to create an additional layer without decluttering turned on. Firstly I added there all the features without labels, and hid them simply not setting the fill style (setting the layer opacity to zero would also work). That was giving me great results when an original decluttered feature was overlapping others, but still giving false positives in empty areas.

So finally I decided to show also this new layer behind the decluttered one, with different styling and without labels. This way, visually you can see all the features and decluttered with labels are shown on top, which works perfectly fine also from an UX perspective.

Upvotes: 1

ahocevar
ahocevar

Reputation: 5648

getFeaturesAtPixel is designed to report on exactly what's rendered on the map. If you want to get all features at a specific location, you can use ol/source/Vector's getFeaturesInExtent method for a small buffer (e.g. 2 pixels) around the coordinate you're interested in:

import {boundingExtent, buffer} from 'ol/extent';

map.on('click', function(e) {
  const extent = boundingExtent([e.coordinate]);
  buffer(extent, 2 / view.getResolution());
  matches = source.getFeaturesInExtent(extent);
});

When you are working with vector tiles, you can achieve the same by first getting the tile

const tileGrid = vectorTileSource.getTileGrid();
const tileCoord = tileGrid.getTileCoordForCoordAndResolution(coordinate, view.getResolution());
const tile = vectorTileSource.getTile(tileCoord);

and then get only the features in your buffer extent:

import {intersects} from 'ol/extent';

const features = tile.getFeatures();
const matches = [];
for (let i = 0, ii = features.length; i < ii; ++i) {
  const feature = features[i];
  if (intersects(extent, feature.getGeometry().getExtent()) {
    matches.push(feature);
  }
}

Upvotes: 2

Related Questions