diff --git a/src/ol/View.js b/src/ol/View.js index 3bcc05219b..b4e5605672 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -129,6 +129,8 @@ import {createMinMaxResolution} from './resolutionconstraint.js'; * @property {boolean} [smoothResolutionConstraint=true] If true, the resolution * min/max values will be applied smoothly, i. e. allow the view to exceed slightly * the given resolution or zoom bounds. + * @property {boolean} [largerResolutionConstraint=false] If true, the constrained + * resolution values will use the larger value to do so. * @property {import("./proj.js").ProjectionLike} [projection='EPSG:3857'] The * projection. The default is Spherical Mercator. * @property {number} [resolution] The initial resolution for the view. The @@ -1611,6 +1613,9 @@ export function createResolutionConstraint(options) { const smooth = options.smoothResolutionConstraint !== undefined ? options.smoothResolutionConstraint : true; + const larger = + options.largerResolutionConstraint !== undefined ? options.largerResolutionConstraint : false; + const projection = createProjection(options.projection, 'EPSG:3857'); const projExtent = projection.getExtent(); let constrainOnlyCenter = options.constrainOnlyCenter; @@ -1628,10 +1633,10 @@ export function createResolutionConstraint(options) { if (options.constrainResolution) { resolutionConstraint = createSnapToResolutions(resolutions, smooth, - !constrainOnlyCenter && extent); + !constrainOnlyCenter && extent, larger); } else { resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth, - !constrainOnlyCenter && extent); + !constrainOnlyCenter && extent, larger); } } else { // calculate the default min and max resolution @@ -1677,10 +1682,10 @@ export function createResolutionConstraint(options) { if (options.constrainResolution) { resolutionConstraint = createSnapToPower( zoomFactor, maxResolution, minResolution, smooth, - !constrainOnlyCenter && extent); + !constrainOnlyCenter && extent, larger); } else { resolutionConstraint = createMinMaxResolution(maxResolution, minResolution, smooth, - !constrainOnlyCenter && extent); + !constrainOnlyCenter && extent, larger); } } return {constraint: resolutionConstraint, maxResolution: maxResolution, diff --git a/src/ol/resolutionconstraint.js b/src/ol/resolutionconstraint.js index 0134bde876..3d14209ec9 100644 --- a/src/ol/resolutionconstraint.js +++ b/src/ol/resolutionconstraint.js @@ -11,16 +11,21 @@ import {clamp} from './math.js'; */ /** - * Returns a modified resolution taking into acocunt the viewport size and maximum + * Returns a modified resolution taking into account the viewport size and maximum * allowed extent. * @param {number} resolution Resolution * @param {import("./extent.js").Extent=} maxExtent Maximum allowed extent. * @param {import("./size.js").Size} viewportSize Viewport size. + * @param {boolean} larger Whether to use the larger resolution size. * @return {number} Capped resolution. */ -function getViewportClampedResolution(resolution, maxExtent, viewportSize) { +function getViewportClampedResolution(resolution, maxExtent, viewportSize, larger) { const xResolution = getWidth(maxExtent) / viewportSize[0]; const yResolution = getHeight(maxExtent) / viewportSize[1]; + + if (larger) { + return Math.min(resolution, Math.max(xResolution, yResolution)); + } return Math.min(resolution, Math.min(xResolution, yResolution)); } @@ -52,9 +57,10 @@ function getSmoothClampedResolution(resolution, maxResolution, minResolution) { * @param {Array} resolutions Resolutions. * @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true. * @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent. + * @param {boolean=} opt_larger If true, the view will constrain the larger resolution value. Default: false. * @return {Type} Zoom function. */ -export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) { +export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent, opt_larger) { return ( /** * @param {number|undefined} resolution Resolution. @@ -68,7 +74,7 @@ export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) const maxResolution = resolutions[0]; const minResolution = resolutions[resolutions.length - 1]; const cappedMaxRes = opt_maxExtent ? - getViewportClampedResolution(maxResolution, opt_maxExtent, size) : + getViewportClampedResolution(maxResolution, opt_maxExtent, size, opt_larger) : maxResolution; // during interacting or animating, allow intermediary values @@ -100,9 +106,10 @@ export function createSnapToResolutions(resolutions, opt_smooth, opt_maxExtent) * @param {number=} opt_minResolution Minimum resolution. * @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true. * @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent. + * @param {boolean=} opt_larger If true, the view will constrain the larger resolution value. Default: false. * @return {Type} Zoom function. */ -export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent) { +export function createSnapToPower(power, maxResolution, opt_minResolution, opt_smooth, opt_maxExtent, opt_larger) { return ( /** * @param {number|undefined} resolution Resolution. @@ -114,7 +121,7 @@ export function createSnapToPower(power, maxResolution, opt_minResolution, opt_s function(resolution, direction, size, opt_isMoving) { if (resolution !== undefined) { const cappedMaxRes = opt_maxExtent ? - getViewportClampedResolution(maxResolution, opt_maxExtent, size) : + getViewportClampedResolution(maxResolution, opt_maxExtent, size, opt_larger) : maxResolution; const minResolution = opt_minResolution !== undefined ? opt_minResolution : 0; @@ -148,9 +155,10 @@ export function createSnapToPower(power, maxResolution, opt_minResolution, opt_s * @param {number} minResolution Min resolution. * @param {boolean=} opt_smooth If true, the view will be able to slightly exceed resolution limits. Default: true. * @param {import("./extent.js").Extent=} opt_maxExtent Maximum allowed extent. + * @param {boolean=} opt_larger If true, the view will constrain the larger resolution value. Default: false. * @return {Type} Zoom function. */ -export function createMinMaxResolution(maxResolution, minResolution, opt_smooth, opt_maxExtent) { +export function createMinMaxResolution(maxResolution, minResolution, opt_smooth, opt_maxExtent, opt_larger) { return ( /** * @param {number|undefined} resolution Resolution. @@ -162,7 +170,7 @@ export function createMinMaxResolution(maxResolution, minResolution, opt_smooth, function(resolution, direction, size, opt_isMoving) { if (resolution !== undefined) { const cappedMaxRes = opt_maxExtent ? - getViewportClampedResolution(maxResolution, opt_maxExtent, size) : + getViewportClampedResolution(maxResolution, opt_maxExtent, size, opt_larger) : maxResolution; const smooth = opt_smooth !== undefined ? opt_smooth : true; diff --git a/test/spec/ol/view.test.js b/test/spec/ol/view.test.js index 17c2f2733e..4d44e2fa43 100644 --- a/test/spec/ol/view.test.js +++ b/test/spec/ol/view.test.js @@ -380,6 +380,32 @@ describe('ol.View', function() { expect(constraint(1, 0, size)).to.be(5); }); + it('accepts extent and uses the smallest value', function() { + const constraint = getConstraint({ + extent: [0, 0, 4000, 6000] + }); + + expect(constraint(1000, 0, size)).to.be(20); + expect(constraint(500, 0, size)).to.be(20); + expect(constraint(100, 0, size)).to.be(20); + expect(constraint(50, 0, size)).to.be(20); + expect(constraint(10, 0, size)).to.be(10); + expect(constraint(1, 0, size)).to.be(1); + }); + + it('accepts extent and largerResolutionConstraint and uses the larger value', function() { + const constraint = getConstraint({ + extent: [0, 0, 4000, 6000], + largerResolutionConstraint: true + }); + + expect(constraint(1000, 0, size)).to.be(30); + expect(constraint(500, 0, size)).to.be(30); + expect(constraint(100, 0, size)).to.be(30); + expect(constraint(50, 0, size)).to.be(30); + expect(constraint(10, 0, size)).to.be(10); + expect(constraint(1, 0, size)).to.be(1); + }); }); describe('overspecified options (prefers resolution)', function() {