OpenLayers 对 MVT 层悬停选择的不一致命中检测

OpenLayers Inconsistent Hit Detection on MVT Layer Hover Selection

objective

我正在尝试从 OpenLayers 站点复制 this example 中的“单选悬停”功能。

问题

当我尝试使用此实现时,vtLayer.getFeatures(event.pixel) 的命中检测非常差。 documentation for the function 状态:

The hit detection algorithm used for this method is optimized for performance, but is less accurate than the one used in map.getFeaturesAtPixel()

确实,当我切换到 map.getFeaturesAtPixel 时,性能提高了,但功能仍然没有完全按预期工作。

当我将指针从外部移到矢量边界上时,它(通常)会按预期运行:

但是,当我移动到相邻边界然后返回时,该功能不再有效:

我的代码:

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

到目前为止我尝试了什么

我试过调整 VectorTile 层中的 renderBufferrenderMode 参数,以及调整 map.forEachFeatureAtPixel 中的 hitTolerance 选项,但我没有还没有运气。当我从 forEachFeatureAtPixel 的特征参数中记录特征 ID 时,我注意到一些奇怪的事情——当意外行为发生时,将指针拖动到相邻的边界线上后,所选变量在两个特征之间快速切换并分配不需要的一个,直到我将指针触摸到不相邻的边界线。修改 hitTolerance 只会导致所选功能更频繁地切换。

理论与问题

我在想,也许我的相邻向量彼此重叠了边界?或者向量作为 MVT 加载的方式可能存在问题?

向 unselectedStyle 添加一个不可见的 Fill() 允许图层被命中检测并解决了我的问题!

// 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)',
  }),
});

谢谢迈克!