Reputation: 56
I'm trying to replicate the "singleselect-hover" feature in this example from the OpenLayers site.
When I tried to use this implementation, the hit detection was very poor with vtLayer.getFeatures(event.pixel)
. The documentation for the function states:
The hit detection algorithm used for this method is optimized for performance, but is less accurate than the one used in map.getFeaturesAtPixel()
Indeed, when I switched to map.getFeaturesAtPixel
, the performance increased, but the features still does not work entirely as expected.
When I move my pointer over a vector boundary from the outside, it (usually) behaves as expected:
However, when I move to an adjacent boundary and then back, the feature no longer works:
proj4.defs(
'EPSG:6931',
'+proj=laea +lat_0=90 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs'
);
register(proj4);
const projection = get('EPSG:6931');
const osm = new OSM();
const map = new Map({
target: something,
layers: [osm],
view: new View({
projection: projection,
zoom: 5,
}),
})
// Vector Styles
const unselectedStyle = new Style({
stroke: new Stroke({
color: 'rgba(50,50,50,0.9)',
width: 1.2,
}),
});
const selectedStyle = new Style({
stroke: new Stroke({
color: 'white',
width: 2,
}),
fill: new Fill({
color: 'rgba(0,0,0,0.3)',
}),
});
const createVtLayer = () => {
const vtSource = new VectorTileSource({
tileGrid: new TileGrid({
extent: [
-9009964.761231285, -9009964.761231285, 9009964.761231285,
9009964.761231285,
],
tileSize: 256,
resolutions: [70390.34969711941],
}),
projection: projection,
format: new MVT({ idProperty: 'some_id' }),
url:
geoserverUrl +
'/gwc/service/tms/1.0.0/' +
mvtLayerName +
'@EPSG%3A' +
projection.getCode().split(':')[1] + // EPSG number of current projection
'@pbf/{z}/{x}/{-y}.pbf',
});
return new VectorTileLayer({
zIndex: 1,
source: vtSource,
style: unselectedStyle,
});
};
const vtLayer = createVtLayer();
// Local lookup for highlighted features
let selection = null;
const selectionLayer = new VectorTileLayer({
zIndex: 2,
source: vtLayer.getSource(),
style: feature => feature === selection && selectedStyle,
});
if (map && vtLayer && selectionLayer) {
// Add layers to map once loaded into state
map.addLayer(vtLayer);
map.addLayer(selectionLayer);
// Update styling of selectionLayer on mouse hover
map.on(['pointermove', 'click'], event => {
// Send vector metadata to parent component on mouse click
if (event.type === 'click' && selection) {
onFeatureSelect(selection);
}
map.forEachFeatureAtPixel(
event.pixel,
feature => {
selection = feature;
}, {
layerFilter: layer => layer === vtLayer,
hitTolerance: 1,
}
);
selectionLayer.changed()
});
}
I've tried adjusting the renderBuffer
and renderMode
parameters in the VectorTile layer, as well as adjusting the hitTolerance
option in map.forEachFeatureAtPixel
, and I haven't had any luck yet. When I logged the feature id from the feature parameter in forEachFeatureAtPixel, I noticed something strange--when the unexpected behavior occurs, after dragging the pointer over an adjacent boundary line, the selected variable rapidly switches between the two features and assigns the undesired one, until I touch the pointer to a non-adjacent boundary line. Modifying the hitTolerance
only causes the selected feature to switch more frequently.
I'm thinking that maybe my adjacent vectors are overlapping each others boundaries? Or maybe there is a problem with the way the vectors are loaded as MVTs?
Upvotes: 1
Views: 629
Reputation: 56
Adding an invisible Fill()
to the unselectedStyle allowed the layer to be hit-detected and solved my issue!
// Vector Styles
const unselectedStyle = new Style({
stroke: new Stroke({
color: 'rgba(50,50,50,0.9)',
width: 1.2,
}),
fill: new Fill({
color: 'rgba(0,0,0,0)',
}),
});
Thanks Mike!
Upvotes: 2