diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index eb404953b6..1f32ccf5b6 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -3,6 +3,7 @@ */ import LayerRenderer from '../Layer.js'; import WebGLHelper from '../../webgl/Helper.js'; +import {TRUE} from '../../functions.js'; /** @@ -81,6 +82,14 @@ class WebGLLayerRenderer extends LayerRenderer { getShaderCompileErrors() { return this.helper.getShaderCompileErrors(); } + + /** + * @inheritDoc + */ + hasFeatureAtCoordinate(coordinate, frameState) { + const feature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, TRUE, null); + return feature !== undefined; + } } diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index b387fa332e..bf2994136b 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -377,6 +377,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { } this.renderHitDetection(frameState); + this.hitRenderTarget_.clearCachedData(); return canvas; } @@ -533,17 +534,14 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { * @inheritDoc */ forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) { - const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice(0, 2)); - const width = frameState.size[0]; - const height = frameState.size[1]; + const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice()); - const data = this.hitRenderTarget_.read(); - const index = Math.floor(pixel[0]) + (height - Math.floor(pixel[1])) * width; + const data = this.hitRenderTarget_.readPixel(pixel[0], pixel[1]); const color = [ - data[index * 4] / 255, - data[index * 4 + 1] / 255, - data[index * 4 + 2] / 255, - data[index * 4 + 3] / 255 + data[0] / 255, + data[1] / 255, + data[2] / 255, + data[3] / 255 ]; const uid = colorDecodeId(color).toString(); diff --git a/src/ol/webgl/RenderTarget.js b/src/ol/webgl/RenderTarget.js index 7c0f6ef1f3..4500495dc1 100644 --- a/src/ol/webgl/RenderTarget.js +++ b/src/ol/webgl/RenderTarget.js @@ -118,6 +118,7 @@ class WebGLRenderTarget { * @param {number} x Pixel coordinate * @param {number} y Pixel coordinate * @returns {Uint8Array} Integer array with one color value (4 components) + * @api */ readPixel(x, y) { this.readAll(); diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js index e08f32ed8f..1f0361851a 100644 --- a/test/spec/ol/renderer/webgl/pointslayer.test.js +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -6,6 +6,7 @@ import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/Point import {get as getProjection} from '../../../../../src/ol/proj.js'; import ViewHint from '../../../../../src/ol/ViewHint.js'; import {POINT_VERTEX_STRIDE, WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.js'; +import {create as createTransform, translate as translateTransform} from '../../../../../src/ol/transform.js'; describe('ol.renderer.webgl.PointsLayer', function() { @@ -146,4 +147,69 @@ describe('ol.renderer.webgl.PointsLayer', function() { }); }); + describe('#forEachFeatureAtCoordinate and #hasFeatureAtCoordinate', function() { + let layer, renderer, feature; + + beforeEach(function() { + feature = new Feature(new Point([0, 0])); + layer = new VectorLayer({ + source: new VectorSource({ + features: [feature] + }) + }); + renderer = new WebGLPointsLayerRenderer(layer, { + sizeCallback: function() { + return 4; + } + }); + }); + + it('correctly hit detects a feature', function(done) { + const transform = translateTransform(createTransform(), 20, 20); + const projection = getProjection('EPSG:3857'); + const frameState = { + viewState: { + projection: projection, + resolution: 1, + rotation: 0, + center: [0, 0] + }, + layerStatesArray: [{}], + layerIndex: 0, + extent: [-20, -20, 20, 20], + size: [40, 40], + viewHints: [], + coordinateToPixelTransform: transform + }; + let found, hit; + const cb = function(feature) { + found = feature; + }; + + renderer.prepareFrame(frameState); + renderer.worker_.addEventListener('message', function() { + if (!renderer.hitRenderInstructions_) { + return; + } + renderer.renderFrame(frameState); + + function checkHit(x, y, expected) { + found = null; + renderer.forEachFeatureAtCoordinate([x, y], frameState, 0, cb, null); + hit = renderer.hasFeatureAtCoordinate([x, y], frameState); + expect(found).to.be(expected ? feature : null); + expect(hit).to.eql(expected); + } + + checkHit(0, 0, true); + checkHit(1, -2, true); + checkHit(-2, 1, true); + checkHit(2, 0, false); + checkHit(1, -3, false); + + done(); + }); + }); + }); + });