From e023c144bb4d43ed517b711d82a62ddb3466f6ab Mon Sep 17 00:00:00 2001 From: Olivier Guyot Date: Tue, 29 Jan 2019 12:13:27 +0100 Subject: [PATCH] View / add adjust* methods to manipulate the view more easily API changes: * (breaking) the `rotate` method is gone * the `adjustRotation`, `adjustResolution` and `adjustZoom` methods are now available and allow using an anchor. This means interactions do not have to do the anchor computation themselves and this also fix anchor computation when constraints must be applied. --- src/ol/View.js | 67 ++++++-- src/ol/interaction/DragRotate.js | 3 +- src/ol/interaction/DragRotateAndZoom.js | 10 +- src/ol/interaction/Interaction.js | 100 ++---------- src/ol/interaction/MouseWheelZoom.js | 44 +----- src/ol/interaction/PinchRotate.js | 3 +- src/ol/interaction/PinchZoom.js | 15 +- .../ol/interaction/dragrotateandzoom.test.js | 24 +-- test/spec/ol/interaction/interaction.test.js | 108 ------------- test/spec/ol/view.test.js | 147 ++++++++++++++++++ 10 files changed, 237 insertions(+), 284 deletions(-) diff --git a/src/ol/View.js b/src/ol/View.js index 44a8570620..380196154d 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -635,10 +635,10 @@ class View extends BaseObject { */ calculateCenterRotate(rotation, anchor) { let center; - const currentCenter = this.targetCenter_; + const currentCenter = this.getCenter(); if (currentCenter !== undefined) { center = [currentCenter[0] - anchor[0], currentCenter[1] - anchor[1]]; - rotateCoordinate(center, rotation - this.targetRotation_); + rotateCoordinate(center, rotation - this.getRotation()); addCoordinate(center, anchor); } return center; @@ -651,8 +651,8 @@ class View extends BaseObject { */ calculateCenterZoom(resolution, anchor) { let center; - const currentCenter = this.targetCenter_; - const currentResolution = this.targetResolution_; + const currentCenter = this.getCenter(); + const currentResolution = this.getResolution(); if (currentCenter !== undefined && currentResolution !== undefined) { const x = anchor[0] - resolution * (anchor[0] - currentCenter[0]) / currentResolution; const y = anchor[1] - resolution * (anchor[1] - currentCenter[1]) / currentResolution; @@ -1123,17 +1123,50 @@ class View extends BaseObject { } /** - * Rotate the view around a given coordinate. - * @param {number} rotation New rotation value for the view. - * @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center. + * Multiply the view resolution by a ratio, optionally using an anchor. + * @param {number} ratio The ratio to apply on the view resolution. + * @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation. + * @observable * @api */ - rotate(rotation, opt_anchor) { + adjustResolution(ratio, opt_anchor) { + const isMoving = this.getAnimating() || this.getInteracting(); + const size = this.getSizeFromViewport_(this.getRotation()); + const newResolution = this.constraints_.resolution(this.targetResolution_ * ratio, 0, size, isMoving); + if (opt_anchor !== undefined) { - const center = this.calculateCenterRotate(rotation, opt_anchor); - this.setCenter(center); + this.targetCenter_ = this.calculateCenterZoom(newResolution, opt_anchor); } - this.setRotation(rotation); + + this.targetResolution_ *= ratio; + this.applyParameters_(); + } + + /** + * Adds a value to the view zoom level, optionally using an anchor. + * @param {number} delta Relative value to add to the zoom level. + * @param {import("./coordinate.js").Coordinate=} opt_anchor The origin of the transformation. + * @api + */ + adjustZoom(delta, opt_anchor) { + this.adjustResolution(Math.pow(this.zoomFactor_, -delta), opt_anchor); + } + + /** + * Adds a value to the view rotation, optionally using an anchor. + * @param {number} delta Relative value to add to the zoom rotation, in radians. + * @param {import("./coordinate.js").Coordinate=} opt_anchor The rotation center. + * @observable + * @api + */ + adjustRotation(delta, opt_anchor) { + const isMoving = this.getAnimating() || this.getInteracting(); + const newRotation = this.constraints_.rotation(this.targetRotation_ + delta, isMoving); + if (opt_anchor !== undefined) { + this.targetCenter_ = this.calculateCenterRotate(newRotation, opt_anchor); + } + this.targetRotation_ += delta; + this.applyParameters_(); } /** @@ -1206,9 +1239,15 @@ class View extends BaseObject { const newResolution = this.constraints_.resolution(this.targetResolution_, 0, size, isMoving); const newCenter = this.constraints_.center(this.targetCenter_, newResolution, size, isMoving); - this.set(ViewProperty.ROTATION, newRotation); - this.set(ViewProperty.RESOLUTION, newResolution); - this.set(ViewProperty.CENTER, newCenter); + if (this.get(ViewProperty.ROTATION) !== newRotation) { + this.set(ViewProperty.ROTATION, newRotation); + } + if (this.get(ViewProperty.RESOLUTION) !== newResolution) { + this.set(ViewProperty.RESOLUTION, newResolution); + } + if (!this.get(ViewProperty.CENTER) || !equals(this.get(ViewProperty.CENTER), newCenter)) { + this.set(ViewProperty.CENTER, newCenter); + } if (this.getAnimating() && !opt_doNotCancelAnims) { this.cancelAnimations(); diff --git a/src/ol/interaction/DragRotate.js b/src/ol/interaction/DragRotate.js index 7d4d9a4be2..c7c57fead9 100644 --- a/src/ol/interaction/DragRotate.js +++ b/src/ol/interaction/DragRotate.js @@ -80,8 +80,7 @@ class DragRotate extends PointerInteraction { Math.atan2(size[1] / 2 - offset[1], offset[0] - size[0] / 2); if (this.lastAngle_ !== undefined) { const delta = theta - this.lastAngle_; - const rotation = view.getRotation(); - rotate(view, rotation - delta); + view.adjustRotation(-delta); } this.lastAngle_ = theta; } diff --git a/src/ol/interaction/DragRotateAndZoom.js b/src/ol/interaction/DragRotateAndZoom.js index 3f3d4d62c3..ab229393fe 100644 --- a/src/ol/interaction/DragRotateAndZoom.js +++ b/src/ol/interaction/DragRotateAndZoom.js @@ -4,7 +4,6 @@ import {disable} from '../rotationconstraint.js'; import ViewHint from '../ViewHint.js'; import {shiftKeyOnly, mouseOnly} from '../events/condition.js'; -import {rotate, zoom} from './Interaction.js'; import PointerInteraction from './Pointer.js'; @@ -88,14 +87,13 @@ class DragRotateAndZoom extends PointerInteraction { const theta = Math.atan2(deltaY, deltaX); const magnitude = Math.sqrt(deltaX * deltaX + deltaY * deltaY); const view = map.getView(); - if (view.getConstraints().rotation !== disable && this.lastAngle_ !== undefined) { - const angleDelta = theta - this.lastAngle_; - rotate(view, view.getRotation() - angleDelta); + if (this.lastAngle_ !== undefined) { + const angleDelta = this.lastAngle_ - theta; + view.adjustRotation(angleDelta); } this.lastAngle_ = theta; if (this.lastMagnitude_ !== undefined) { - const resolution = this.lastMagnitude_ * (view.getResolution() / magnitude); - zoom(view, resolution); + view.adjustResolution(this.lastMagnitude_ / magnitude); } if (this.lastMagnitude_ !== undefined) { this.lastScaleDelta_ = this.lastMagnitude_ / magnitude; diff --git a/src/ol/interaction/Interaction.js b/src/ol/interaction/Interaction.js index b2897c8658..c48921d7ca 100644 --- a/src/ol/interaction/Interaction.js +++ b/src/ol/interaction/Interaction.js @@ -111,80 +111,14 @@ export function pan(view, delta, opt_duration) { const currentCenter = view.getCenter(); if (currentCenter) { const center = [currentCenter[0] + delta[0], currentCenter[1] + delta[1]]; - if (opt_duration) { - view.animate({ - duration: opt_duration, - easing: linear, - center: center - }); - } else { - view.setCenter(center); - } + view.animate({ + duration: opt_duration !== undefined ? opt_duration : 250, + easing: linear, + center: center + }); } } - -/** - * @param {import("../View.js").default} view View. - * @param {number|undefined} rotation Rotation. - * @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - */ -export function rotate(view, rotation, opt_anchor, opt_duration) { - if (rotation !== undefined) { - const currentRotation = view.getRotation(); - const currentCenter = view.getCenter(); - if (currentRotation !== undefined && currentCenter && opt_duration > 0) { - view.animate({ - rotation: rotation, - anchor: opt_anchor, - duration: opt_duration, - easing: easeOut - }); - } else { - view.rotate(rotation, opt_anchor); - } - } -} - - -/** - * @param {import("../View.js").default} view View. - * @param {number|undefined} resolution Resolution to go to. - * @param {import("../coordinate.js").Coordinate=} opt_anchor Anchor coordinate. - * @param {number=} opt_duration Duration. - * @param {number=} opt_direction Zooming direction; > 0 indicates - * zooming out, in which case the constraints system will select - * the largest nearest resolution; < 0 indicates zooming in, in - * which case the constraints system will select the smallest - * nearest resolution; == 0 indicates that the zooming direction - * is unknown/not relevant, in which case the constraints system - * will select the nearest resolution. If not defined 0 is - * assumed. - */ -export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction) { - if (resolution) { - const currentResolution = view.getResolution(); - const currentCenter = view.getCenter(); - if (currentResolution !== undefined && currentCenter && - resolution !== currentResolution && opt_duration) { - view.animate({ - resolution: resolution, - anchor: opt_anchor, - duration: opt_duration, - easing: easeOut - }); - } else { - if (opt_anchor) { - const center = view.calculateCenterZoom(resolution, opt_anchor); - view.setCenter(center); - } - view.setResolution(resolution); - } - } -} - - /** * @param {import("../View.js").default} view View. * @param {number} delta Delta from previous zoom level. @@ -201,23 +135,15 @@ export function zoomByDelta(view, delta, opt_anchor, opt_duration) { const newZoom = view.getValidZoomLevel(currentZoom + delta); const newResolution = view.getResolutionForZoom(newZoom); - 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); + if (view.getAnimating()) { + view.cancelAnimations(); } + view.animate({ + resolution: newResolution, + anchor: opt_anchor, + duration: opt_duration !== undefined ? opt_duration : 250, + easing: easeOut + }); } export default Interaction; diff --git a/src/ol/interaction/MouseWheelZoom.js b/src/ol/interaction/MouseWheelZoom.js index 8ba3c68fd0..47c638464f 100644 --- a/src/ol/interaction/MouseWheelZoom.js +++ b/src/ol/interaction/MouseWheelZoom.js @@ -212,49 +212,7 @@ class MouseWheelZoom extends Interaction { view.beginInteraction(); } this.trackpadTimeoutId_ = setTimeout(this.decrementInteractingHint_.bind(this), this.trackpadEventGap_); - let resolution = view.getResolution() * Math.pow(2, delta / this.trackpadDeltaPerZoom_); - const minResolution = view.getMinResolution(); - const maxResolution = view.getMaxResolution(); - let rebound = 0; - if (resolution < minResolution) { - resolution = Math.max(resolution, minResolution / this.trackpadZoomBuffer_); - rebound = 1; - } else if (resolution > maxResolution) { - resolution = Math.min(resolution, maxResolution * this.trackpadZoomBuffer_); - rebound = -1; - } - if (this.lastAnchor_) { - const center = view.calculateCenterZoom(resolution, this.lastAnchor_); - view.setCenter(center); - } - view.setResolution(resolution); - - if (rebound === 0) { - const zoomDelta = delta > 0 ? -1 : 1; - const newZoom = view.getValidZoomLevel(view.getZoom() + zoomDelta); - view.animate({ - resolution: view.getResolutionForZoom(newZoom), - easing: easeOut, - anchor: this.lastAnchor_, - duration: this.duration_ - }); - } - - if (rebound > 0) { - view.animate({ - resolution: minResolution, - easing: easeOut, - anchor: this.lastAnchor_, - duration: 500 - }); - } else if (rebound < 0) { - view.animate({ - resolution: maxResolution, - easing: easeOut, - anchor: this.lastAnchor_, - duration: 500 - }); - } + view.adjustZoom(-delta / this.trackpadDeltaPerZoom_, this.lastAnchor_); this.startTime_ = now; return false; } diff --git a/src/ol/interaction/PinchRotate.js b/src/ol/interaction/PinchRotate.js index fba8c0ff96..f18652557a 100644 --- a/src/ol/interaction/PinchRotate.js +++ b/src/ol/interaction/PinchRotate.js @@ -118,9 +118,8 @@ class PinchRotate extends PointerInteraction { // rotate if (this.rotating_) { - const rotation = view.getRotation(); map.render(); - rotate(view, rotation + rotationDelta, this.anchor_); + view.adjustRotation(rotationDelta, this.anchor_); } } diff --git a/src/ol/interaction/PinchZoom.js b/src/ol/interaction/PinchZoom.js index b3c1e16c90..d3e0e9f6bd 100644 --- a/src/ol/interaction/PinchZoom.js +++ b/src/ol/interaction/PinchZoom.js @@ -83,17 +83,6 @@ class PinchZoom extends PointerInteraction { const map = mapBrowserEvent.map; const view = map.getView(); - const resolution = view.getResolution(); - const maxResolution = view.getMaxResolution(); - const minResolution = view.getMinResolution(); - let newResolution = resolution * scaleDelta; - if (newResolution > maxResolution) { - scaleDelta = maxResolution / resolution; - newResolution = maxResolution; - } else if (newResolution < minResolution) { - scaleDelta = minResolution / resolution; - newResolution = minResolution; - } if (scaleDelta != 1.0) { this.lastScaleDelta_ = scaleDelta; @@ -108,7 +97,7 @@ class PinchZoom extends PointerInteraction { // scale, bypass the resolution constraint map.render(); - zoom(view, newResolution, this.anchor_); + view.adjustResolution(scaleDelta, this.anchor_); } /** @@ -118,7 +107,7 @@ class PinchZoom extends PointerInteraction { if (this.targetPointers.length < 2) { const map = mapBrowserEvent.map; const view = map.getView(); - const direction = this.lastScaleDelta_ - 1; + const direction = this.lastScaleDelta_ > 1 ? 1 : -1; view.endInteraction(this.duration_, direction); return false; } else { diff --git a/test/spec/ol/interaction/dragrotateandzoom.test.js b/test/spec/ol/interaction/dragrotateandzoom.test.js index 7a9249182e..47bc94cab4 100644 --- a/test/spec/ol/interaction/dragrotateandzoom.test.js +++ b/test/spec/ol/interaction/dragrotateandzoom.test.js @@ -62,13 +62,18 @@ describe('ol.interaction.DragRotateAndZoom', function() { true); interaction.lastAngle_ = Math.PI; - let view = map.getView(); - let spy = sinon.spy(view, 'rotate'); - interaction.handleDragEvent(event); - expect(spy.callCount).to.be(1); - expect(interaction.lastAngle_).to.be(-0.8308214428190254); - view.rotate.restore(); + let callCount = 0; + let view = map.getView(); + view.on('change:rotation', function() { + callCount++; + }); + + interaction.handleDragEvent(event); + expect(callCount).to.be(1); + expect(interaction.lastAngle_).to.be(-0.8308214428190254); + + callCount = 0; view = new View({ projection: 'EPSG:4326', center: [0, 0], @@ -76,15 +81,16 @@ describe('ol.interaction.DragRotateAndZoom', function() { enableRotation: false }); map.setView(view); + view.on('change:rotation', function() { + callCount++; + }); event = new MapBrowserPointerEvent('pointermove', map, new PointerEvent('pointermove', {clientX: 24, clientY: 16}, {pointerType: 'mouse'}), true); - spy = sinon.spy(view, 'rotate'); interaction.handleDragEvent(event); - expect(spy.callCount).to.be(0); - view.rotate.restore(); + expect(callCount).to.be(0); }); }); diff --git a/test/spec/ol/interaction/interaction.test.js b/test/spec/ol/interaction/interaction.test.js index 5a6f14a703..7d752ab0c9 100644 --- a/test/spec/ol/interaction/interaction.test.js +++ b/test/spec/ol/interaction/interaction.test.js @@ -87,112 +87,4 @@ describe('ol.interaction.Interaction', function() { }); - describe('zoomByDelta()', function() { - - it('changes view resolution', function() { - const view = new View({ - resolution: 1, - resolutions: [4, 2, 1, 0.5, 0.25] - }); - - zoomByDelta(view, 1); - expect(view.getResolution()).to.be(0.5); - - zoomByDelta(view, -1); - expect(view.getResolution()).to.be(1); - - zoomByDelta(view, 2); - expect(view.getResolution()).to.be(0.25); - - zoomByDelta(view, -2); - expect(view.getResolution()).to.be(1); - }); - - it('changes view resolution and center relative to the anchor', function() { - const view = new View({ - center: [0, 0], - resolution: 1, - resolutions: [4, 2, 1, 0.5, 0.25] - }); - - zoomByDelta(view, 1, [10, 10]); - expect(view.getCenter()).to.eql([5, 5]); - - zoomByDelta(view, -1, [0, 0]); - expect(view.getCenter()).to.eql([10, 10]); - - zoomByDelta(view, 2, [0, 0]); - expect(view.getCenter()).to.eql([2.5, 2.5]); - - zoomByDelta(view, -2, [0, 0]); - expect(view.getCenter()).to.eql([10, 10]); - }); - - it('changes view resolution and center relative to the anchor, while respecting the extent (center only)', function() { - const view = new View({ - center: [0, 0], - extent: [-2.5, -2.5, 2.5, 2.5], - constrainOnlyCenter: true, - resolution: 1, - resolutions: [4, 2, 1, 0.5, 0.25] - }); - - zoomByDelta(view, 1, [10, 10]); - expect(view.getCenter()).to.eql([2.5, 2.5]); - - zoomByDelta(view, -1, [0, 0]); - expect(view.getCenter()).to.eql([2.5, 2.5]); - - zoomByDelta(view, 2, [10, 10]); - expect(view.getCenter()).to.eql([2.5, 2.5]); - - zoomByDelta(view, -2, [0, 0]); - expect(view.getCenter()).to.eql([2.5, 2.5]); - }); - - it('changes view resolution and center relative to the anchor, while respecting the extent', function() { - const map = new Map({}); - const view = new View({ - center: [50, 50], - extent: [0, 0, 100, 100], - resolution: 1, - resolutions: [4, 2, 1, 0.5, 0.25, 0.125] - }); - map.setView(view); - - zoomByDelta(view, 1, [100, 100]); - expect(view.getCenter()).to.eql([75, 75]); - - zoomByDelta(view, -1, [75, 75]); - expect(view.getCenter()).to.eql([50, 50]); - - zoomByDelta(view, 2, [100, 100]); - expect(view.getCenter()).to.eql([87.5, 87.5]); - - zoomByDelta(view, -3, [0, 0]); - expect(view.getCenter()).to.eql([50, 50]); - expect(view.getResolution()).to.eql(2); - }); - - it('changes view resolution and center relative to the anchor, while respecting the extent (rotated)', function() { - const map = new Map({}); - const view = new View({ - center: [50, 50], - extent: [-100, -100, 100, 100], - resolution: 1, - resolutions: [2, 1, 0.5, 0.25, 0.125], - rotation: Math.PI / 4 - }); - map.setView(view); - const halfSize = 100 * Math.SQRT1_2; - - zoomByDelta(view, 1, [100, 100]); - expect(view.getCenter()).to.eql([100 - halfSize / 2, 100 - halfSize / 2]); - - view.setCenter([0, 50]); - zoomByDelta(view, -1, [0, 0]); - expect(view.getCenter()).to.eql([0, 100 - halfSize]); - }); - }); - }); diff --git a/test/spec/ol/view.test.js b/test/spec/ol/view.test.js index 669a12ab7c..3b52a5ba89 100644 --- a/test/spec/ol/view.test.js +++ b/test/spec/ol/view.test.js @@ -8,6 +8,7 @@ import {createEmpty} from '../../../src/ol/extent.js'; import Circle from '../../../src/ol/geom/Circle.js'; import LineString from '../../../src/ol/geom/LineString.js'; import Point from '../../../src/ol/geom/Point.js'; +import {zoomByDelta} from '../../../src/ol/interaction/Interaction'; describe('ol.View', function() { @@ -1559,6 +1560,152 @@ describe('ol.View', function() { expect(view.getValidResolution(4, -1)).to.be(8); }); }); + + describe('#adjustRotation()', function() { + it('changes view rotation with anchor', function() { + const view = new View({ + resolution: 1, + center: [0, 0] + }); + + view.adjustRotation(Math.PI / 2); + expect(view.getRotation()).to.be(Math.PI / 2); + expect(view.getCenter()).to.eql([0, 0]); + + view.adjustRotation(-Math.PI); + expect(view.getRotation()).to.be(-Math.PI / 2); + expect(view.getCenter()).to.eql([0, 0]); + + view.adjustRotation(Math.PI / 3, [50, 0]); + expect(view.getRotation()).to.roughlyEqual(-Math.PI / 6, 1e-9); + expect(view.getCenter()[0]).to.roughlyEqual(50 * (1 - Math.cos(Math.PI / 3)), 1e-9); + expect(view.getCenter()[1]).to.roughlyEqual(-50 * Math.sin(Math.PI / 3), 1e-9); + }); + + it('does not change view parameters if rotation is disabled', function() { + const view = new View({ + resolution: 1, + enableRotation: false, + center: [0, 0] + }); + + view.adjustRotation(Math.PI / 2); + expect(view.getRotation()).to.be(0); + expect(view.getCenter()).to.eql([0, 0]); + + view.adjustRotation(-Math.PI * 3, [-50, 0]); + expect(view.getRotation()).to.be(0); + expect(view.getCenter()).to.eql([0, 0]); + }); + }); + + describe('#adjustZoom()', function() { + + it('changes view resolution', function() { + const view = new View({ + resolution: 1, + resolutions: [4, 2, 1, 0.5, 0.25] + }); + + view.adjustZoom(1); + expect(view.getResolution()).to.be(0.5); + + view.adjustZoom(-1); + expect(view.getResolution()).to.be(1); + + view.adjustZoom(2); + expect(view.getResolution()).to.be(0.25); + + view.adjustZoom(-2); + expect(view.getResolution()).to.be(1); + }); + + it('changes view resolution and center relative to the anchor', function() { + const view = new View({ + center: [0, 0], + resolution: 1, + resolutions: [4, 2, 1, 0.5, 0.25] + }); + + view.adjustZoom(1, [10, 10]); + expect(view.getCenter()).to.eql([5, 5]); + + view.adjustZoom(-1, [0, 0]); + expect(view.getCenter()).to.eql([10, 10]); + + view.adjustZoom(2, [0, 0]); + expect(view.getCenter()).to.eql([2.5, 2.5]); + + view.adjustZoom(-2, [0, 0]); + expect(view.getCenter()).to.eql([10, 10]); + }); + + it('changes view resolution and center relative to the anchor, while respecting the extent (center only)', function() { + const view = new View({ + center: [0, 0], + extent: [-2.5, -2.5, 2.5, 2.5], + constrainOnlyCenter: true, + resolution: 1, + resolutions: [4, 2, 1, 0.5, 0.25] + }); + + view.adjustZoom(1, [10, 10]); + expect(view.getCenter()).to.eql([2.5, 2.5]); + + view.adjustZoom(-1, [0, 0]); + expect(view.getCenter()).to.eql([2.5, 2.5]); + + view.adjustZoom(2, [10, 10]); + expect(view.getCenter()).to.eql([2.5, 2.5]); + + view.adjustZoom(-2, [0, 0]); + expect(view.getCenter()).to.eql([2.5, 2.5]); + }); + + it('changes view resolution and center relative to the anchor, while respecting the extent', function() { + const map = new Map({}); + const view = new View({ + center: [50, 50], + extent: [0, 0, 100, 100], + resolution: 1, + resolutions: [4, 2, 1, 0.5, 0.25, 0.125] + }); + map.setView(view); + + view.adjustZoom(1, [100, 100]); + expect(view.getCenter()).to.eql([75, 75]); + + view.adjustZoom(-1, [75, 75]); + expect(view.getCenter()).to.eql([50, 50]); + + view.adjustZoom(2, [100, 100]); + expect(view.getCenter()).to.eql([87.5, 87.5]); + + view.adjustZoom(-3, [0, 0]); + expect(view.getCenter()).to.eql([50, 50]); + expect(view.getResolution()).to.eql(1); + }); + + it('changes view resolution and center relative to the anchor, while respecting the extent (rotated)', function() { + const map = new Map({}); + const view = new View({ + center: [50, 50], + extent: [-100, -100, 100, 100], + resolution: 1, + resolutions: [2, 1, 0.5, 0.25, 0.125], + rotation: Math.PI / 4 + }); + map.setView(view); + const halfSize = 100 * Math.SQRT1_2; + + view.adjustZoom(1, [100, 100]); + expect(view.getCenter()).to.eql([100 - halfSize / 2, 100 - halfSize / 2]); + + view.setCenter([0, 50]); + view.adjustZoom(-1, [0, 0]); + expect(view.getCenter()).to.eql([0, 100 - halfSize]); + }); + }); }); describe('ol.View.isNoopAnimation()', function() {