Merge pull request #10301 from ahocevar/getfeatures-fixes
Create hit detection data per layer and without requestAnimationFrame
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user