diff --git a/src/ol/View.js b/src/ol/View.js index 81c5aa07e5..e114b8e715 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -1217,6 +1217,23 @@ class View extends BaseObject { endInteraction() { this.setHint(ViewHint.INTERACTING, -1); } + + /** + * Get a valid zoom level according to the current view constraints. + * @param {number|undefined} targetZoom Target resolution. + * @param {number=} opt_direction Direction. Default is `0`. Specify `-1` or `1` to return + * the available value respectively lower or greater than the target one. Leaving `0` will simply choose + * the nearest available value. + * @return {number|undefined} Valid zoom level. + * @api + */ + getValidZoomLevel(targetZoom, opt_direction) { + const direction = opt_direction || 0; + const currentRes = this.getResolution(); + const currentZoom = this.getZoom(); + return this.getZoomForResolution( + this.constraints_.resolution(currentRes, targetZoom - currentZoom, direction)); + } } diff --git a/src/ol/control/Zoom.js b/src/ol/control/Zoom.js index 5e016950a9..f84254f904 100644 --- a/src/ol/control/Zoom.js +++ b/src/ol/control/Zoom.js @@ -114,20 +114,20 @@ class Zoom extends Control { // upon it return; } - const currentResolution = view.getResolution(); - if (currentResolution) { - const newResolution = view.constrainResolution(currentResolution, delta); + const currentZoom = view.getZoom(); + if (currentZoom !== undefined) { + const newZoom = view.getValidZoomLevel(currentZoom + delta); if (this.duration_ > 0) { if (view.getAnimating()) { view.cancelAnimations(); } view.animate({ - resolution: newResolution, + zoom: newZoom, duration: this.duration_, easing: easeOut }); } else { - view.setResolution(newResolution); + view.setZoom(newZoom); } } } diff --git a/src/ol/control/ZoomSlider.js b/src/ol/control/ZoomSlider.js index d46ae6eff9..124cf2130a 100644 --- a/src/ol/control/ZoomSlider.js +++ b/src/ol/control/ZoomSlider.js @@ -218,9 +218,10 @@ class ZoomSlider extends Control { event.offsetY - this.thumbSize_[1] / 2); const resolution = this.getResolutionForPosition_(relativePosition); + const zoom = view.getValidZoomLevel(view.getZoomForResolution(resolution)); view.animate({ - resolution: view.constrainResolution(resolution), + zoom: zoom, duration: this.duration_, easing: easeOut }); @@ -281,8 +282,11 @@ class ZoomSlider extends Control { const view = this.getMap().getView(); view.endInteraction(); + const zoom = view.getValidZoomLevel( + view.getZoomForResolution(this.currentResolution_)); + view.animate({ - resolution: view.constrainResolution(this.currentResolution_), + zoom: zoom, duration: this.duration_, easing: easeOut }); diff --git a/src/ol/interaction/DragZoom.js b/src/ol/interaction/DragZoom.js index 83b70c5b47..406e9c8419 100644 --- a/src/ol/interaction/DragZoom.js +++ b/src/ol/interaction/DragZoom.js @@ -80,14 +80,14 @@ function onBoxEnd() { extent = mapExtent; } - const resolution = view.constrainResolution( - view.getResolutionForExtent(extent, size)); + const resolution = view.getResolutionForExtent(extent, size); + const zoom = view.getValidZoomLevel(view.getZoomForResolution(resolution)); let center = getCenter(extent); center = view.constrainCenter(center); view.animate({ - resolution: resolution, + zoom: zoom, center: center, duration: this.duration_, easing: easeOut diff --git a/src/ol/interaction/Interaction.js b/src/ol/interaction/Interaction.js index 6a13a03857..09eb70a397 100644 --- a/src/ol/interaction/Interaction.js +++ b/src/ol/interaction/Interaction.js @@ -189,34 +189,49 @@ export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction) * @param {number=} opt_duration Duration. */ export function zoomByDelta(view, delta, opt_anchor, opt_duration) { + const currentZoom = view.getZoom(); const currentResolution = view.getResolution(); - let resolution = view.constrainResolution(currentResolution, delta, 0); - if (resolution !== undefined) { - const resolutions = view.getResolutions(); - resolution = clamp( - resolution, - view.getMinResolution() || resolutions[resolutions.length - 1], - view.getMaxResolution() || resolutions[0]); + if (currentZoom === undefined) { + return; } + const newZoom = view.getValidZoomLevel(currentZoom + delta); + const newResolution = view.getResolutionForZoom(newZoom); + // If we have a constraint on center, we need to change the anchor so that the // new center is within the extent. We first calculate the new center, apply // the constraint to it, and then calculate back the anchor - if (opt_anchor && resolution !== undefined && resolution !== currentResolution) { + if (opt_anchor) { const currentCenter = view.getCenter(); - let center = view.calculateCenterZoom(resolution, opt_anchor); + let center = view.calculateCenterZoom(newResolution, opt_anchor); center = view.constrainCenter(center); opt_anchor = [ - (resolution * currentCenter[0] - currentResolution * center[0]) / - (resolution - currentResolution), - (resolution * currentCenter[1] - currentResolution * center[1]) / - (resolution - currentResolution) + (newResolution * currentCenter[0] - currentResolution * center[0]) / + (newResolution - currentResolution), + (newResolution * currentCenter[1] - currentResolution * center[1]) / + (newResolution - currentResolution) ]; } - zoomWithoutConstraints(view, resolution, opt_anchor, opt_duration); + if (opt_duration > 0) { + if (view.getAnimating()) { + view.cancelAnimations(); + } + view.animate({ + resolution: newResolution, + anchor: opt_anchor, + duration: opt_duration, + easing: easeOut + }); + } else { + if (opt_anchor) { + const center = view.calculateCenterZoom(newResolution, opt_anchor); + view.setCenter(center); + } + view.setResolution(newResolution); + } } diff --git a/src/ol/interaction/MouseWheelZoom.js b/src/ol/interaction/MouseWheelZoom.js index e9fb98c8af..24d29fd099 100644 --- a/src/ol/interaction/MouseWheelZoom.js +++ b/src/ol/interaction/MouseWheelZoom.js @@ -239,8 +239,10 @@ class MouseWheelZoom extends Interaction { view.setResolution(resolution); if (rebound === 0 && this.constrainResolution_) { + const zoomDelta = delta > 0 ? -1 : 1; + const newZoom = view.getValidZoomLevel(view.getZoom() + zoomDelta); view.animate({ - resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1), + resolution: view.getResolutionForZoom(newZoom), easing: easeOut, anchor: this.lastAnchor_, duration: this.duration_ diff --git a/test/spec/ol/view.test.js b/test/spec/ol/view.test.js index fed967810b..926748e6fa 100644 --- a/test/spec/ol/view.test.js +++ b/test/spec/ol/view.test.js @@ -1439,6 +1439,35 @@ describe('ol.View', function() { expect(view.getHints()[1]).to.be(0); }); }); + + describe('#getValidZoomLevel()', function() { + let view; + + it('works correctly without constraint', function() { + view = new View({ + zoom: 0 + }); + expect(view.getValidZoomLevel(3)).to.be(3); + }); + it('works correctly with resolution constraints', function() { + view = new View({ + zoom: 4, + minZoom: 4, + maxZoom: 8 + }); + expect(view.getValidZoomLevel(3)).to.be(4); + expect(view.getValidZoomLevel(10)).to.be(8); + }); + it('works correctly with a specific resolution set', function() { + view = new View({ + zoom: 0, + resolutions: [512, 256, 128, 64, 32, 16, 8] + }); + expect(view.getValidZoomLevel(0)).to.be(0); + expect(view.getValidZoomLevel(4)).to.be(4); + expect(view.getValidZoomLevel(8)).to.be(6); + }); + }); }); describe('ol.View.isNoopAnimation()', function() {