From 772741cd0e3c059863f8c34ff81ebedff20b3887 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sat, 14 Mar 2020 10:14:28 +0000 Subject: [PATCH 01/24] Always load frameState extent for graticule layers do not call graticule loader with wrapped projection extent --- src/ol/renderer/canvas/VectorLayer.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 591ea95f83..d9e5e308e2 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -363,6 +363,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { vectorLayerRenderBuffer * resolution); const projectionExtent = viewState.projection.getExtent(); + let loadExtent = extent.slice(); + if (vectorSource.getWrapX() && viewState.projection.canWrapX() && !containsExtent(projectionExtent, frameState.extent)) { // For the replay group, we need an extent that intersects the real world @@ -378,6 +380,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { center[0] -= (worldsAway * worldWidth); } + if (typeof /** @type {?} */ (vectorLayer).getMeridians !== 'function') { + loadExtent = extent; + } + if (!this.dirty_ && this.renderedResolution_ == resolution && this.renderedRevision_ == vectorLayerRevision && @@ -398,10 +404,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const userProjection = getUserProjection(); let userTransform; if (userProjection) { - vectorSource.loadFeatures(toUserExtent(extent, projection), resolution, userProjection); + vectorSource.loadFeatures(toUserExtent(loadExtent, projection), resolution, userProjection); userTransform = getTransformFromProjections(userProjection, projection); } else { - vectorSource.loadFeatures(extent, resolution, projection); + vectorSource.loadFeatures(loadExtent, resolution, projection); } const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio); From bad0ff38ca2a690b045e94c86fab9e449c8ed5a0 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sat, 14 Mar 2020 10:37:03 +0000 Subject: [PATCH 02/24] handle wrapX without calculating excess meridians avoid calculating more meridians or longer parallels than necessary when viewport extent includes a wrapped world --- src/ol/layer/Graticule.js | 112 ++++++++++++++++++++++++++------------ 1 file changed, 76 insertions(+), 36 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 6bc7360dfa..0e3f073eb1 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -717,6 +717,22 @@ 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 { + const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); + center[0] -= (worldsAway * worldWidth); + extent[0] -= (worldsAway * worldWidth); + extent[2] -= (worldsAway * worldWidth); + wrapX = true; + } + } + // Constrain the center to fit into the extent available to the graticule const validCenterP = [ @@ -740,44 +756,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 +815,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; From 516a75ae226c8bbdcb418437e825601451175f0a Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sun, 15 Mar 2020 11:44:30 +0000 Subject: [PATCH 03/24] Always load frameState extent for graticule layers reorder and comment --- src/ol/renderer/canvas/VectorLayer.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index d9e5e308e2..3429f64106 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -361,9 +361,8 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const center = viewState.center.slice(); const extent = buffer(frameStateExtent, vectorLayerRenderBuffer * resolution); - const projectionExtent = viewState.projection.getExtent(); - let loadExtent = extent.slice(); + const projectionExtent = viewState.projection.getExtent(); if (vectorSource.getWrapX() && viewState.projection.canWrapX() && !containsExtent(projectionExtent, frameState.extent)) { @@ -376,14 +375,14 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const gutter = Math.max(getWidth(extent) / 2, worldWidth); extent[0] = projectionExtent[0] - gutter; extent[2] = projectionExtent[2] + gutter; + // Except for Graticule use this for loading features + if (typeof /** @type {?} */ (vectorLayer).getMeridians !== 'function') { + loadExtent = extent; + } const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); center[0] -= (worldsAway * worldWidth); } - if (typeof /** @type {?} */ (vectorLayer).getMeridians !== 'function') { - loadExtent = extent; - } - if (!this.dirty_ && this.renderedResolution_ == resolution && this.renderedRevision_ == vectorLayerRevision && From f6ede1a9c0895bfa946e636d6566427a5e5b473c Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Wed, 18 Mar 2020 13:28:21 +0000 Subject: [PATCH 04/24] handle wrapX without calculating excess meridians override extent validation only if the extent includes parts of two worlds --- src/ol/layer/Graticule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 0e3f073eb1..15ba7cd2a1 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -729,7 +729,7 @@ class Graticule extends VectorLayer { center[0] -= (worldsAway * worldWidth); extent[0] -= (worldsAway * worldWidth); extent[2] -= (worldsAway * worldWidth); - wrapX = true; + wrapX = !containsExtent(projectionExtent, extent); } } From f6bbf414a8de864801d31c136ab8c3816c07fe7d Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sat, 28 Mar 2020 20:59:23 +0000 Subject: [PATCH 05/24] Add loadWrapX option and getter --- src/ol/source/Vector.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index 49ac34296b..edf1c4e25d 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -146,6 +146,10 @@ export class VectorSourceEvent extends Event { * @property {boolean} [wrapX=true] Wrap the world horizontally. For vector editing across the * -180° and 180° meridians to work properly, this should be set to `false`. The * resulting geometry coordinates will then exceed the world bounds. + * @property {boolean} [loadWrapX=true] Call the loader with one world width either side + * of the projection extent when the world is wrapped horizontally. This allows features + * to be loaded in a single request, but may be inefficient. If `false` only the viewport + * extent is used and the loader must determine the appropriate real world requests. */ @@ -190,7 +194,13 @@ class VectorSource extends Source { * @private * @type {boolean} */ - this.overlaps_ = options.overlaps == undefined ? true : options.overlaps; + this.loadWrapX_ = options.loadWrapX === undefined ? true : options.loadWrapX; + + /** + * @private + * @type {boolean} + */ + this.overlaps_ = options.overlaps === undefined ? true : options.overlaps; /** * @private @@ -801,6 +811,14 @@ class VectorSource extends Source { } + /** + * @return {boolean} The loadWrapX option used to construct the source. + */ + getLoadWrapX() { + return this.loadWrapX_; + } + + /** * @return {boolean} The source can have overlapping geometries. */ From b560dab513bd60020df510d70ff32975de031ae8 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sat, 28 Mar 2020 21:02:32 +0000 Subject: [PATCH 06/24] Set loadWrapX: false in source --- src/ol/layer/Graticule.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 15ba7cd2a1..aa13cd8fee 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -437,7 +437,8 @@ class Graticule extends VectorLayer { features: new Collection(), overlaps: false, useSpatialIndex: false, - wrapX: options.wrapX + wrapX: options.wrapX, + loadWrapX: false }) ); From 3b760dc3083965f77ebcd2473974ef5361ce4fc0 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sat, 28 Mar 2020 21:10:41 +0000 Subject: [PATCH 07/24] Use getLoadWrapX() to determine extent to load --- src/ol/renderer/canvas/VectorLayer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 3429f64106..bdfe6c2e5b 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -375,8 +375,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const gutter = Math.max(getWidth(extent) / 2, worldWidth); extent[0] = projectionExtent[0] - gutter; extent[2] = projectionExtent[2] + gutter; - // Except for Graticule use this for loading features - if (typeof /** @type {?} */ (vectorLayer).getMeridians !== 'function') { + if (vectorSource.getLoadWrapX()) { loadExtent = extent; } const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); From 67c37c2163af4bb573b0106962c31ddc5e74b800 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sun, 29 Mar 2020 16:54:10 +0100 Subject: [PATCH 08/24] Test extents passed to loader --- .../ol/renderer/canvas/vectorlayer.test.js | 102 +++++++++++++++++- 1 file changed, 99 insertions(+), 3 deletions(-) diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index a766fdb767..575d7c11a8 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -6,6 +6,7 @@ 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,17 +221,25 @@ describe('ol.renderer.canvas.VectorLayer', function() { }); describe('#prepareFrame and #compose', function() { - let frameState, projExtent, renderer, worldWidth, buffer; + let frameState, projExtent, renderer, worldWidth, buffer, loadExtent; + const loader = function(extent) { + loadExtent = 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(); + loadExtent = undefined; frameState = { viewHints: [], viewState: { @@ -251,7 +260,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); - + expect(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); }); it('sets correct extent for viewport less than 1 world wide', function() { @@ -263,6 +272,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); + expect(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); }); it('sets correct extent for viewport more than 1 world wide', function() { @@ -274,6 +284,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); + expect(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); }); it('sets correct extent for viewport more than 2 worlds wide', function() { @@ -287,6 +298,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - 2 * worldWidth - 10000, -10000, projExtent[2] + 2 * worldWidth + 10000, 10000 ], buffer)); + expect(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); }); it('sets replayGroupChanged correctly', function() { @@ -319,6 +331,90 @@ describe('ol.renderer.canvas.VectorLayer', function() { }); + describe('#prepareFrame with a loadWrapX: false source', function() { + let frameState, projExtent, renderer, worldWidth, buffer, loadExtent; + const loader = function(extent) { + loadExtent = extent; + } + + beforeEach(function() { + const layer = new VectorLayer({ + source: new VectorSource({ + wrapX: true, + loadWrapX: false, + loader: loader, + strategy: bboxStrategy + }) + }); + renderer = new CanvasVectorLayerRenderer(layer); + const projection = getProjection('EPSG:3857'); + projExtent = projection.getExtent(); + worldWidth = getWidth(projExtent); + buffer = layer.getRenderBuffer(); + loadExtent = undefined; + frameState = { + viewHints: [], + viewState: { + center: [0, 0], + projection: projection, + resolution: 1, + rotation: 0 + } + }; + }); + + it('loads correct extent for small viewport near dateline', function() { + + frameState.extent = + [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(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); + }); + + it('loads correct extent for viewport less than 1 world wide', function() { + + frameState.extent = + [projExtent[0] - 10000, -10000, projExtent[1] - 10000, 10000]; + renderer.prepareFrame(frameState); + expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ + projExtent[0] - worldWidth + buffer, + -10000, projExtent[2] + worldWidth - buffer, 10000 + ], buffer)); + expect(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); + }); + + it('loads correct extent for viewport more than 1 world wide', function() { + + frameState.extent = + [2 * projExtent[0] - 10000, -10000, 2 * projExtent[1] + 10000, 10000]; + renderer.prepareFrame(frameState); + expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ + projExtent[0] - worldWidth + buffer, + -10000, projExtent[2] + worldWidth - buffer, 10000 + ], buffer)); + expect(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); + }); + + it('loads correct extent for viewport more than 2 worlds wide', function() { + + frameState.extent = [ + projExtent[0] - 2 * worldWidth - 10000, + -10000, projExtent[1] + 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(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); + }); + + }); + describe('hit detection', function() { it('with no fill and transparent fill', function() { From 0c9324f398df0150d1afe5f83d8a98c9fa5a0025 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sun, 29 Mar 2020 16:58:57 +0100 Subject: [PATCH 09/24] Test extents passed to loader --- test/spec/ol/renderer/canvas/vectorlayer.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index 575d7c11a8..f163a6b4cf 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -224,7 +224,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { let frameState, projExtent, renderer, worldWidth, buffer, loadExtent; const loader = function(extent) { loadExtent = extent; - } + }; beforeEach(function() { const layer = new VectorLayer({ @@ -335,7 +335,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { let frameState, projExtent, renderer, worldWidth, buffer, loadExtent; const loader = function(extent) { loadExtent = extent; - } + }; beforeEach(function() { const layer = new VectorLayer({ From 190cd202a177b18c31e29acf69e41c0e3dc1ac46 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Sun, 29 Mar 2020 18:14:31 +0200 Subject: [PATCH 10/24] Always use load extent with real world center --- src/ol/layer/Graticule.js | 3 +- src/ol/renderer/canvas/VectorLayer.js | 7 ++- src/ol/source/Vector.js | 18 ------- .../ol/renderer/canvas/vectorlayer.test.js | 49 ++++++++++++------- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index aa13cd8fee..15ba7cd2a1 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -437,8 +437,7 @@ class Graticule extends VectorLayer { features: new Collection(), overlaps: false, useSpatialIndex: false, - wrapX: options.wrapX, - loadWrapX: false + wrapX: options.wrapX }) ); diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index bdfe6c2e5b..beef39b2f3 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -361,7 +361,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const center = viewState.center.slice(); const extent = buffer(frameStateExtent, vectorLayerRenderBuffer * resolution); - let loadExtent = extent.slice(); + const loadExtent = extent.slice(); const projectionExtent = viewState.projection.getExtent(); if (vectorSource.getWrapX() && viewState.projection.canWrapX() && @@ -375,11 +375,10 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const gutter = Math.max(getWidth(extent) / 2, worldWidth); extent[0] = projectionExtent[0] - gutter; extent[2] = projectionExtent[2] + gutter; - if (vectorSource.getLoadWrapX()) { - loadExtent = extent; - } const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); center[0] -= (worldsAway * worldWidth); + loadExtent[0] -= (worldsAway * worldWidth); + loadExtent[2] -= (worldsAway * worldWidth); } if (!this.dirty_ && diff --git a/src/ol/source/Vector.js b/src/ol/source/Vector.js index edf1c4e25d..607f0d3b67 100644 --- a/src/ol/source/Vector.js +++ b/src/ol/source/Vector.js @@ -146,10 +146,6 @@ export class VectorSourceEvent extends Event { * @property {boolean} [wrapX=true] Wrap the world horizontally. For vector editing across the * -180° and 180° meridians to work properly, this should be set to `false`. The * resulting geometry coordinates will then exceed the world bounds. - * @property {boolean} [loadWrapX=true] Call the loader with one world width either side - * of the projection extent when the world is wrapped horizontally. This allows features - * to be loaded in a single request, but may be inefficient. If `false` only the viewport - * extent is used and the loader must determine the appropriate real world requests. */ @@ -190,12 +186,6 @@ class VectorSource extends Source { */ this.format_ = options.format; - /** - * @private - * @type {boolean} - */ - this.loadWrapX_ = options.loadWrapX === undefined ? true : options.loadWrapX; - /** * @private * @type {boolean} @@ -811,14 +801,6 @@ class VectorSource extends Source { } - /** - * @return {boolean} The loadWrapX option used to construct the source. - */ - getLoadWrapX() { - return this.loadWrapX_; - } - - /** * @return {boolean} The source can have overlapping geometries. */ diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index f163a6b4cf..05e7c011b6 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -1,7 +1,7 @@ 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'; @@ -243,7 +243,6 @@ describe('ol.renderer.canvas.VectorLayer', function() { frameState = { viewHints: [], viewState: { - center: [0, 0], projection: projection, resolution: 1, rotation: 0 @@ -251,58 +250,72 @@ 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(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); + expect(loadExtent).to.eql(bufferExtent(frameState.extent, 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(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); + expect(loadExtent).to.eql(bufferExtent(frameState.extent, 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(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); + expect(loadExtent).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(loadExtent).to.eql(renderer.replayGroup_.maxExtent_); + const normalizedExtent = [projExtent[0] - 2 * worldWidth + worldWidth - 10000, -10000, projExtent[0] + 2 * worldWidth + worldWidth + 10000, 10000]; + expect(loadExtent).to.eql(bufferExtent(normalizedExtent, buffer)); + }); + + it('sets correct extent for small viewport near dateline, 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)); + const normalizedExtent = [-10000, -10000, 10000, 10000]; + expect(loadExtent).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); From 3d8495742bc03775c6d1e4bede168947d9a1f02b Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Sun, 29 Mar 2020 19:50:35 +0100 Subject: [PATCH 11/24] Simplify following renderer changes --- src/ol/layer/Graticule.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 15ba7cd2a1..e16cc7d955 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -725,11 +725,7 @@ class Graticule extends VectorLayer { extent[0] = projectionExtent[0]; extent[2] = projectionExtent[2]; } else { - const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); - center[0] -= (worldsAway * worldWidth); - extent[0] -= (worldsAway * worldWidth); - extent[2] -= (worldsAway * worldWidth); - wrapX = !containsExtent(projectionExtent, extent); + wrapX = true; } } From a35794ae973869ea5144c3d822e0a5b3f5642b30 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 30 Mar 2020 12:58:50 +0200 Subject: [PATCH 12/24] Load two extents for views that cross the date line --- src/ol/renderer/canvas/VectorLayer.js | 17 ++- .../ol/renderer/canvas/vectorlayer.test.js | 116 ++++-------------- 2 files changed, 35 insertions(+), 98 deletions(-) diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index beef39b2f3..472333c8c9 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -361,7 +361,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const center = viewState.center.slice(); const extent = buffer(frameStateExtent, vectorLayerRenderBuffer * resolution); - const loadExtent = extent.slice(); + const loadExtents = [extent.slice()]; const projectionExtent = viewState.projection.getExtent(); if (vectorSource.getWrapX() && viewState.projection.canWrapX() && @@ -377,8 +377,15 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { extent[2] = projectionExtent[2] + gutter; const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); center[0] -= (worldsAway * worldWidth); + const loadExtent = loadExtents[0]; loadExtent[0] -= (worldsAway * worldWidth); loadExtent[2] -= (worldsAway * worldWidth); + // 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_ && @@ -401,10 +408,14 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const userProjection = getUserProjection(); let userTransform; if (userProjection) { - vectorSource.loadFeatures(toUserExtent(loadExtent, 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(loadExtent, resolution, projection); + for (let i = 0, ii = loadExtents.length; i < ii; ++i) { + vectorSource.loadFeatures(loadExtents[i], resolution, projection); + } } const squaredTolerance = getSquaredRenderTolerance(resolution, pixelRatio); diff --git a/test/spec/ol/renderer/canvas/vectorlayer.test.js b/test/spec/ol/renderer/canvas/vectorlayer.test.js index 05e7c011b6..f2fabaddd8 100644 --- a/test/spec/ol/renderer/canvas/vectorlayer.test.js +++ b/test/spec/ol/renderer/canvas/vectorlayer.test.js @@ -221,10 +221,11 @@ describe('ol.renderer.canvas.VectorLayer', function() { }); describe('#prepareFrame and #compose', function() { - let frameState, projExtent, renderer, worldWidth, buffer, loadExtent; - const loader = function(extent) { - loadExtent = extent; - }; + let frameState, projExtent, renderer, worldWidth, buffer, loadExtents; + + function loader(extent) { + loadExtents.push(extent); + } beforeEach(function() { const layer = new VectorLayer({ @@ -239,7 +240,7 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent = projection.getExtent(); worldWidth = getWidth(projExtent); buffer = layer.getRenderBuffer(); - loadExtent = undefined; + loadExtents = []; frameState = { viewHints: [], viewState: { @@ -263,7 +264,10 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); - expect(loadExtent).to.eql(bufferExtent(frameState.extent, 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() { @@ -274,7 +278,10 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); - expect(loadExtent).to.eql(bufferExtent(frameState.extent, 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() { @@ -285,7 +292,8 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); - expect(loadExtent).to.eql(bufferExtent(frameState.extent, 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, one world away', function() { @@ -298,11 +306,12 @@ describe('ol.renderer.canvas.VectorLayer', function() { 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(loadExtent).to.eql(bufferExtent(normalizedExtent, buffer)); + expect(loadExtents[0]).to.eql(bufferExtent(normalizedExtent, buffer)); }); - it('sets correct extent for small viewport near dateline, one world away', function() { + it('sets correct extent for small viewport, one world away', function() { setExtent([-worldWidth - 10000, -10000, -worldWidth + 10000, 10000]); renderer.prepareFrame(frameState); @@ -310,8 +319,9 @@ describe('ol.renderer.canvas.VectorLayer', function() { projExtent[0] - worldWidth + buffer, -10000, projExtent[2] + worldWidth - buffer, 10000 ], buffer)); + expect(loadExtents.length).to.be(1); const normalizedExtent = [-10000, -10000, 10000, 10000]; - expect(loadExtent).to.eql(bufferExtent(normalizedExtent, buffer)); + expect(loadExtents[0]).to.eql(bufferExtent(normalizedExtent, buffer)); }); it('sets replayGroupChanged correctly', function() { @@ -344,90 +354,6 @@ describe('ol.renderer.canvas.VectorLayer', function() { }); - describe('#prepareFrame with a loadWrapX: false source', function() { - let frameState, projExtent, renderer, worldWidth, buffer, loadExtent; - const loader = function(extent) { - loadExtent = extent; - }; - - beforeEach(function() { - const layer = new VectorLayer({ - source: new VectorSource({ - wrapX: true, - loadWrapX: false, - loader: loader, - strategy: bboxStrategy - }) - }); - renderer = new CanvasVectorLayerRenderer(layer); - const projection = getProjection('EPSG:3857'); - projExtent = projection.getExtent(); - worldWidth = getWidth(projExtent); - buffer = layer.getRenderBuffer(); - loadExtent = undefined; - frameState = { - viewHints: [], - viewState: { - center: [0, 0], - projection: projection, - resolution: 1, - rotation: 0 - } - }; - }); - - it('loads correct extent for small viewport near dateline', function() { - - frameState.extent = - [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(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); - }); - - it('loads correct extent for viewport less than 1 world wide', function() { - - frameState.extent = - [projExtent[0] - 10000, -10000, projExtent[1] - 10000, 10000]; - renderer.prepareFrame(frameState); - expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ - projExtent[0] - worldWidth + buffer, - -10000, projExtent[2] + worldWidth - buffer, 10000 - ], buffer)); - expect(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); - }); - - it('loads correct extent for viewport more than 1 world wide', function() { - - frameState.extent = - [2 * projExtent[0] - 10000, -10000, 2 * projExtent[1] + 10000, 10000]; - renderer.prepareFrame(frameState); - expect(renderer.replayGroup_.maxExtent_).to.eql(bufferExtent([ - projExtent[0] - worldWidth + buffer, - -10000, projExtent[2] + worldWidth - buffer, 10000 - ], buffer)); - expect(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); - }); - - it('loads correct extent for viewport more than 2 worlds wide', function() { - - frameState.extent = [ - projExtent[0] - 2 * worldWidth - 10000, - -10000, projExtent[1] + 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(loadExtent).to.eql(bufferExtent(frameState.extent, buffer)); - }); - - }); - describe('hit detection', function() { it('with no fill and transparent fill', function() { From 48b79cf7d1c369d39959c561a61915bc3408e1de Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 30 Mar 2020 14:51:27 +0100 Subject: [PATCH 13/24] only use one extent if two are passed --- src/ol/layer/Graticule.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index e16cc7d955..7a991858dc 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -479,11 +479,25 @@ class Graticule extends VectorLayer { * @return {Array} Extents. */ strategyFunction(extent, resolution) { - if (this.loadedExtent_ && !equals(this.loadedExtent_, extent)) { + // extents may be passed in different worlds, to avoid endless loop we use only one + const realExtent = extent.slice(); + if (this.projection_) { + const center = getCenter(extent); + const projectionExtent = this.projection_.getExtent(); + const worldWidth = getWidth(projectionExtent); + if (this.getSource().getWrapX() && this.projection_.canWrapX() && !containsExtent(projectionExtent, extent)) { + const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); + realExtent[0] -= (worldsAway * worldWidth); + realExtent[2] -= (worldsAway * worldWidth); + } + realExtent[0] = Math.round(realExtent[0] * 1e6) / 1e6; + realExtent[2] = Math.round(realExtent[2] * 1e6) / 1e6; + } + if (this.loadedExtent_ && !equals(this.loadedExtent_, realExtent)) { // we should not keep track of loaded extents this.getSource().removeLoadedExtent(this.loadedExtent_); } - return [extent]; + return [realExtent]; } /** From 098885a006244d0c42a4f6683e665a4a07e99ee6 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Mon, 30 Mar 2020 19:26:26 +0200 Subject: [PATCH 14/24] New wrapX functions for coordinate and extent --- src/ol/coordinate.js | 20 +++++++++++++ src/ol/extent.js | 22 ++++++++++++++ src/ol/layer/Graticule.js | 19 ++++-------- src/ol/renderer/Map.js | 14 +++------ src/ol/renderer/canvas/VectorLayer.js | 13 ++++----- src/ol/renderer/canvas/VectorTileLayer.js | 5 ++-- test/spec/ol/coordinate.test.js | 28 +++++++++++++++++- test/spec/ol/extent.test.js | 35 ++++++++++++++++++++++- 8 files changed, 121 insertions(+), 35 deletions(-) diff --git a/src/ol/coordinate.js b/src/ol/coordinate.js index 177c8bce55..fb375162b9 100644 --- a/src/ol/coordinate.js +++ b/src/ol/coordinate.js @@ -3,6 +3,7 @@ */ import {modulo} from './math.js'; import {padNumber} from './string.js'; +import {getWidth} from './extent.js'; /** @@ -402,3 +403,22 @@ 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. + * + * @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; +} diff --git a/src/ol/extent.js b/src/ol/extent.js index 94eb9cb249..ef969a196a 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -813,3 +813,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; +} diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 7a991858dc..4e4023fd68 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -24,7 +24,9 @@ import { getIntersection, getWidth, intersects, - isEmpty + isEmpty, + buffer as bufferExtent, + wrapX } from '../extent.js'; import {clamp} from '../math.js'; import Style from '../style/Style.js'; @@ -481,19 +483,10 @@ class Graticule extends VectorLayer { strategyFunction(extent, resolution) { // extents may be passed in different worlds, to avoid endless loop we use only one const realExtent = extent.slice(); - if (this.projection_) { - const center = getCenter(extent); - const projectionExtent = this.projection_.getExtent(); - const worldWidth = getWidth(projectionExtent); - if (this.getSource().getWrapX() && this.projection_.canWrapX() && !containsExtent(projectionExtent, extent)) { - const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth); - realExtent[0] -= (worldsAway * worldWidth); - realExtent[2] -= (worldsAway * worldWidth); - } - realExtent[0] = Math.round(realExtent[0] * 1e6) / 1e6; - realExtent[2] = Math.round(realExtent[2] * 1e6) / 1e6; + if (this.projection_ && this.getSource().getWrapX()) { + wrapX(realExtent, this.projection_); } - if (this.loadedExtent_ && !equals(this.loadedExtent_, realExtent)) { + if (this.loadedExtent_ && !containsExtent(bufferExtent(this.loadedExtent_, resolution / 2), realExtent)) { // we should not keep track of loaded extents this.getSource().removeLoadedExtent(this.loadedExtent_); } diff --git a/src/ol/renderer/Map.js b/src/ol/renderer/Map.js index 591e96534f..86ee999a81 100644 --- a/src/ol/renderer/Map.js +++ b/src/ol/renderer/Map.js @@ -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; diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 472333c8c9..0e2bebf908 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -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'; @@ -364,7 +365,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const loadExtents = [extent.slice()]; const projectionExtent = viewState.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° @@ -375,11 +376,9 @@ 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); - const loadExtent = loadExtents[0]; - loadExtent[0] -= (worldsAway * worldWidth); - loadExtent[2] -= (worldsAway * worldWidth); + wrapCoordinateX(center, projection); + const loadExtent = wrapExtentX(loadExtents[0], projection); + wrapExtentX(loadExtent, 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]]); diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index 9c0974fa91..acdd1198d6 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.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; } diff --git a/test/spec/ol/coordinate.test.js b/test/spec/ol/coordinate.test.js index 23d18509fc..90b6858718 100644 --- a/test/spec/ol/coordinate.test.js +++ b/test/spec/ol/coordinate.test.js @@ -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]); + }); + + }); + }); diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 3e12639d19..2bad7e640a 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -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,37 @@ 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]); + }); + + }); + }); From 660845f5b85a47c4546abe8c2cf54d2be95be912 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 30 Mar 2020 23:17:52 +0100 Subject: [PATCH 15/24] Include center at right edge in calculations --- src/ol/extent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index ef969a196a..c68f9c9ff9 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -826,7 +826,7 @@ export function applyTransform(extent, transformFn, opt_extent, opt_stops) { export function wrapX(extent, projection) { const projectionExtent = projection.getExtent(); const center = getCenter(extent); - if (projection.canWrapX() && (center[0] < projectionExtent[0] || center[0] > projectionExtent[2])) { + 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); From e3ad05f805d06d37df95437337280898b3093d26 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 30 Mar 2020 23:18:52 +0100 Subject: [PATCH 16/24] Include center at right edge in calculations --- src/ol/coordinate.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/coordinate.js b/src/ol/coordinate.js index fb375162b9..79d196a911 100644 --- a/src/ol/coordinate.js +++ b/src/ol/coordinate.js @@ -415,7 +415,7 @@ export function toStringXY(coordinate, opt_fractionDigits) { */ export function wrapX(coordinate, projection) { const projectionExtent = projection.getExtent(); - if (projection.canWrapX() && (coordinate[0] < projectionExtent[0] || coordinate[0] > projectionExtent[2])) { + 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); From 99a1641afe7296a81d9f86f70cc88e75c4d7cc50 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 30 Mar 2020 23:31:30 +0100 Subject: [PATCH 17/24] remove duplication --- src/ol/renderer/canvas/VectorLayer.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ol/renderer/canvas/VectorLayer.js b/src/ol/renderer/canvas/VectorLayer.js index 0e2bebf908..0d7af8e81b 100644 --- a/src/ol/renderer/canvas/VectorLayer.js +++ b/src/ol/renderer/canvas/VectorLayer.js @@ -363,7 +363,7 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { const extent = buffer(frameStateExtent, vectorLayerRenderBuffer * resolution); const loadExtents = [extent.slice()]; - const projectionExtent = viewState.projection.getExtent(); + const projectionExtent = projection.getExtent(); if (vectorSource.getWrapX() && projection.canWrapX() && !containsExtent(projectionExtent, frameState.extent)) { @@ -378,7 +378,6 @@ class CanvasVectorLayerRenderer extends CanvasLayerRenderer { extent[2] = projectionExtent[2] + gutter; wrapCoordinateX(center, projection); const loadExtent = wrapExtentX(loadExtents[0], projection); - wrapExtentX(loadExtent, 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]]); From 6013763480a60af8144915559856653ddb7493a2 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 30 Mar 2020 23:43:26 +0100 Subject: [PATCH 18/24] replace containsExtent with equals in strategy --- src/ol/layer/Graticule.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 4e4023fd68..f66cf5732d 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -25,8 +25,7 @@ import { getWidth, intersects, isEmpty, - buffer as bufferExtent, - wrapX + wrapX as wrapExtentX } from '../extent.js'; import {clamp} from '../math.js'; import Style from '../style/Style.js'; @@ -484,9 +483,11 @@ class Graticule extends VectorLayer { // extents may be passed in different worlds, to avoid endless loop we use only one const realExtent = extent.slice(); if (this.projection_ && this.getSource().getWrapX()) { - wrapX(realExtent, this.projection_); + wrapExtentX(realExtent, this.projection_); } - if (this.loadedExtent_ && !containsExtent(bufferExtent(this.loadedExtent_, resolution / 2), realExtent)) { + realExtent[0] = Math.round(realExtent[0] * 1e8) / 1e8; + realExtent[2] = Math.round(realExtent[2] * 1e8) / 1e8; + if (this.loadedExtent_ && !equals(this.loadedExtent_, realExtent)) { // we should not keep track of loaded extents this.getSource().removeLoadedExtent(this.loadedExtent_); } From 2c7f58dbed352f3025deec869272561b73da974b Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Mon, 30 Mar 2020 23:51:06 +0100 Subject: [PATCH 19/24] remove unused import --- src/ol/renderer/canvas/VectorTileLayer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/renderer/canvas/VectorTileLayer.js b/src/ol/renderer/canvas/VectorTileLayer.js index acdd1198d6..a3520f9caa 100644 --- a/src/ol/renderer/canvas/VectorTileLayer.js +++ b/src/ol/renderer/canvas/VectorTileLayer.js @@ -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'; From cdafc4fa05515ceddf0e87d7969c0ff7635f21fe Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Tue, 31 Mar 2020 14:52:02 +0200 Subject: [PATCH 20/24] Add approximatelyEquals function for comparing extents --- src/ol/extent.js | 12 ++++++++++++ src/ol/layer/Graticule.js | 14 ++++++-------- test/spec/ol/extent.test.js | 9 +++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/ol/extent.js b/src/ol/extent.js index c68f9c9ff9..4679d49820 100644 --- a/src/ol/extent.js +++ b/src/ol/extent.js @@ -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. diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index f66cf5732d..6fc590283d 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -18,7 +18,7 @@ import { applyTransform, containsCoordinate, containsExtent, - equals, + approximatelyEquals, getCenter, getHeight, getIntersection, @@ -481,17 +481,15 @@ class Graticule extends VectorLayer { */ strategyFunction(extent, resolution) { // extents may be passed in different worlds, to avoid endless loop we use only one - const realExtent = extent.slice(); + const realWorldExtent = extent.slice(); if (this.projection_ && this.getSource().getWrapX()) { - wrapExtentX(realExtent, this.projection_); + wrapExtentX(realWorldExtent, this.projection_); } - realExtent[0] = Math.round(realExtent[0] * 1e8) / 1e8; - realExtent[2] = Math.round(realExtent[2] * 1e8) / 1e8; - if (this.loadedExtent_ && !equals(this.loadedExtent_, realExtent)) { + if (this.loadedExtent_ && !approximatelyEquals(this.loadedExtent_, realWorldExtent, resolution)) { // we should not keep track of loaded extents this.getSource().removeLoadedExtent(this.loadedExtent_); } - return [realExtent]; + return [realWorldExtent]; } /** @@ -508,7 +506,7 @@ class Graticule extends VectorLayer { const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity]; const renderExtent = getIntersection(layerExtent, extent); - if (this.renderedExtent_ && equals(this.renderedExtent_, renderExtent)) { + if (this.renderedExtent_ && approximatelyEquals(this.renderedExtent_, renderExtent, resolution)) { return; } this.renderedExtent_ = renderExtent; diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index 2bad7e640a..ba971781da 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -851,4 +851,13 @@ describe('ol.extent', function() { }); + 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); + }); + }); + }); From 9af1e223afc38a8bc0c93b0f48c42afcb1cc2406 Mon Sep 17 00:00:00 2001 From: Andreas Hocevar Date: Tue, 31 Mar 2020 15:52:29 +0200 Subject: [PATCH 21/24] More tests and docs for extent and coordinate wrapX --- src/ol/coordinate.js | 3 ++- test/spec/ol/extent.test.js | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/ol/coordinate.js b/src/ol/coordinate.js index 79d196a911..563ff1bf64 100644 --- a/src/ol/coordinate.js +++ b/src/ol/coordinate.js @@ -407,7 +407,8 @@ export function toStringXY(coordinate, opt_fractionDigits) { /** * Modifies the provided coordinate in-place to be within the real world - * extent. + * extent. The lower projection extent boundary is inclusive, the upper one + * exclusive. * * @param {Coordinate} coordinate Coordinate. * @param {import("./proj/Projection.js").default} projection Projection diff --git a/test/spec/ol/extent.test.js b/test/spec/ol/extent.test.js index ba971781da..cc9011175a 100644 --- a/test/spec/ol/extent.test.js +++ b/test/spec/ol/extent.test.js @@ -849,6 +849,12 @@ describe('ol.extent', 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() { From 149ca7efad89d185344e876a0bc485e65e5c1b33 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Tue, 31 Mar 2020 16:26:30 +0100 Subject: [PATCH 22/24] return previous extent if extents are approx equal --- src/ol/layer/Graticule.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 6fc590283d..3994cd11a1 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -481,13 +481,18 @@ class Graticule extends VectorLayer { */ strategyFunction(extent, resolution) { // extents may be passed in different worlds, to avoid endless loop we use only one - const realWorldExtent = extent.slice(); + let realWorldExtent = extent.slice(); if (this.projection_ && this.getSource().getWrapX()) { wrapExtentX(realWorldExtent, this.projection_); } - if (this.loadedExtent_ && !approximatelyEquals(this.loadedExtent_, realWorldExtent, resolution)) { - // we should not keep track of loaded extents - this.getSource().removeLoadedExtent(this.loadedExtent_); + 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]; } From 929b9f4068ee576e0902a794053c94cae19b818f Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Tue, 31 Mar 2020 16:55:23 +0100 Subject: [PATCH 23/24] change loader check back to equal extents --- src/ol/layer/Graticule.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 3994cd11a1..691637b6ae 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -18,6 +18,7 @@ import { applyTransform, containsCoordinate, containsExtent, + equals, approximatelyEquals, getCenter, getHeight, @@ -511,7 +512,7 @@ class Graticule extends VectorLayer { const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity]; const renderExtent = getIntersection(layerExtent, extent); - if (this.renderedExtent_ && approximatelyEquals(this.renderedExtent_, renderExtent, resolution)) { + if (this.renderedExtent_ && equals(this.renderedExtent_, renderExtent, resolution)) { return; } this.renderedExtent_ = renderExtent; From bfca3cf713e50ab3784f7889209daefe2865e711 Mon Sep 17 00:00:00 2001 From: mike-000 <49240900+mike-000@users.noreply.github.com> Date: Tue, 31 Mar 2020 17:00:12 +0100 Subject: [PATCH 24/24] change loader check back to equal extents --- src/ol/layer/Graticule.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ol/layer/Graticule.js b/src/ol/layer/Graticule.js index 691637b6ae..6051a608aa 100644 --- a/src/ol/layer/Graticule.js +++ b/src/ol/layer/Graticule.js @@ -512,7 +512,7 @@ class Graticule extends VectorLayer { const layerExtent = this.getExtent() || [-Infinity, -Infinity, Infinity, Infinity]; const renderExtent = getIntersection(layerExtent, extent); - if (this.renderedExtent_ && equals(this.renderedExtent_, renderExtent, resolution)) { + if (this.renderedExtent_ && equals(this.renderedExtent_, renderExtent)) { return; } this.renderedExtent_ = renderExtent;