Merge pull request #10301 from ahocevar/getfeatures-fixes

Create hit detection data per layer and without requestAnimationFrame
This commit is contained in:
Andreas Hocevar
2019-11-29 09:44:09 +01:00
committed by GitHub
4 changed files with 74 additions and 54 deletions
+2 -2
View File
@@ -61,9 +61,9 @@ class VectorRenderTile extends Tile {
this.errorSourceTileKeys = {}; this.errorSourceTileKeys = {};
/** /**
* @type {ImageData} * @type {Object<number, ImageData>}
*/ */
this.hitDetectionImageData = null; this.hitDetectionImageData = {};
/** /**
* @private * @private
+36 -40
View File
@@ -232,49 +232,45 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
getFeatures(pixel) { getFeatures(pixel) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
if (!this.hitDetectionImageData_ && !this.animatingOrInteracting_) { if (!this.hitDetectionImageData_ && !this.animatingOrInteracting_) {
requestAnimationFrame(function() { const size = [this.context.canvas.width, this.context.canvas.height];
const size = [this.context.canvas.width, this.context.canvas.height]; apply(this.pixelTransform, size);
apply(this.pixelTransform, size); const center = this.renderedCenter_;
const center = this.renderedCenter_; const resolution = this.renderedResolution_;
const resolution = this.renderedResolution_; const rotation = this.renderedRotation_;
const rotation = this.renderedRotation_; const projection = this.renderedProjection_;
const projection = this.renderedProjection_; const extent = this.renderedExtent_;
const extent = this.renderedExtent_; const layer = this.getLayer();
const layer = this.getLayer(); const transforms = [];
const transforms = []; const width = size[0] / 2;
const width = size[0] / 2; const height = size[1] / 2;
const height = size[1] / 2; transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, 0).slice());
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, 0).slice()); const source = layer.getSource();
const source = layer.getSource(); const projectionExtent = projection.getExtent();
const projectionExtent = projection.getExtent(); if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) { let startX = extent[0];
let startX = extent[0]; const worldWidth = getWidth(projectionExtent);
const worldWidth = getWidth(projectionExtent); let world = 0;
let world = 0; let offsetX;
let offsetX; while (startX < projectionExtent[0]) {
while (startX < projectionExtent[0]) { --world;
--world; offsetX = worldWidth * world;
offsetX = worldWidth * world; transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice()); startX += worldWidth;
startX += worldWidth;
}
world = 0;
startX = extent[2];
while (startX > projectionExtent[2]) {
++world;
offsetX = worldWidth * world;
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
startX -= worldWidth;
}
} }
world = 0;
startX = extent[2];
while (startX > projectionExtent[2]) {
++world;
offsetX = worldWidth * world;
transforms.push(this.getRenderTransform(center, resolution, rotation, 0.5, width, height, offsetX).slice());
startX -= worldWidth;
}
}
this.hitDetectionImageData_ = createHitDetectionImageData(size, transforms, this.hitDetectionImageData_ = createHitDetectionImageData(size, transforms,
this.renderedFeatures_, layer.getStyleFunction(), extent, resolution, rotation); this.renderedFeatures_, layer.getStyleFunction(), extent, resolution, rotation);
resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
}.bind(this));
} else {
resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
} }
resolve(hitDetect(pixel, this.renderedFeatures_, this.hitDetectionImageData_));
}.bind(this)); }.bind(this));
} }
+10 -11
View File
@@ -221,7 +221,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
executorGroups[i].dispose(); executorGroups[i].dispose();
} }
} }
tile.hitDetectionImageData = null; delete tile.hitDetectionImageData[layerUid];
tile.executorGroups[layerUid] = []; tile.executorGroups[layerUid] = [];
for (let t = 0, tt = sourceTiles.length; t < tt; ++t) { for (let t = 0, tt = sourceTiles.length; t < tt; ++t) {
const sourceTile = sourceTiles[t]; const sourceTile = sourceTiles[t];
@@ -341,6 +341,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
getFeatures(pixel) { getFeatures(pixel) {
return new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer()); const layer = /** @type {import("../../layer/VectorTile.js").default} */ (this.getLayer());
const layerUid = getUid(layer);
const source = layer.getSource(); const source = layer.getSource();
const projection = this.renderedProjection; const projection = this.renderedProjection;
const projectionExtent = projection.getExtent(); const projectionExtent = projection.getExtent();
@@ -377,7 +378,8 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
const features = tile.getSourceTiles().reduce(function(accumulator, sourceTile) { const features = tile.getSourceTiles().reduce(function(accumulator, sourceTile) {
return accumulator.concat(sourceTile.getFeatures()); return accumulator.concat(sourceTile.getFeatures());
}, []); }, []);
if (!tile.hitDetectionImageData) { let hitDetectionImageData = tile.hitDetectionImageData[layerUid];
if (!hitDetectionImageData && !this.animatingOrInteracting_) {
const tileSize = toSize(tileGrid.getTileSize(tileGrid.getZForResolution(resolution))); const tileSize = toSize(tileGrid.getTileSize(tileGrid.getZForResolution(resolution)));
const size = [tileSize[0] / 2, tileSize[1] / 2]; const size = [tileSize[0] / 2, tileSize[1] / 2];
const rotation = this.renderedRotation_; const rotation = this.renderedRotation_;
@@ -385,16 +387,13 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
this.getRenderTransform(tileGrid.getTileCoordCenter(tile.wrappedTileCoord), this.getRenderTransform(tileGrid.getTileCoordCenter(tile.wrappedTileCoord),
resolution, 0, 0.5, size[0], size[1], 0) resolution, 0, 0.5, size[0], size[1], 0)
]; ];
requestAnimationFrame(function() { hitDetectionImageData = createHitDetectionImageData(tileSize, transforms,
tile.hitDetectionImageData = createHitDetectionImageData(tileSize, transforms, features, layer.getStyleFunction(),
features, layer.getStyleFunction(), tileGrid.getTileCoordExtent(tile.wrappedTileCoord),
tileGrid.getTileCoordExtent(tile.wrappedTileCoord), tile.getReplayState(layer).renderedResolution, rotation);
tile.getReplayState(layer).renderedResolution, rotation); tile.hitDetectionImageData[layerUid] = hitDetectionImageData;
resolve(hitDetect(tilePixel, features, tile.hitDetectionImageData));
});
} else {
resolve(hitDetect(tilePixel, features, tile.hitDetectionImageData));
} }
resolve(hitDetect(tilePixel, features, hitDetectionImageData));
}.bind(this)); }.bind(this));
} }
+26 -1
View File
@@ -125,7 +125,32 @@ describe('ol.layer.VectorTile', function() {
layer.getFeatures(pixel).then(function(features) { layer.getFeatures(pixel).then(function(features) {
expect(features[0].get('name')).to.be('feature1'); expect(features[0].get('name')).to.be('feature1');
done(); done();
}); }).catch(done);
});
});
it('does not give false positives', function(done) {
map.once('rendercomplete', function() {
const pixel = map.getPixelFromCoordinate(fromLonLat([0, 0]));
layer.getFeatures(pixel).then(function(features) {
expect(features.length).to.be(0);
done();
}).catch(done);
});
});
it('stores separate hit detection data for each layer that uses the source', function(done) {
const layer2 = new VectorTileLayer({
source: layer.getSource()
});
map.addLayer(layer2);
map.once('rendercomplete', function() {
const pixel = map.getPixelFromCoordinate(fromLonLat([-36, 0]));
Promise.all([layer.getFeatures(pixel), layer2.getFeatures(pixel)]).then(function(result) {
const tile = layer.getSource().tileCache.get('0/0/0');
expect(Object.keys(tile.hitDetectionImageData).length).to.be(2);
done();
}).catch(done);
}); });
}); });