简体   繁体   中英

OpenLayers Inconsistent Hit Detection on MVT Layer Hover Selection

The objective

I'm trying to replicate the "singleselect-hover" feature in this example from the OpenLayers site.

The issue

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:

意外行为

My code:

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()
  });
}

What I've tried so far

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.

控制台记录从 forEachFeatureAtPixel 参数中选择的功能和功能 ID

Theories and questions

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?

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM