View / add a method to compute a valid zoom level

The `getValidZoomLevel` apply the current resolution constraint to return
a value that is guaranteed valid.

This is used for interactions & controls which need a target value to work:
the +/- buttons, the zoom clider, the dragbox zoom and the mouse wheel zoom.
This commit is contained in:
Olivier Guyot
2019-01-06 14:25:00 +01:00
parent 4e1ece16ed
commit 3c1e3779e2
7 changed files with 92 additions and 25 deletions
+17
View File
@@ -1217,6 +1217,23 @@ class View extends BaseObject {
endInteraction() { endInteraction() {
this.setHint(ViewHint.INTERACTING, -1); 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));
}
} }
+5 -5
View File
@@ -114,20 +114,20 @@ class Zoom extends Control {
// upon it // upon it
return; return;
} }
const currentResolution = view.getResolution(); const currentZoom = view.getZoom();
if (currentResolution) { if (currentZoom !== undefined) {
const newResolution = view.constrainResolution(currentResolution, delta); const newZoom = view.getValidZoomLevel(currentZoom + delta);
if (this.duration_ > 0) { if (this.duration_ > 0) {
if (view.getAnimating()) { if (view.getAnimating()) {
view.cancelAnimations(); view.cancelAnimations();
} }
view.animate({ view.animate({
resolution: newResolution, zoom: newZoom,
duration: this.duration_, duration: this.duration_,
easing: easeOut easing: easeOut
}); });
} else { } else {
view.setResolution(newResolution); view.setZoom(newZoom);
} }
} }
} }
+6 -2
View File
@@ -218,9 +218,10 @@ class ZoomSlider extends Control {
event.offsetY - this.thumbSize_[1] / 2); event.offsetY - this.thumbSize_[1] / 2);
const resolution = this.getResolutionForPosition_(relativePosition); const resolution = this.getResolutionForPosition_(relativePosition);
const zoom = view.getValidZoomLevel(view.getZoomForResolution(resolution));
view.animate({ view.animate({
resolution: view.constrainResolution(resolution), zoom: zoom,
duration: this.duration_, duration: this.duration_,
easing: easeOut easing: easeOut
}); });
@@ -281,8 +282,11 @@ class ZoomSlider extends Control {
const view = this.getMap().getView(); const view = this.getMap().getView();
view.endInteraction(); view.endInteraction();
const zoom = view.getValidZoomLevel(
view.getZoomForResolution(this.currentResolution_));
view.animate({ view.animate({
resolution: view.constrainResolution(this.currentResolution_), zoom: zoom,
duration: this.duration_, duration: this.duration_,
easing: easeOut easing: easeOut
}); });
+3 -3
View File
@@ -80,14 +80,14 @@ function onBoxEnd() {
extent = mapExtent; extent = mapExtent;
} }
const resolution = view.constrainResolution( const resolution = view.getResolutionForExtent(extent, size);
view.getResolutionForExtent(extent, size)); const zoom = view.getValidZoomLevel(view.getZoomForResolution(resolution));
let center = getCenter(extent); let center = getCenter(extent);
center = view.constrainCenter(center); center = view.constrainCenter(center);
view.animate({ view.animate({
resolution: resolution, zoom: zoom,
center: center, center: center,
duration: this.duration_, duration: this.duration_,
easing: easeOut easing: easeOut
+29 -14
View File
@@ -189,34 +189,49 @@ export function zoom(view, resolution, opt_anchor, opt_duration, opt_direction)
* @param {number=} opt_duration Duration. * @param {number=} opt_duration Duration.
*/ */
export function zoomByDelta(view, delta, opt_anchor, opt_duration) { export function zoomByDelta(view, delta, opt_anchor, opt_duration) {
const currentZoom = view.getZoom();
const currentResolution = view.getResolution(); const currentResolution = view.getResolution();
let resolution = view.constrainResolution(currentResolution, delta, 0);
if (resolution !== undefined) { if (currentZoom === undefined) {
const resolutions = view.getResolutions(); return;
resolution = clamp(
resolution,
view.getMinResolution() || resolutions[resolutions.length - 1],
view.getMaxResolution() || resolutions[0]);
} }
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 // 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 // new center is within the extent. We first calculate the new center, apply
// the constraint to it, and then calculate back the anchor // the constraint to it, and then calculate back the anchor
if (opt_anchor && resolution !== undefined && resolution !== currentResolution) { if (opt_anchor) {
const currentCenter = view.getCenter(); const currentCenter = view.getCenter();
let center = view.calculateCenterZoom(resolution, opt_anchor); let center = view.calculateCenterZoom(newResolution, opt_anchor);
center = view.constrainCenter(center); center = view.constrainCenter(center);
opt_anchor = [ opt_anchor = [
(resolution * currentCenter[0] - currentResolution * center[0]) / (newResolution * currentCenter[0] - currentResolution * center[0]) /
(resolution - currentResolution), (newResolution - currentResolution),
(resolution * currentCenter[1] - currentResolution * center[1]) / (newResolution * currentCenter[1] - currentResolution * center[1]) /
(resolution - currentResolution) (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);
}
} }
+3 -1
View File
@@ -239,8 +239,10 @@ class MouseWheelZoom extends Interaction {
view.setResolution(resolution); view.setResolution(resolution);
if (rebound === 0 && this.constrainResolution_) { if (rebound === 0 && this.constrainResolution_) {
const zoomDelta = delta > 0 ? -1 : 1;
const newZoom = view.getValidZoomLevel(view.getZoom() + zoomDelta);
view.animate({ view.animate({
resolution: view.constrainResolution(resolution, delta > 0 ? -1 : 1), resolution: view.getResolutionForZoom(newZoom),
easing: easeOut, easing: easeOut,
anchor: this.lastAnchor_, anchor: this.lastAnchor_,
duration: this.duration_ duration: this.duration_
+29
View File
@@ -1439,6 +1439,35 @@ describe('ol.View', function() {
expect(view.getHints()[1]).to.be(0); 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() { describe('ol.View.isNoopAnimation()', function() {