getFeaturesInExtent() for VectorTile source

This commit is contained in:
Andreas Hocevar
2019-12-08 17:24:18 +01:00
parent f460198850
commit bbc1de280d
2 changed files with 112 additions and 1 deletions

View File

@@ -7,7 +7,7 @@ import VectorRenderTile from '../VectorRenderTile.js';
import Tile from '../VectorTile.js';
import {toSize} from '../size.js';
import UrlTile from './UrlTile.js';
import {getKeyZXY} from '../tilecoord.js';
import {getKeyZXY, fromKey} from '../tilecoord.js';
import {createXYZ, extentFromProjection, createForProjection} from '../tilegrid.js';
import {buffer as bufferExtent, getIntersection, intersects} from '../extent.js';
import EventType from '../events/EventType.js';
@@ -170,6 +170,51 @@ class VectorTile extends UrlTile {
}
/**
* Get features whose bounding box intersects the provided extent. Only features for cached
* tiles for the last rendered zoom level are available in the source. So this method is only
* suitable for requesting tiles for extents that are currently rendered.
*
* Features are returned in random tile order and as they are included in the tiles. This means
* they can be clipped, duplicated across tiles, and simplified to the render resolution.
*
* @param {import("../extent.js").Extent} extent Extent.
* @return {Array<import("../Feature.js").FeatureLike>} Features.
* @api
*/
getFeaturesInExtent(extent) {
const features = [];
const tileCache = this.tileCache;
if (tileCache.getCount() === 0) {
return features;
}
const z = fromKey(tileCache.peekFirstKey())[0];
const tileGrid = this.tileGrid;
tileCache.forEach(function(tile) {
if (tile.tileCoord[0] !== z || tile.getState() !== TileState.LOADED) {
return;
}
const sourceTiles = tile.getSourceTiles();
for (let i = 0, ii = sourceTiles.length; i < ii; ++i) {
const sourceTile = sourceTiles[i];
const tileCoord = sourceTile.tileCoord;
if (intersects(extent, tileGrid.getTileCoordExtent(tileCoord))) {
const tileFeatures = sourceTile.getFeatures();
if (tileFeatures) {
for (let j = 0, jj = tileFeatures.length; j < jj; ++j) {
const candidate = tileFeatures[j];
const geometry = candidate.getGeometry();
if (geometry.intersectsExtent(extent)) {
features.push(candidate);
}
}
}
}
}
});
return features;
}
/**
* @return {boolean} The source can have overlapping geometries.
*/

View File

@@ -13,6 +13,8 @@ import {listen, unlistenByKey} from '../../../../src/ol/events.js';
import TileState from '../../../../src/ol/TileState.js';
import {getCenter} from '../../../../src/ol/extent.js';
import {unByKey} from '../../../../src/ol/Observable.js';
import Feature from '../../../../src/ol/Feature.js';
import {fromExtent} from '../../../../src/ol/geom/Polygon.js';
describe('ol.source.VectorTile', function() {
@@ -354,4 +356,68 @@ describe('ol.source.VectorTile', function() {
});
describe('getFeatuersInExtent', function() {
let map, source, target;
beforeEach(function() {
source = new VectorTileSource({
maxZoom: 15,
tileSize: 256,
url: '{z}/{x}/{y}',
tileLoadFunction: function(tile) {
const extent = source.getTileGrid().getTileCoordExtent(tile.tileCoord);
const feature = new Feature(fromExtent(extent));
feature.set('z', tile.tileCoord[0]);
tile.setFeatures([feature]);
}
});
target = document.createElement('div');
target.style.width = '100px';
target.style.height = '100px';
document.body.appendChild(target);
map = new Map({
target: target,
layers: [
new VectorTileLayer({
source: source
})
],
view: new View({
center: [0, 0],
zoom: 0
})
});
});
afterEach(function() {
map.setTarget(null);
document.body.removeChild(target);
});
it('returns an empty array when no tiles are in the cache', function() {
source.tileCache.clear();
const extent = map.getView().calculateExtent(map.getSize());
expect(source.getFeaturesInExtent(extent).length).to.be(0);
});
it('returns features in extent for the last rendered z', function(done) {
map.getView().setZoom(15);
map.once('rendercomplete', function() {
const extent = map.getView().calculateExtent(map.getSize());
const features = source.getFeaturesInExtent(extent);
expect(features.length).to.be(4);
expect(features[0].get('z')).to.be(15);
map.getView().setZoom(0);
map.once('rendercomplete', function() {
const extent = map.getView().calculateExtent(map.getSize());
const features = source.getFeaturesInExtent(extent);
expect(features.length).to.be(1);
expect(features[0].get('z')).to.be(0);
done();
});
});
});
});
});