From 217c6ba7647b81cf42d9f38cc880106afab878f5 Mon Sep 17 00:00:00 2001 From: Tim Schaub Date: Tue, 14 May 2019 19:34:03 -0600 Subject: [PATCH] Avoid panning off the edge of the world --- changelog/upgrade-notes.md | 4 +- .../layer-vector-polygon-partial/main.js | 3 +- src/ol/View.js | 16 +++++-- test/spec/ol/view.test.js | 46 +++++++++++++++++-- 4 files changed, 59 insertions(+), 10 deletions(-) diff --git a/changelog/upgrade-notes.md b/changelog/upgrade-notes.md index d150d3c48a..8645323f93 100644 --- a/changelog/upgrade-notes.md +++ b/changelog/upgrade-notes.md @@ -32,9 +32,9 @@ Previously, this options only constrained the view *center*. This behaviour can As a side effect, the view `rotate` method is gone and has been replaced with `adjustRotation` which takes a delta as input. -##### Zoom is constrained so only one world is visible +##### The view is constrained so only one world is visible -Previously, maps showed multiple worlds at low zoom levels. Now, the view is restricted to show only one world. To get the previous behavior, configure the `ol/View` with `multiWorld: true`. +Previously, maps showed multiple worlds at low zoom levels. In addition, it used to be possible to pan off the north or south edge of the world. Now, the view is restricted to show only one world, and you cannot pan off the edge. To get the previous behavior, configure the `ol/View` with `multiWorld: true`. ##### Removal of deprecated methods diff --git a/rendering/cases/layer-vector-polygon-partial/main.js b/rendering/cases/layer-vector-polygon-partial/main.js index 8370649101..b953ea3130 100644 --- a/rendering/cases/layer-vector-polygon-partial/main.js +++ b/rendering/cases/layer-vector-polygon-partial/main.js @@ -42,7 +42,8 @@ const layer = new VectorLayer({ const view = new View({ center: [-9.5, 78], zoom: 2, - projection: 'EPSG:4326' + projection: 'EPSG:4326', + multiWorld: true }); new Map({ pixelRatio: 1, diff --git a/src/ol/View.js b/src/ol/View.js index 6e0a44d41a..fd00409b04 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -1409,11 +1409,19 @@ function animationCallback(callback, returnValue) { */ export function createCenterConstraint(options) { if (options.extent !== undefined) { - return createExtent(options.extent, options.constrainOnlyCenter, - options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true); - } else { - return centerNone; + const smooth = options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true; + return createExtent(options.extent, options.constrainOnlyCenter, smooth); } + + const projection = createProjection(options.projection, 'EPSG:3857'); + if (options.multiWorld !== true && projection.isGlobal()) { + const extent = projection.getExtent().slice(); + extent[0] = -Infinity; + extent[2] = Infinity; + return createExtent(extent, false, false); + } + + return centerNone; } diff --git a/test/spec/ol/view.test.js b/test/spec/ol/view.test.js index 054fc0e4bc..d4bf59b078 100644 --- a/test/spec/ol/view.test.js +++ b/test/spec/ol/view.test.js @@ -100,10 +100,50 @@ describe('ol.View', function() { describe('with no options', function() { it('gives a correct center constraint function', function() { const options = {}; + const size = [512, 256]; + const resolution = 1e5; const fn = createCenterConstraint(options); - expect(fn([0, 0])).to.eql([0, 0]); - expect(fn(undefined)).to.eql(undefined); - expect(fn([42, -100])).to.eql([42, -100]); + expect(fn([0, 0], resolution, size)).to.eql([0, 0]); + expect(fn([42, -100], resolution, size)).to.eql([42, -100]); + }); + }); + + describe('panning off the edge of the world', function() { + it('disallows going north off the world', function() { + const options = { + projection: 'EPSG:4326' + }; + const size = [360, 180]; + const resolution = 0.5; + const fn = createCenterConstraint(options); + expect(fn([0, 0], resolution, size)).to.eql([0, 0]); + expect(fn([0, 60], resolution, size)).to.eql([0, 45]); + expect(fn([180, 60], resolution, size)).to.eql([180, 45]); + expect(fn([-180, 60], resolution, size)).to.eql([-180, 45]); + }); + + it('disallows going south off the world', function() { + const options = { + projection: 'EPSG:4326' + }; + const size = [360, 180]; + const resolution = 0.5; + const fn = createCenterConstraint(options); + expect(fn([0, 0], resolution, size)).to.eql([0, 0]); + expect(fn([0, -60], resolution, size)).to.eql([0, -45]); + expect(fn([180, -60], resolution, size)).to.eql([180, -45]); + expect(fn([-180, -60], resolution, size)).to.eql([-180, -45]); + }); + }); + + describe('with multiWorld: true', function() { + it('gives a correct center constraint function', function() { + const options = {multiWorld: true}; + const size = [512, 256]; + const resolution = 1e5; + const fn = createCenterConstraint(options); + expect(fn([0, 0], resolution, size)).to.eql([0, 0]); + expect(fn([42, -100], resolution, size)).to.eql([42, -100]); }); });