Webgl points / read only one pixel for feature hit detection
Also implements `hasFeatureAtCoordinate`. `hitTolerance` is not supported for now.
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import LayerRenderer from '../Layer.js';
|
import LayerRenderer from '../Layer.js';
|
||||||
import WebGLHelper from '../../webgl/Helper.js';
|
import WebGLHelper from '../../webgl/Helper.js';
|
||||||
|
import {TRUE} from '../../functions.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,6 +82,14 @@ class WebGLLayerRenderer extends LayerRenderer {
|
|||||||
getShaderCompileErrors() {
|
getShaderCompileErrors() {
|
||||||
return this.helper.getShaderCompileErrors();
|
return this.helper.getShaderCompileErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
hasFeatureAtCoordinate(coordinate, frameState) {
|
||||||
|
const feature = this.forEachFeatureAtCoordinate(coordinate, frameState, 0, TRUE, null);
|
||||||
|
return feature !== undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,7 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.renderHitDetection(frameState);
|
this.renderHitDetection(frameState);
|
||||||
|
this.hitRenderTarget_.clearCachedData();
|
||||||
|
|
||||||
return canvas;
|
return canvas;
|
||||||
}
|
}
|
||||||
@@ -533,17 +534,14 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {
|
||||||
const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice(0, 2));
|
const pixel = applyTransform(frameState.coordinateToPixelTransform, coordinate.slice());
|
||||||
const width = frameState.size[0];
|
|
||||||
const height = frameState.size[1];
|
|
||||||
|
|
||||||
const data = this.hitRenderTarget_.read();
|
const data = this.hitRenderTarget_.readPixel(pixel[0], pixel[1]);
|
||||||
const index = Math.floor(pixel[0]) + (height - Math.floor(pixel[1])) * width;
|
|
||||||
const color = [
|
const color = [
|
||||||
data[index * 4] / 255,
|
data[0] / 255,
|
||||||
data[index * 4 + 1] / 255,
|
data[1] / 255,
|
||||||
data[index * 4 + 2] / 255,
|
data[2] / 255,
|
||||||
data[index * 4 + 3] / 255
|
data[3] / 255
|
||||||
];
|
];
|
||||||
const uid = colorDecodeId(color).toString();
|
const uid = colorDecodeId(color).toString();
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ class WebGLRenderTarget {
|
|||||||
* @param {number} x Pixel coordinate
|
* @param {number} x Pixel coordinate
|
||||||
* @param {number} y Pixel coordinate
|
* @param {number} y Pixel coordinate
|
||||||
* @returns {Uint8Array} Integer array with one color value (4 components)
|
* @returns {Uint8Array} Integer array with one color value (4 components)
|
||||||
|
* @api
|
||||||
*/
|
*/
|
||||||
readPixel(x, y) {
|
readPixel(x, y) {
|
||||||
this.readAll();
|
this.readAll();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/Point
|
|||||||
import {get as getProjection} from '../../../../../src/ol/proj.js';
|
import {get as getProjection} from '../../../../../src/ol/proj.js';
|
||||||
import ViewHint from '../../../../../src/ol/ViewHint.js';
|
import ViewHint from '../../../../../src/ol/ViewHint.js';
|
||||||
import {POINT_VERTEX_STRIDE, WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.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() {
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user