Merge pull request #11995 from MoonE/vectorlayer-hitdetect-rounding
Fix hitdetect inaccuracy for VectorLayer's getFeatures
This commit is contained in:
@@ -6,6 +6,7 @@ import CanvasImmediateRenderer from './Immediate.js';
|
|||||||
import GeometryType from '../../geom/GeometryType.js';
|
import GeometryType from '../../geom/GeometryType.js';
|
||||||
import IconAnchorUnits from '../../style/IconAnchorUnits.js';
|
import IconAnchorUnits from '../../style/IconAnchorUnits.js';
|
||||||
import {Icon} from '../../style.js';
|
import {Icon} from '../../style.js';
|
||||||
|
import {clamp} from '../../math.js';
|
||||||
import {createCanvasContext2D} from '../../dom.js';
|
import {createCanvasContext2D} from '../../dom.js';
|
||||||
import {intersects} from '../../extent.js';
|
import {intersects} from '../../extent.js';
|
||||||
import {numberSafeCompareFunction} from '../../array.js';
|
import {numberSafeCompareFunction} from '../../array.js';
|
||||||
@@ -162,8 +163,13 @@ export function createHitDetectionImageData(
|
|||||||
export function hitDetect(pixel, features, imageData) {
|
export function hitDetect(pixel, features, imageData) {
|
||||||
const resultFeatures = [];
|
const resultFeatures = [];
|
||||||
if (imageData) {
|
if (imageData) {
|
||||||
|
// The pixel coordinate is clamped down to the hit-detect canvas' size to account
|
||||||
|
// for browsers returning coordinates slightly larger than the actual canvas size
|
||||||
|
// due to a non-integer pixel ratio.
|
||||||
const index =
|
const index =
|
||||||
(Math.round(pixel[0] / 2) + Math.round(pixel[1] / 2) * imageData.width) *
|
(clamp(Math.floor(Math.round(pixel[0]) / 2), 0, imageData.width - 1) +
|
||||||
|
clamp(Math.floor(Math.round(pixel[1]) / 2), 0, imageData.height - 1) *
|
||||||
|
imageData.width) *
|
||||||
4;
|
4;
|
||||||
const r = imageData.data[index];
|
const r = imageData.data[index];
|
||||||
const g = imageData.data[index + 1];
|
const g = imageData.data[index + 1];
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import Feature from '../../../../../src/ol/Feature.js';
|
|||||||
import Point from '../../../../../src/ol/geom/Point.js';
|
import Point from '../../../../../src/ol/geom/Point.js';
|
||||||
import {Style} from '../../../../../src/ol/style.js';
|
import {Style} from '../../../../../src/ol/style.js';
|
||||||
import {create} from '../../../../../src/ol/transform.js';
|
import {create} from '../../../../../src/ol/transform.js';
|
||||||
import {createHitDetectionImageData} from '../../../../../src/ol/render/canvas/hitdetect.js';
|
import {createCanvasContext2D} from '../../../../../src/ol/dom.js';
|
||||||
|
import {
|
||||||
|
createHitDetectionImageData,
|
||||||
|
hitDetect,
|
||||||
|
} from '../../../../../src/ol/render/canvas/hitdetect.js';
|
||||||
|
|
||||||
describe('hitdetect', function () {
|
describe('hitdetect', function () {
|
||||||
let features, styleFunction;
|
let features, styleFunction;
|
||||||
@@ -40,4 +44,53 @@ describe('hitdetect', function () {
|
|||||||
252,
|
252,
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
it('detects hit at the correct position', function () {
|
||||||
|
const context = createCanvasContext2D(3, 3);
|
||||||
|
context.fillStyle = '#ffffff';
|
||||||
|
context.fillRect(1, 1, 1, 1);
|
||||||
|
const features = [new Feature()];
|
||||||
|
const imageData = context.getImageData(0, 0, 3, 3);
|
||||||
|
expect(hitDetect([2, 2], features, imageData)).to.have.length(1);
|
||||||
|
expect(hitDetect([2, 3], features, imageData)).to.have.length(1);
|
||||||
|
expect(hitDetect([3, 2], features, imageData)).to.have.length(1);
|
||||||
|
expect(hitDetect([3, 3], features, imageData)).to.have.length(1);
|
||||||
|
|
||||||
|
expect(hitDetect([1.5, 1.5], features, imageData)).to.have.length(1);
|
||||||
|
expect(hitDetect([3.4, 3.4], features, imageData)).to.have.length(1);
|
||||||
|
|
||||||
|
expect(hitDetect([1.4, 1], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([1, 2.4], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([2.4, 1], features, imageData)).to.have.length(0);
|
||||||
|
|
||||||
|
expect(hitDetect([3.5, 4.5], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([5, 4], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([4.5, 5], features, imageData)).to.have.length(0);
|
||||||
|
|
||||||
|
expect(hitDetect([1.4, 3.5], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([1, 4.5], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([1.5, 5], features, imageData)).to.have.length(0);
|
||||||
|
});
|
||||||
|
it('correctly detects hit for pixel exceeding canvas dimension', function () {
|
||||||
|
const features = [new Feature()];
|
||||||
|
const context = createCanvasContext2D(2, 2);
|
||||||
|
context.fillStyle = '#ffffff';
|
||||||
|
|
||||||
|
context.fillRect(1, 1, 1, 1);
|
||||||
|
let imageData = context.getImageData(0, 0, 2, 2);
|
||||||
|
expect(hitDetect([4, 2], features, imageData)).to.have.length(1);
|
||||||
|
expect(hitDetect([2, 4], features, imageData)).to.have.length(1);
|
||||||
|
|
||||||
|
expect(hitDetect([-2, 4], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([4, -2], features, imageData)).to.have.length(0);
|
||||||
|
|
||||||
|
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
|
||||||
|
|
||||||
|
context.fillRect(0, 0, 1, 1);
|
||||||
|
imageData = context.getImageData(0, 0, 2, 2);
|
||||||
|
expect(hitDetect([-2, 0], features, imageData)).to.have.length(1);
|
||||||
|
expect(hitDetect([0, -2], features, imageData)).to.have.length(1);
|
||||||
|
|
||||||
|
expect(hitDetect([-2, 4], features, imageData)).to.have.length(0);
|
||||||
|
expect(hitDetect([4, -2], features, imageData)).to.have.length(0);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user