Merge pull request #10807 from mike-000/patch-2
Handle Graticule wrapX without calculating excess meridians
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import {modulo} from './math.js';
|
||||
import {padNumber} from './string.js';
|
||||
import {getWidth} from './extent.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -402,3 +403,23 @@ export function toStringHDMS(coordinate, opt_fractionDigits) {
|
||||
export function toStringXY(coordinate, opt_fractionDigits) {
|
||||
return format(coordinate, '{x}, {y}', opt_fractionDigits);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modifies the provided coordinate in-place to be within the real world
|
||||
* extent. The lower projection extent boundary is inclusive, the upper one
|
||||
* exclusive.
|
||||
*
|
||||
* @param {Coordinate} coordinate Coordinate.
|
||||
* @param {import("./proj/Projection.js").default} projection Projection
|
||||
* @return {Coordinate} The coordinate within the real world extent.
|
||||
*/
|
||||
export function wrapX(coordinate, projection) {
|
||||
const projectionExtent = projection.getExtent();
|
||||
if (projection.canWrapX() && (coordinate[0] < projectionExtent[0] || coordinate[0] >= projectionExtent[2])) {
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
const worldsAway = Math.floor((coordinate[0] - projectionExtent[0]) / worldWidth);
|
||||
coordinate[0] -= (worldsAway * worldWidth);
|
||||
}
|
||||
return coordinate;
|
||||
}
|
||||
|
||||
@@ -295,6 +295,18 @@ export function equals(extent1, extent2) {
|
||||
extent1[1] == extent2[1] && extent1[3] == extent2[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if two extents are approximately equivalent.
|
||||
* @param {Extent} extent1 Extent 1.
|
||||
* @param {Extent} extent2 Extent 2.
|
||||
* @param {number} tolerance Tolerance in extent coordinate units.
|
||||
* @return {boolean} The two extents differ by less than the tolerance.
|
||||
*/
|
||||
export function approximatelyEquals(extent1, extent2, tolerance) {
|
||||
return Math.abs(extent1[0] - extent2[0]) < tolerance && Math.abs(extent1[2] - extent2[2]) < tolerance &&
|
||||
Math.abs(extent1[1] - extent2[1]) < tolerance && Math.abs(extent1[3] - extent2[3]) < tolerance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modify an extent to include another extent.
|
||||
@@ -813,3 +825,25 @@ export function applyTransform(extent, transformFn, opt_extent, opt_stops) {
|
||||
}
|
||||
return _boundingExtentXYs(xs, ys, opt_extent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Modifies the provided extent in-place to be within the real world
|
||||
* extent.
|
||||
*
|
||||
* @param {Extent} extent Extent.
|
||||
* @param {import("./proj/Projection.js").default} projection Projection
|
||||
* @return {Extent} The extent within the real world extent.
|
||||
*/
|
||||
export function wrapX(extent, projection) {
|
||||
const projectionExtent = projection.getExtent();
|
||||
const center = getCenter(extent);
|
||||
if (projection.canWrapX() && (center[0] < projectionExtent[0] || center[0] >= projectionExtent[2])) {
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth);
|
||||
const offset = (worldsAway * worldWidth);
|
||||
extent[0] -= offset;
|
||||
extent[2] -= offset;
|
||||
}
|
||||
return extent;
|
||||
}
|
||||
|
||||
@@ -19,12 +19,14 @@ import {
|
||||
containsCoordinate,
|
||||
containsExtent,
|
||||
equals,
|
||||
approximatelyEquals,
|
||||
getCenter,
|
||||
getHeight,
|
||||
getIntersection,
|
||||
getWidth,
|
||||
intersects,
|
||||
isEmpty
|
||||
isEmpty,
|
||||
wrapX as wrapExtentX
|
||||
} from '../extent.js';
|
||||
import {clamp} from '../math.js';
|
||||
import Style from '../style/Style.js';
|
||||
@@ -479,11 +481,21 @@ class Graticule extends VectorLayer {
|
||||
* @return {Array<import("../extent.js").Extent>} Extents.
|
||||
*/
|
||||
strategyFunction(extent, resolution) {
|
||||
if (this.loadedExtent_ && !equals(this.loadedExtent_, extent)) {
|
||||
// we should not keep track of loaded extents
|
||||
this.getSource().removeLoadedExtent(this.loadedExtent_);
|
||||
// extents may be passed in different worlds, to avoid endless loop we use only one
|
||||
let realWorldExtent = extent.slice();
|
||||
if (this.projection_ && this.getSource().getWrapX()) {
|
||||
wrapExtentX(realWorldExtent, this.projection_);
|
||||
}
|
||||
return [extent];
|
||||
if (this.loadedExtent_) {
|
||||
if (approximatelyEquals(this.loadedExtent_, realWorldExtent, resolution)) {
|
||||
// make sure result is exactly equal to previous extent
|
||||
realWorldExtent = this.loadedExtent_.slice();
|
||||
} else {
|
||||
// we should not keep track of loaded extents
|
||||
this.getSource().removeLoadedExtent(this.loadedExtent_);
|
||||
}
|
||||
}
|
||||
return [realWorldExtent];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -717,6 +729,18 @@ class Graticule extends VectorLayer {
|
||||
return;
|
||||
}
|
||||
|
||||
let wrapX = false;
|
||||
const projectionExtent = this.projection_.getExtent();
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
if (this.getSource().getWrapX() && this.projection_.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||
if (getWidth(extent) >= worldWidth) {
|
||||
extent[0] = projectionExtent[0];
|
||||
extent[2] = projectionExtent[2];
|
||||
} else {
|
||||
wrapX = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Constrain the center to fit into the extent available to the graticule
|
||||
|
||||
const validCenterP = [
|
||||
@@ -740,44 +764,56 @@ class Graticule extends VectorLayer {
|
||||
|
||||
// Limit the extent to fit into the extent available to the graticule
|
||||
|
||||
const validExtentP = [
|
||||
clamp(extent[0], this.minX_, this.maxX_),
|
||||
clamp(extent[1], this.minY_, this.maxY_),
|
||||
clamp(extent[2], this.minX_, this.maxX_),
|
||||
clamp(extent[3], this.minY_, this.maxY_)
|
||||
];
|
||||
let validExtentP = extent;
|
||||
if (!wrapX) {
|
||||
validExtentP = [
|
||||
clamp(extent[0], this.minX_, this.maxX_),
|
||||
clamp(extent[1], this.minY_, this.maxY_),
|
||||
clamp(extent[2], this.minX_, this.maxX_),
|
||||
clamp(extent[3], this.minY_, this.maxY_)
|
||||
];
|
||||
}
|
||||
|
||||
// Transform the extent to get the lon lat ranges for the edges of the extent
|
||||
|
||||
const validExtent = applyTransform(validExtentP, this.toLonLatTransform_, undefined, 8);
|
||||
|
||||
// Check if extremities of the world extent lie inside the extent
|
||||
// (for example the pole in a polar projection)
|
||||
// and extend the extent as appropriate
|
||||
let maxLat = validExtent[3];
|
||||
let maxLon = validExtent[2];
|
||||
let minLat = validExtent[1];
|
||||
let minLon = validExtent[0];
|
||||
|
||||
if (containsCoordinate(validExtentP, this.bottomLeft_)) {
|
||||
validExtent[0] = this.minLon_;
|
||||
validExtent[1] = this.minLat_;
|
||||
}
|
||||
if (containsCoordinate(validExtentP, this.bottomRight_)) {
|
||||
validExtent[2] = this.maxLon_;
|
||||
validExtent[1] = this.minLat_;
|
||||
}
|
||||
if (containsCoordinate(validExtentP, this.topLeft_)) {
|
||||
validExtent[0] = this.minLon_;
|
||||
validExtent[3] = this.maxLat_;
|
||||
}
|
||||
if (containsCoordinate(validExtentP, this.topRight_)) {
|
||||
validExtent[2] = this.maxLon_;
|
||||
validExtent[3] = this.maxLat_;
|
||||
}
|
||||
if (!wrapX) {
|
||||
|
||||
// The transformed center may also extend the lon lat ranges used for rendering
|
||||
// Check if extremities of the world extent lie inside the extent
|
||||
// (for example the pole in a polar projection)
|
||||
// and extend the extent as appropriate
|
||||
|
||||
const maxLat = clamp(validExtent[3], centerLat, this.maxLat_);
|
||||
const maxLon = clamp(validExtent[2], centerLon, this.maxLon_);
|
||||
const minLat = clamp(validExtent[1], this.minLat_, centerLat);
|
||||
const minLon = clamp(validExtent[0], this.minLon_, centerLon);
|
||||
if (containsCoordinate(validExtentP, this.bottomLeft_)) {
|
||||
minLon = this.minLon_;
|
||||
minLat = this.minLat_;
|
||||
}
|
||||
if (containsCoordinate(validExtentP, this.bottomRight_)) {
|
||||
maxLon = this.maxLon_;
|
||||
minLat = this.minLat_;
|
||||
}
|
||||
if (containsCoordinate(validExtentP, this.topLeft_)) {
|
||||
minLon = this.minLon_;
|
||||
maxLat = this.maxLat_;
|
||||
}
|
||||
if (containsCoordinate(validExtentP, this.topRight_)) {
|
||||
maxLon = this.maxLon_;
|
||||
maxLat = this.maxLat_;
|
||||
}
|
||||
|
||||
// The transformed center may also extend the lon lat ranges used for rendering
|
||||
|
||||
maxLat = clamp(maxLat, centerLat, this.maxLat_);
|
||||
maxLon = clamp(maxLon, centerLon, this.maxLon_);
|
||||
minLat = clamp(minLat, this.minLat_, centerLat);
|
||||
minLon = clamp(minLon, this.minLon_, centerLon);
|
||||
|
||||
}
|
||||
|
||||
// Create meridians
|
||||
|
||||
@@ -787,17 +823,29 @@ class Graticule extends VectorLayer {
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0);
|
||||
|
||||
cnt = 0;
|
||||
while (lon != this.minLon_ && cnt++ < maxLines) {
|
||||
lon = Math.max(lon - interval, this.minLon_);
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
if (wrapX) {
|
||||
while ((lon -= interval) >= minLon && cnt++ < maxLines) {
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
}
|
||||
} else {
|
||||
while (lon != this.minLon_ && cnt++ < maxLines) {
|
||||
lon = Math.max(lon - interval, this.minLon_);
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
}
|
||||
}
|
||||
|
||||
lon = clamp(centerLon, this.minLon_, this.maxLon_);
|
||||
|
||||
cnt = 0;
|
||||
while (lon != this.maxLon_ && cnt++ < maxLines) {
|
||||
lon = Math.min(lon + interval, this.maxLon_);
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
if (wrapX) {
|
||||
while ((lon += interval) <= maxLon && cnt++ < maxLines) {
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
}
|
||||
} else {
|
||||
while (lon != this.maxLon_ && cnt++ < maxLines) {
|
||||
lon = Math.min(lon + interval, this.maxLon_);
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
}
|
||||
}
|
||||
|
||||
this.meridians_.length = idx;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {inView} from '../layer/Layer.js';
|
||||
import {shared as iconImageCache} from '../style/IconImageCache.js';
|
||||
import {compose as composeTransform, makeInverse} from '../transform.js';
|
||||
import {renderDeclutterItems} from '../render.js';
|
||||
import {wrapX} from '../coordinate.js';
|
||||
|
||||
/**
|
||||
* @abstract
|
||||
@@ -102,19 +103,12 @@ class MapRenderer extends Disposable {
|
||||
|
||||
const projection = viewState.projection;
|
||||
|
||||
let translatedCoordinate = coordinate;
|
||||
const translatedCoordinate = wrapX(coordinate.slice(), projection);
|
||||
const offsets = [[0, 0]];
|
||||
if (projection.canWrapX()) {
|
||||
if (projection.canWrapX() && checkWrapped) {
|
||||
const projectionExtent = projection.getExtent();
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
const x = coordinate[0];
|
||||
if (x < projectionExtent[0] || x > projectionExtent[2]) {
|
||||
const worldsAway = Math.ceil((projectionExtent[0] - x) / worldWidth);
|
||||
translatedCoordinate = [x + worldWidth * worldsAway, coordinate[1]];
|
||||
}
|
||||
if (checkWrapped) {
|
||||
offsets.push([-worldWidth, 0], [worldWidth, 0]);
|
||||
}
|
||||
offsets.push([-worldWidth, 0], [worldWidth, 0]);
|
||||
}
|
||||
|
||||
const layerStates = frameState.layerStatesArray;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
*/
|
||||
import {getUid} from '../../util.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import {buffer, createEmpty, containsExtent, getWidth, intersects as intersectsExtent} from '../../extent.js';
|
||||
import {buffer, createEmpty, containsExtent, getWidth, intersects as intersectsExtent, wrapX as wrapExtentX} from '../../extent.js';
|
||||
import {wrapX as wrapCoordinateX} from '../../coordinate.js';
|
||||
import {fromUserExtent, toUserExtent, getUserProjection, getTransformFromProjections} from '../../proj.js';
|
||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||
import ExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
||||
@@ -361,9 +362,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
const center = viewState.center.slice();
|
||||
const extent = buffer(frameStateExtent,
|
||||
vectorLayerRenderBuffer * resolution);
|
||||
const projectionExtent = viewState.projection.getExtent();
|
||||
const loadExtents = [extent.slice()];
|
||||
const projectionExtent = projection.getExtent();
|
||||
|
||||
if (vectorSource.getWrapX() && viewState.projection.canWrapX() &&
|
||||
if (vectorSource.getWrapX() && projection.canWrapX() &&
|
||||
!containsExtent(projectionExtent, frameState.extent)) {
|
||||
// For the replay group, we need an extent that intersects the real world
|
||||
// (-180° to +180°). To support geometries in a coordinate range from -540°
|
||||
@@ -374,8 +376,14 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
const gutter = Math.max(getWidth(extent) / 2, worldWidth);
|
||||
extent[0] = projectionExtent[0] - gutter;
|
||||
extent[2] = projectionExtent[2] + gutter;
|
||||
const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth);
|
||||
center[0] -= (worldsAway * worldWidth);
|
||||
wrapCoordinateX(center, projection);
|
||||
const loadExtent = wrapExtentX(loadExtents[0], projection);
|
||||
// If the extent crosses the date line, we load data for both edges of the worlds
|
||||
if (loadExtent[0] < projectionExtent[0] && loadExtent[2] < projectionExtent[2]) {
|
||||
loadExtents.push([loadExtent[0] + worldWidth, loadExtent[1], loadExtent[2] + worldWidth, loadExtent[3]]);
|
||||
} else if (loadExtent[0] > projectionExtent[0] && loadExtent[2] > projectionExtent[2]) {
|
||||
loadExtents.push([loadExtent[0] - worldWidth, loadExtent[1], loadExtent[2] - worldWidth, loadExtent[3]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.dirty_ &&
|
||||
@@ -398,10 +406,14 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer {
|
||||
const userProjection = getUserProjection();
|
||||
let userTransform;
|
||||
if (userProjection) {
|
||||
vectorSource.loadFeatures(toUserExtent(extent, projection), resolution, userProjection);
|
||||
for (let i = 0, ii = loadExtents.length; i < ii; ++i) {
|
||||
vectorSource.loadFeatures(toUserExtent(loadExtents[i], projection), resolution, userProjection);
|
||||
}
|
||||
userTransform = getTransformFromProjections(userProjection, projection);
|
||||
} else {
|
||||
vectorSource.loadFeatures(extent, resolution, projection);
|
||||
for (let i = 0, ii = loadExtents.length; i < ii; ++i) {
|
||||
vectorSource.loadFeatures(loadExtents[i], resolution, projection);
|
||||
}
|
||||
}
|
||||
|
||||
const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio);
|
||||
|
||||
@@ -6,7 +6,7 @@ import TileState from '../../TileState.js';
|
||||
import ViewHint from '../../ViewHint.js';
|
||||
import {listen, unlistenByKey} from '../../events.js';
|
||||
import EventType from '../../events/EventType.js';
|
||||
import {buffer, containsCoordinate, equals, getIntersection, intersects, containsExtent, getWidth, getTopLeft} from '../../extent.js';
|
||||
import {buffer, containsCoordinate, equals, getIntersection, intersects, containsExtent, getTopLeft} from '../../extent.js';
|
||||
import VectorTileRenderType from '../../layer/VectorTileRenderType.js';
|
||||
import ReplayType from '../../render/canvas/BuilderType.js';
|
||||
import CanvasBuilderGroup from '../../render/canvas/BuilderGroup.js';
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import CanvasExecutorGroup, {replayDeclutter} from '../../render/canvas/ExecutorGroup.js';
|
||||
import {clear} from '../../obj.js';
|
||||
import {createHitDetectionImageData, hitDetect} from '../../render/canvas/hitdetect.js';
|
||||
import {wrapX} from '../../coordinate.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -353,9 +354,7 @@ class CanvasVectorTileLayerRenderer extends CanvasTileLayerRenderer {
|
||||
if (tile.getState() === TileState.LOADED && tile.hifi) {
|
||||
const extent = tileGrid.getTileCoordExtent(tile.tileCoord);
|
||||
if (source.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||
const worldWidth = getWidth(projectionExtent);
|
||||
const worldsAway = Math.floor((coordinate[0] - projectionExtent[0]) / worldWidth);
|
||||
coordinate[0] -= (worldsAway * worldWidth);
|
||||
wrapX(coordinate, projection);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -190,7 +190,7 @@ class VectorSource extends Source {
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.overlaps_ = options.overlaps == undefined ? true : options.overlaps;
|
||||
this.overlaps_ = options.overlaps === undefined ? true : options.overlaps;
|
||||
|
||||
/**
|
||||
* @private
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {add as addCoordinate, scale as scaleCoordinate, rotate as rotateCoordinate, equals as coordinatesEqual, format as formatCoordinate, closestOnCircle, closestOnSegment, createStringXY, squaredDistanceToSegment, toStringXY, toStringHDMS} from '../../../src/ol/coordinate.js';
|
||||
import {add as addCoordinate, scale as scaleCoordinate, rotate as rotateCoordinate, equals as coordinatesEqual, format as formatCoordinate, closestOnCircle, closestOnSegment, createStringXY, squaredDistanceToSegment, toStringXY, toStringHDMS, wrapX} from '../../../src/ol/coordinate.js';
|
||||
import Circle from '../../../src/ol/geom/Circle.js';
|
||||
import {get} from '../../../src/ol/proj.js';
|
||||
|
||||
|
||||
describe('ol.coordinate', function() {
|
||||
@@ -235,4 +236,29 @@ describe('ol.coordinate', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('wrapX()', function() {
|
||||
const projection = get('EPSG:4326');
|
||||
|
||||
it('leaves real world coordinate untouched', function() {
|
||||
expect(wrapX([16, 48], projection)).to.eql([16, 48]);
|
||||
});
|
||||
|
||||
it('moves left world coordinate to real world', function() {
|
||||
expect(wrapX([-344, 48], projection)).to.eql([16, 48]);
|
||||
});
|
||||
|
||||
it('moves right world coordinate to real world', function() {
|
||||
expect(wrapX([376, 48], projection)).to.eql([16, 48]);
|
||||
});
|
||||
|
||||
it('moves far off left coordinate to real world', function() {
|
||||
expect(wrapX([-1064, 48], projection)).to.eql([16, 48]);
|
||||
});
|
||||
|
||||
it('moves far off right coordinate to real world', function() {
|
||||
expect(wrapX([1096, 48], projection)).to.eql([16, 48]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as _ol_extent_ from '../../../src/ol/extent.js';
|
||||
import {getTransform} from '../../../src/ol/proj.js';
|
||||
import {getTransform, get} from '../../../src/ol/proj.js';
|
||||
import {register} from '../../../src/ol/proj/proj4.js';
|
||||
|
||||
|
||||
@@ -818,4 +818,52 @@ describe('ol.extent', function() {
|
||||
|
||||
});
|
||||
|
||||
describe('wrapX()', function() {
|
||||
const projection = get('EPSG:4326');
|
||||
|
||||
it('leaves real world extent untouched', function() {
|
||||
expect(_ol_extent_.wrapX([16, 48, 18, 49], projection)).to.eql([16, 48, 18, 49]);
|
||||
});
|
||||
|
||||
it('moves left world extent to real world', function() {
|
||||
expect(_ol_extent_.wrapX([-344, 48, -342, 49], projection)).to.eql([16, 48, 18, 49]);
|
||||
});
|
||||
|
||||
it('moves right world extent to real world', function() {
|
||||
expect(_ol_extent_.wrapX([376, 48, 378, 49], projection)).to.eql([16, 48, 18, 49]);
|
||||
});
|
||||
|
||||
it('moves far off left extent to real world', function() {
|
||||
expect(_ol_extent_.wrapX([-1064, 48, -1062, 49], projection)).to.eql([16, 48, 18, 49]);
|
||||
});
|
||||
|
||||
it('moves far off right extent to real world', function() {
|
||||
expect(_ol_extent_.wrapX([1096, 48, 1098, 49], projection)).to.eql([16, 48, 18, 49]);
|
||||
});
|
||||
|
||||
it('leaves -180 crossing extent with real world center untouched', function() {
|
||||
expect(_ol_extent_.wrapX([-184, 48, 16, 49], projection)).to.eql([-184, 48, 16, 49]);
|
||||
});
|
||||
|
||||
it('moves +180 crossing extent with off-world center to the real world', function() {
|
||||
expect(_ol_extent_.wrapX([300, 48, 376, 49], projection)).to.eql([-60, 48, 16, 49]);
|
||||
});
|
||||
|
||||
it('produces the same real world extent for shifted extents with center at +/-180', function() {
|
||||
expect(_ol_extent_.wrapX([360, -90, 720, 90], projection)).to.eql([-360, -90, 0, 90]);
|
||||
expect(_ol_extent_.wrapX([0, -90, 360, 90], projection)).to.eql([-360, -90, 0, 90]);
|
||||
expect(_ol_extent_.wrapX([-360, -90, 0, 90], projection)).to.eql([-360, -90, 0, 90]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('approximatelyEquals', function() {
|
||||
it('returns true when within tolerance', function() {
|
||||
expect(_ol_extent_.approximatelyEquals([16, 48, 17, 49], [16.09, 48, 17, 49], 0.1)).to.be(true);
|
||||
});
|
||||
it('returns false when not within tolerance', function() {
|
||||
expect(_ol_extent_.approximatelyEquals([16, 48, 17, 49], [16.11, 48, 17, 49], 0.1)).to.be(false);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import Feature from '../../../../../src/ol/Feature.js';
|
||||
import Map from '../../../../../src/ol/Map.js';
|
||||
import View from '../../../../../src/ol/View.js';
|
||||
import {buffer as bufferExtent, getWidth} from '../../../../../src/ol/extent.js';
|
||||
import {buffer as bufferExtent, getWidth, getCenter} from '../../../../../src/ol/extent.js';
|
||||
import Circle from '../../../../../src/ol/geom/Circle.js';
|
||||
import Point from '../../../../../src/ol/geom/Point.js';
|
||||
import {fromExtent} from '../../../../../src/ol/geom/Polygon.js';
|
||||
import VectorLayer from '../../../../../src/ol/layer/Vector.js';
|
||||
import {bbox as bboxStrategy} from '../../../../../src/ol/loadingstrategy.js';
|
||||
import {get as getProjection} from '../../../../../src/ol/proj.js';
|
||||
import {checkedFonts} from '../../../../../src/ol/render/canvas.js';
|
||||
import CanvasVectorLayerRenderer from '../../../../../src/ol/renderer/canvas/VectorLayer.js';
|
||||
@@ -220,21 +221,29 @@ describe('ol.renderer.canvas.VectorLayer', function() {
|
||||
});
|
||||
|
||||
describe('#prepareFrame and #compose', function() {
|
||||
let frameState, projExtent, renderer, worldWidth, buffer;
|
||||
let frameState, projExtent, renderer, worldWidth, buffer, loadExtents;
|
||||
|
||||
function loader(extent) {
|
||||
loadExtents.push(extent);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
const layer = new VectorLayer({
|
||||
source: new VectorSource({wrapX: true})
|
||||
source: new VectorSource({
|
||||
wrapX: true,
|
||||
loader: loader,
|
||||
strategy: bboxStrategy
|
||||
})
|
||||
});
|
||||
renderer = new CanvasVectorLayerRenderer(layer);
|
||||
const projection = getProjection('EPSG:3857');
|
||||
projExtent = projection.getExtent();
|
||||
worldWidth = getWidth(projExtent);
|
||||
buffer = layer.getRenderBuffer();
|
||||
loadExtents = [];
|
||||
frameState = {
|
||||
viewHints: [],
|
||||
viewState: {
|
||||
center: [0, 0],
|
||||
projection: projection,
|
||||
resolution: 1,
|
||||
rotation: 0
|
||||
@@ -242,55 +251,81 @@ describe('ol.renderer.canvas.VectorLayer', function() {
|
||||
};
|
||||
});
|
||||
|
||||
function setExtent(extent) {
|
||||
frameState.extent = extent;
|
||||
frameState.viewState.center = getCenter(extent);
|
||||
}
|
||||
|
||||
it('sets correct extent for small viewport near dateline', function() {
|
||||
|
||||
frameState.extent =
|
||||
[projExtent[0] - 10000, -10000, projExtent[0] + 10000, 10000];
|
||||
setExtent([projExtent[0] - 10000, -10000, projExtent[0] + 10000, 10000]);
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([
|
||||
projExtent[0] - worldWidth + buffer,
|
||||
-10000, projExtent[2] + worldWidth - buffer, 10000
|
||||
], buffer));
|
||||
|
||||
expect(loadExtents.length).to.be(2);
|
||||
expect(loadExtents[0]).to.eql(bufferExtent(frameState.extent, buffer));
|
||||
const otherExtent = [projExtent[2] - 10000, -10000, projExtent[2] + 10000, 10000];
|
||||
expect(loadExtents[1]).to.eql(bufferExtent(otherExtent, buffer));
|
||||
});
|
||||
|
||||
it('sets correct extent for viewport less than 1 world wide', function() {
|
||||
|
||||
frameState.extent =
|
||||
[projExtent[0] - 10000, -10000, projExtent[1] - 10000, 10000];
|
||||
setExtent([projExtent[0] - 10000, -10000, projExtent[2] - 10000, 10000]);
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([
|
||||
projExtent[0] - worldWidth + buffer,
|
||||
-10000, projExtent[2] + worldWidth - buffer, 10000
|
||||
], buffer));
|
||||
expect(loadExtents.length).to.be(2);
|
||||
expect(loadExtents[0]).to.eql(bufferExtent(frameState.extent, buffer));
|
||||
const otherExtent = [projExtent[0] - 10000 + worldWidth, -10000, projExtent[2] - 10000 + worldWidth, 10000];
|
||||
expect(loadExtents[1]).to.eql(bufferExtent(otherExtent, buffer));
|
||||
});
|
||||
|
||||
it('sets correct extent for viewport more than 1 world wide', function() {
|
||||
|
||||
frameState.extent =
|
||||
[2 * projExtent[0] - 10000, -10000, 2 * projExtent[1] + 10000, 10000];
|
||||
setExtent([2 * projExtent[0] + 10000, -10000, 2 * projExtent[2] - 10000, 10000]);
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([
|
||||
projExtent[0] - worldWidth + buffer,
|
||||
-10000, projExtent[2] + worldWidth - buffer, 10000
|
||||
], buffer));
|
||||
expect(loadExtents.length).to.be(1);
|
||||
expect(loadExtents[0]).to.eql(bufferExtent(frameState.extent, buffer));
|
||||
});
|
||||
|
||||
it('sets correct extent for viewport more than 2 worlds wide', function() {
|
||||
it('sets correct extent for viewport more than 2 worlds wide, one world away', function() {
|
||||
|
||||
frameState.extent = [
|
||||
projExtent[0] - 2 * worldWidth - 10000,
|
||||
-10000, projExtent[1] + 2 * worldWidth + 10000, 10000
|
||||
];
|
||||
setExtent([projExtent[0] - 2 * worldWidth - 10000,
|
||||
-10000, projExtent[0] + 2 * worldWidth + 10000, 10000
|
||||
]);
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([
|
||||
projExtent[0] - 2 * worldWidth - 10000,
|
||||
-10000, projExtent[2] + 2 * worldWidth + 10000, 10000
|
||||
], buffer));
|
||||
expect(loadExtents.length).to.be(1);
|
||||
const normalizedExtent = [projExtent[0] - 2 * worldWidth + worldWidth - 10000, -10000, projExtent[0] + 2 * worldWidth + worldWidth + 10000, 10000];
|
||||
expect(loadExtents[0]).to.eql(bufferExtent(normalizedExtent, buffer));
|
||||
});
|
||||
|
||||
it('sets correct extent for small viewport, one world away', function() {
|
||||
|
||||
setExtent([-worldWidth - 10000, -10000, -worldWidth + 10000, 10000]);
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([
|
||||
projExtent[0] - worldWidth + buffer,
|
||||
-10000, projExtent[2] + worldWidth - buffer, 10000
|
||||
], buffer));
|
||||
expect(loadExtents.length).to.be(1);
|
||||
const normalizedExtent = [-10000, -10000, 10000, 10000];
|
||||
expect(loadExtents[0]).to.eql(bufferExtent(normalizedExtent, buffer));
|
||||
});
|
||||
|
||||
it('sets replayGroupChanged correctly', function() {
|
||||
frameState.extent = [-10000, -10000, 10000, 10000];
|
||||
setExtent([-10000, -10000, 10000, 10000]);
|
||||
renderer.prepareFrame(frameState);
|
||||
expect(renderer.replayGroupChanged).to.be(true);
|
||||
renderer.prepareFrame(frameState);
|
||||
|
||||
Reference in New Issue
Block a user