#13690 VectorSource#getFeaturesInExtent add projection parameter

This commit is contained in:
Tomas Burleigh
2022-05-21 14:28:19 +12:00
parent 3857e2051c
commit 78274befb8
4 changed files with 148 additions and 3 deletions

View File

@@ -858,3 +858,47 @@ export function wrapX(extent, projection) {
}
return extent;
}
/**
* Fits the extent to the real world
*
* If the extent does not cross the anti meridian, this will return the extent in an array
* If the extent crosses the anti meridian, the extent will be sliced, so each part fits within the
* real world
*
*
* @param {Extent} extent Extent.
* @param {import("./proj/Projection.js").default} projection Projection
* @return {Array<Extent>} The extent within the real world extent.
*/
export function wrapAndSliceX(extent, projection) {
if (projection.canWrapX()) {
const projectionExtent = projection.getExtent();
if (!isFinite(extent[0]) || !isFinite(extent[2])) {
return [[projectionExtent[0], extent[1], projectionExtent[2], extent[3]]];
}
wrapX(extent, projection);
const worldWidth = getWidth(projectionExtent);
if (getWidth(extent) > worldWidth) {
// the extent wraps around on itself
return [[projectionExtent[0], extent[1], projectionExtent[2], extent[3]]];
} else if (extent[0] < projectionExtent[0]) {
// the extent crosses the anti meridian, so it needs to be sliced
return [
[extent[0] + worldWidth, extent[1], projectionExtent[2], extent[3]],
[projectionExtent[0], extent[1], extent[2], extent[3]],
];
} else if (extent[2] > projectionExtent[2]) {
// the extent crosses the anti meridian, so it needs to be sliced
return [
[extent[0], extent[1], projectionExtent[2], extent[3]],
[projectionExtent[0], extent[1], extent[2] - worldWidth, extent[3]],
];
}
}
return [extent];
}

View File

@@ -14,7 +14,7 @@ import VectorEventType from './VectorEventType.js';
import {TRUE, VOID} from '../functions.js';
import {all as allStrategy} from '../loadingstrategy.js';
import {assert} from '../asserts.js';
import {containsExtent, equals} from '../extent.js';
import {containsExtent, equals, wrapAndSliceX} from '../extent.js';
import {extend} from '../array.js';
import {getUid} from '../util.js';
import {getValues, isEmpty} from '../obj.js';
@@ -736,12 +736,23 @@ class VectorSource extends Source {
* features.
*
* @param {import("../extent.js").Extent} extent Extent.
* @param {import("../proj/Projection.js").default} [projection] Projection.
* @return {Array<import("../Feature.js").default<Geometry>>} Features.
* @api
*/
getFeaturesInExtent(extent) {
getFeaturesInExtent(extent, projection) {
if (this.featuresRtree_) {
return this.featuresRtree_.getInExtent(extent);
const multiWorld = projection && projection.canWrapX() && this.getWrapX();
if (!multiWorld) {
return this.featuresRtree_.getInExtent(extent);
}
const extents = wrapAndSliceX(extent, projection);
return [].concat(
...extents.map((anExtent) => this.featuresRtree_.getInExtent(anExtent))
);
} else if (this.featuresCollection_) {
return this.featuresCollection_.getArray().slice(0);
} else {

View File

@@ -7,6 +7,7 @@ import Point from '../../../../../src/ol/geom/Point.js';
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
import VectorSource from '../../../../../src/ol/source/Vector.js';
import View from '../../../../../src/ol/View.js';
import sinon from 'sinon';
import {bbox as bboxStrategy} from '../../../../../src/ol/loadingstrategy.js';
import {
fromLonLat,
@@ -979,4 +980,45 @@ describe('ol.source.Vector', function () {
expect(source.getFeatures().length).to.be(0);
});
});
describe('#getFeaturesInExtent()', function () {
it('adjusts the extent if projection canWrapX', function () {
const a = new Feature(new Point([0, 0]));
const b = new Feature(new Point([179, 0]));
const c = new Feature(new Point([-179, 0]));
const source = new VectorSource({
features: [a, b, c],
});
const projection = getProjection('EPSG:4326');
expect(
source.getFeaturesInExtent([-180, -90, 180, 90], projection).length
).to.be(3);
const onlyB = source.getFeaturesInExtent([1, -90, 180, 90], projection);
expect(onlyB.length).to.be(1);
expect(onlyB).to.contain(b);
const bAndC = source.getFeaturesInExtent([1, -90, 182, 90], projection);
expect(bAndC.length).to.be(2);
expect(bAndC).to.contain(b);
expect(bAndC).to.contain(c);
const onlyC = source.getFeaturesInExtent([-180, -90, -1, 90], projection);
expect(onlyC.length).to.be(1);
expect(onlyC).to.contain(c);
const bAndCAgain = source.getFeaturesInExtent(
[-182, -90, -1, 90],
projection
);
expect(bAndCAgain.length).to.be(2);
expect(bAndCAgain).to.contain(b);
expect(bAndCAgain).to.contain(c);
const onlyA = source.getFeaturesInExtent([359, -90, 361, 90], projection);
expect(onlyA.length).to.be(1);
expect(onlyA).to.contain(a);
});
});
});

View File

@@ -964,4 +964,52 @@ describe('ol/extent.js', function () {
).to.be(false);
});
});
describe('wrapAndSliceX', function () {
const projection = get('EPSG:4326');
it('leaves real world extent untouched', function () {
expect(_ol_extent_.wrapAndSliceX([16, 48, 18, 49], projection)).to.eql([
[16, 48, 18, 49],
]);
});
it('slices +180 crossing extents', function () {
expect(_ol_extent_.wrapAndSliceX([164, 48, 198, 49], projection)).to.eql([
[164, 48, 180, 49],
[-180, 48, -162, 49],
]);
expect(_ol_extent_.wrapAndSliceX([178, 48, 198, 49], projection)).to.eql([
[178, 48, 180, 49],
[-180, 48, -162, 49],
]);
});
it('slices -180 crossing extents', function () {
expect(
_ol_extent_.wrapAndSliceX([-198, 48, -160, 49], projection)
).to.eql([
[162, 48, 180, 49],
[-180, 48, -160, 49],
]);
expect(
_ol_extent_.wrapAndSliceX([-202, 48, -160, 49], projection)
).to.eql([
[158, 48, 180, 49],
[-180, 48, -160, 49],
]);
});
it('fits infinite extents to the projection extent', function () {
expect(
_ol_extent_.wrapAndSliceX([-Infinity, 48, -160, 49], projection)
).to.eql([[-180, 48, 180, 49]]);
expect(
_ol_extent_.wrapAndSliceX([-198, 48, Infinity, 49], projection)
).to.eql([[-180, 48, 180, 49]]);
});
});
});