From 238cdc392e1d7612ae021cb6f409f628ef32969b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Fri, 7 May 2021 14:13:19 +0200 Subject: [PATCH 1/6] Add test for DragZoom with view padding --- .../spec/ol/interaction/dragzoom.test.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/browser/spec/ol/interaction/dragzoom.test.js b/test/browser/spec/ol/interaction/dragzoom.test.js index 9fef5182b2..f26d7af092 100644 --- a/test/browser/spec/ol/interaction/dragzoom.test.js +++ b/test/browser/spec/ol/interaction/dragzoom.test.js @@ -79,6 +79,27 @@ describe('ol.interaction.DragZoom', function () { }, 50); }); + it('centers the view with padding on the box geometry', function (done) { + map.getView().padding = [0, 180, 0, 0]; + + const interaction = new DragZoom({ + duration: 10, + }); + map.addInteraction(interaction); + + const box = new RenderBox(); + const extent = [-180, -90, 0, 90]; + box.geometry_ = polygonFromExtent(extent); + interaction.box_ = box; + + interaction.onBoxEnd(); + setTimeout(function () { + const view = map.getView(); + expect(view.getResolution()).to.be(1); + expect(view.calculateExtentInternal()).to.eql([-180, -90, 180, 90]); + done(); + }, 50); + }); it('sets new resolution while zooming out', function (done) { const interaction = new DragZoom({ duration: 10, From 8129a3919637ee8767db85ffc0b9a0f2078812aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Fri, 7 May 2021 14:13:57 +0200 Subject: [PATCH 2/6] Fix DragZoom with View padding --- src/ol/interaction/DragZoom.js | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ol/interaction/DragZoom.js b/src/ol/interaction/DragZoom.js index 66b8373a51..a839367087 100644 --- a/src/ol/interaction/DragZoom.js +++ b/src/ol/interaction/DragZoom.js @@ -5,11 +5,11 @@ import DragBox from './DragBox.js'; import { createOrUpdateFromCoordinates, getBottomLeft, - getCenter, getTopRight, scaleFromCenter, } from '../extent.js'; import {easeOut} from '../easing.js'; +import {fromExtent as polygonFromExtent} from '../geom/Polygon.js'; import {shiftKeyOnly} from '../events/condition.js'; /** @@ -71,10 +71,10 @@ class DragZoom extends DragBox { onBoxEnd(event) { const map = this.getMap(); const view = /** @type {!import("../View.js").default} */ (map.getView()); - const size = /** @type {!import("../size.js").Size} */ (map.getSize()); let extent = this.getGeometry().getExtent(); if (this.out_) { + const size = /** @type {!import("../size.js").Size} */ (map.getSize()); const mapExtent = view.calculateExtentInternal(size); const boxPixelExtent = createOrUpdateFromCoordinates([ map.getPixelFromCoordinateInternal(getBottomLeft(extent)), @@ -86,14 +86,7 @@ class DragZoom extends DragBox { extent = mapExtent; } - const resolution = view.getConstrainedResolution( - view.getResolutionForExtentInternal(extent, size) - ); - const center = view.getConstrainedCenter(getCenter(extent), resolution); - - view.animateInternal({ - resolution: resolution, - center: center, + view.fitInternal(polygonFromExtent(extent), { duration: this.duration_, easing: easeOut, }); From c19664b6f54dac674dddc15ac6de9ce740bf6c76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Sat, 8 May 2021 02:18:57 +0200 Subject: [PATCH 3/6] Fix dragzoom test case --- test/browser/spec/ol/interaction/dragzoom.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/browser/spec/ol/interaction/dragzoom.test.js b/test/browser/spec/ol/interaction/dragzoom.test.js index f26d7af092..c421a15f05 100644 --- a/test/browser/spec/ol/interaction/dragzoom.test.js +++ b/test/browser/spec/ol/interaction/dragzoom.test.js @@ -96,7 +96,7 @@ describe('ol.interaction.DragZoom', function () { setTimeout(function () { const view = map.getView(); expect(view.getResolution()).to.be(1); - expect(view.calculateExtentInternal()).to.eql([-180, -90, 180, 90]); + expect(view.calculateExtentInternal()).to.eql(extent); done(); }, 50); }); From b89809c1a1a04e3289c864c9118673e58342fe0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Sat, 8 May 2021 03:35:32 +0200 Subject: [PATCH 4/6] Add new dragzoom test with view rotation Also adds a new test for the duration and removes the duration from the other tests to save on execution time. --- .../spec/ol/interaction/dragzoom.test.js | 141 ++++++++++++++---- 1 file changed, 111 insertions(+), 30 deletions(-) diff --git a/test/browser/spec/ol/interaction/dragzoom.test.js b/test/browser/spec/ol/interaction/dragzoom.test.js index c421a15f05..6a55babcc6 100644 --- a/test/browser/spec/ol/interaction/dragzoom.test.js +++ b/test/browser/spec/ol/interaction/dragzoom.test.js @@ -1,14 +1,21 @@ import DragZoom from '../../../../../src/ol/interaction/DragZoom.js'; import Map from '../../../../../src/ol/Map.js'; +import Polygon, { + fromExtent as polygonFromExtent, +} from '../../../../../src/ol/geom/Polygon.js'; import RenderBox from '../../../../../src/ol/render/Box.js'; import VectorLayer from '../../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../../src/ol/source/Vector.js'; import View from '../../../../../src/ol/View.js'; -import {getCenter} from '../../../../../src/ol/extent.js'; -import {fromExtent as polygonFromExtent} from '../../../../../src/ol/geom/Polygon.js'; +import {getCenter, scaleFromCenter} from '../../../../../src/ol/extent.js'; describe('ol.interaction.DragZoom', function () { - let target, map, source; + /** @type {HTMLElement} */ + let target; + /** @type {Map} */ + let map; + /** @type {VectorSource} */ + let source; const width = 360; const height = 180; @@ -31,6 +38,7 @@ describe('ol.interaction.DragZoom', function () { projection: 'EPSG:4326', center: [0, 0], resolution: 1, + multiWorld: true, }), }); map.once('postrender', function () { @@ -59,9 +67,27 @@ describe('ol.interaction.DragZoom', function () { }); describe('#onBoxEnd()', function () { - it('centers the view on the box geometry', function (done) { + it('uses the configured duration', function () { const interaction = new DragZoom({ - duration: 10, + duration: 1, + }); + map.addInteraction(interaction); + const view = map.getView(); + view.fitInternal = sinon.spy(); + + const box = new RenderBox(); + const extent = [-110, 40, -90, 60]; + box.geometry_ = polygonFromExtent(extent); + interaction.box_ = box; + + interaction.onBoxEnd(); + + expect(view.fitInternal.calledOnce).to.be(true); + expect(view.fitInternal.args[0][1].duration).to.be(1); + }); + it('centers the view on the box geometry', function () { + const interaction = new DragZoom({ + duration: 0, }); map.addInteraction(interaction); @@ -71,19 +97,47 @@ describe('ol.interaction.DragZoom', function () { interaction.box_ = box; interaction.onBoxEnd(); - setTimeout(function () { - const view = map.getView(); - const center = view.getCenterInternal(); - expect(center).to.eql(getCenter(extent)); - done(); - }, 50); + const view = map.getView(); + const center = view.getCenterInternal(); + expect(center).to.eql(getCenter(extent)); }); - it('centers the view with padding on the box geometry', function (done) { + it('centers the rotated view on the box geometry', function () { + const view = map.getView(); + view.setRotation(Math.PI / 4); + + const interaction = new DragZoom({ + duration: 0, + }); + map.addInteraction(interaction); + + const box = new RenderBox(); + map.renderSync(); + box.geometry_ = new Polygon([ + [ + map.getCoordinateFromPixel([0, 0]), + map.getCoordinateFromPixel([360, 0]), + map.getCoordinateFromPixel([360, 180]), + map.getCoordinateFromPixel([0, 180]), + map.getCoordinateFromPixel([0, 0]), + ], + ]); + interaction.box_ = box; + + const extentBefore = view.calculateExtentInternal(); + interaction.onBoxEnd(); + const newExtent = view.calculateExtentInternal(); + expect(newExtent[0]).to.roughlyEqual(extentBefore[0], 1e-9); + expect(newExtent[1]).to.roughlyEqual(extentBefore[1], 1e-9); + expect(newExtent[2]).to.roughlyEqual(extentBefore[2], 1e-9); + expect(newExtent[3]).to.roughlyEqual(extentBefore[3], 1e-9); + expect(view.getResolution()).to.roughlyEqual(1, 1e-9); + }); + it('centers the padded view on the box geometry', function () { map.getView().padding = [0, 180, 0, 0]; const interaction = new DragZoom({ - duration: 10, + duration: 0, }); map.addInteraction(interaction); @@ -93,16 +147,13 @@ describe('ol.interaction.DragZoom', function () { interaction.box_ = box; interaction.onBoxEnd(); - setTimeout(function () { - const view = map.getView(); - expect(view.getResolution()).to.be(1); - expect(view.calculateExtentInternal()).to.eql(extent); - done(); - }, 50); + const view = map.getView(); + expect(view.getResolution()).to.be(1); + expect(view.calculateExtentInternal()).to.eql(extent); }); - it('sets new resolution while zooming out', function (done) { + it('sets new resolution while zooming out', function () { const interaction = new DragZoom({ - duration: 10, + duration: 0, out: true, }); map.addInteraction(interaction); @@ -113,15 +164,45 @@ describe('ol.interaction.DragZoom', function () { interaction.box_ = box; map.getView().setResolution(0.25); - setTimeout(function () { - interaction.onBoxEnd(); - setTimeout(function () { - const view = map.getView(); - const resolution = view.getResolution(); - expect(resolution).to.eql(view.getConstrainedResolution(0.5)); - done(); - }, 50); - }, 50); + interaction.onBoxEnd(); + const view = map.getView(); + const resolution = view.getResolution(); + expect(resolution).to.eql(view.getConstrainedResolution(0.5)); + }); + it('sets new resolution while zooming out with view padding and rotation', function () { + const view = map.getView(); + view.setResolution(0.5); + view.setRotation(Math.PI / 4); + view.padding = [90, 0, 0, 0]; + + const interaction = new DragZoom({ + duration: 0, + out: true, + }); + map.addInteraction(interaction); + + const box = new RenderBox(); + map.renderSync(); + box.geometry_ = new Polygon([ + [ + map.getCoordinateFromPixel([90, 117.5]), + map.getCoordinateFromPixel([90, 152.5]), + map.getCoordinateFromPixel([270, 152.5]), + map.getCoordinateFromPixel([270, 117.5]), + map.getCoordinateFromPixel([90, 117.5]), + ], + ]); + interaction.box_ = box; + + const expected = view.calculateExtentInternal(); + scaleFromCenter(expected, 2); + interaction.onBoxEnd(); + const newExtent = view.calculateExtentInternal(); + expect(view.getResolution()).to.roughlyEqual(1, 1e-9); + expect(newExtent[0]).to.roughlyEqual(expected[0], 1e-9); + expect(newExtent[1]).to.roughlyEqual(expected[1], 1e-9); + expect(newExtent[2]).to.roughlyEqual(expected[2], 1e-9); + expect(newExtent[3]).to.roughlyEqual(expected[3], 1e-9); }); }); }); From c19ebc72cbd72095cbfdfea2d48a929f788c7e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Sat, 8 May 2021 02:10:05 +0200 Subject: [PATCH 5/6] Fix dragzoom with rotated view When the view is rotated the extent of the drag box is not the same as the drag box. --- src/ol/View.js | 70 +++++++++++++++++++--------------- src/ol/interaction/DragZoom.js | 26 ++++--------- 2 files changed, 47 insertions(+), 49 deletions(-) diff --git a/src/ol/View.js b/src/ol/View.js index e1cd60c174..ae435a1c2f 100644 --- a/src/ol/View.js +++ b/src/ol/View.js @@ -1076,7 +1076,7 @@ class View extends BaseObject { * the given size. */ getResolutionForExtentInternal(extent, opt_size) { - const size = opt_size || this.getViewportSize_(); + const size = opt_size || this.getViewportSizeMinusPadding_(); const xResolution = getWidth(extent) / size[0]; const yResolution = getHeight(extent) / size[1]; return Math.max(xResolution, yResolution); @@ -1298,6 +1298,32 @@ class View extends BaseObject { this.fitInternal(geometry, opt_options); } + /** + * Calculate rotated extent + * @param {import("./geom/SimpleGeometry.js").default} geometry The geometry. + * @return {import("./extent").Extent} The rotated extent for the geometry. + */ + rotatedExtentForGeometry(geometry) { + const rotation = this.getRotation(); + const cosAngle = Math.cos(rotation); + const sinAngle = Math.sin(-rotation); + const coords = geometry.getFlatCoordinates(); + const stride = geometry.getStride(); + let minRotX = +Infinity; + let minRotY = +Infinity; + let maxRotX = -Infinity; + let maxRotY = -Infinity; + for (let i = 0, ii = coords.length; i < ii; i += stride) { + const rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; + const rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; + minRotX = Math.min(minRotX, rotX); + minRotY = Math.min(minRotY, rotY); + maxRotX = Math.max(maxRotX, rotX); + maxRotY = Math.max(maxRotY, rotY); + } + return [minRotX, minRotY, maxRotX, maxRotY]; + } + /** * @param {import("./geom/SimpleGeometry.js").default} geometry The geometry. * @param {FitOptions} [opt_options] Options. @@ -1319,44 +1345,28 @@ class View extends BaseObject { } else { minResolution = 0; } - const coords = geometry.getFlatCoordinates(); - // calculate rotated extent - const rotation = this.getRotation(); - const cosAngle = Math.cos(-rotation); - let sinAngle = Math.sin(-rotation); - let minRotX = +Infinity; - let minRotY = +Infinity; - let maxRotX = -Infinity; - let maxRotY = -Infinity; - const stride = geometry.getStride(); - for (let i = 0, ii = coords.length; i < ii; i += stride) { - const rotX = coords[i] * cosAngle - coords[i + 1] * sinAngle; - const rotY = coords[i] * sinAngle + coords[i + 1] * cosAngle; - minRotX = Math.min(minRotX, rotX); - minRotY = Math.min(minRotY, rotY); - maxRotX = Math.max(maxRotX, rotX); - maxRotY = Math.max(maxRotY, rotY); - } + const rotatedExtent = this.rotatedExtentForGeometry(geometry); // calculate resolution - let resolution = this.getResolutionForExtentInternal( - [minRotX, minRotY, maxRotX, maxRotY], - [size[0] - padding[1] - padding[3], size[1] - padding[0] - padding[2]] - ); + let resolution = this.getResolutionForExtentInternal(rotatedExtent, [ + size[0] - padding[1] - padding[3], + size[1] - padding[0] - padding[2], + ]); resolution = isNaN(resolution) ? minResolution : Math.max(resolution, minResolution); resolution = this.getConstrainedResolution(resolution, nearest ? 0 : 1); // calculate center - sinAngle = -sinAngle; // go back to original rotation - let centerRotX = (minRotX + maxRotX) / 2; - let centerRotY = (minRotY + maxRotY) / 2; - centerRotX += ((padding[1] - padding[3]) / 2) * resolution; - centerRotY += ((padding[0] - padding[2]) / 2) * resolution; - const centerX = centerRotX * cosAngle - centerRotY * sinAngle; - const centerY = centerRotY * cosAngle + centerRotX * sinAngle; + const rotation = this.getRotation(); + const sinAngle = Math.sin(rotation); + const cosAngle = Math.cos(rotation); + const centerRot = getCenter(rotatedExtent); + centerRot[0] += ((padding[1] - padding[3]) / 2) * resolution; + centerRot[1] += ((padding[0] - padding[2]) / 2) * resolution; + const centerX = centerRot[0] * cosAngle - centerRot[1] * sinAngle; + const centerY = centerRot[1] * cosAngle + centerRot[0] * sinAngle; const center = this.getConstrainedCenter([centerX, centerY], resolution); const callback = options.callback ? options.callback : VOID; diff --git a/src/ol/interaction/DragZoom.js b/src/ol/interaction/DragZoom.js index a839367087..46b6d10b27 100644 --- a/src/ol/interaction/DragZoom.js +++ b/src/ol/interaction/DragZoom.js @@ -2,14 +2,7 @@ * @module ol/interaction/DragZoom */ import DragBox from './DragBox.js'; -import { - createOrUpdateFromCoordinates, - getBottomLeft, - getTopRight, - scaleFromCenter, -} from '../extent.js'; import {easeOut} from '../easing.js'; -import {fromExtent as polygonFromExtent} from '../geom/Polygon.js'; import {shiftKeyOnly} from '../events/condition.js'; /** @@ -71,22 +64,17 @@ class DragZoom extends DragBox { onBoxEnd(event) { const map = this.getMap(); const view = /** @type {!import("../View.js").default} */ (map.getView()); - let extent = this.getGeometry().getExtent(); + let geometry = this.getGeometry(); if (this.out_) { - const size = /** @type {!import("../size.js").Size} */ (map.getSize()); - const mapExtent = view.calculateExtentInternal(size); - const boxPixelExtent = createOrUpdateFromCoordinates([ - map.getPixelFromCoordinateInternal(getBottomLeft(extent)), - map.getPixelFromCoordinateInternal(getTopRight(extent)), - ]); - const factor = view.getResolutionForExtentInternal(boxPixelExtent, size); - - scaleFromCenter(mapExtent, 1 / factor); - extent = mapExtent; + const rotatedExtent = view.rotatedExtentForGeometry(geometry); + const resolution = view.getResolutionForExtentInternal(rotatedExtent); + const factor = view.getResolution() / resolution; + geometry = geometry.clone(); + geometry.scale(factor * factor); } - view.fitInternal(polygonFromExtent(extent), { + view.fitInternal(geometry, { duration: this.duration_, easing: easeOut, }); From dabfe40700f1bbdd4afbfab3e2599e27e65bf589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20Kr=C3=B6g?= Date: Sat, 8 May 2021 02:15:43 +0200 Subject: [PATCH 6/6] Fix error in view-padding example An error occured when the buttons are clicked before the source is loaded. --- examples/view-padding.js | 42 +++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/examples/view-padding.js b/examples/view-padding.js index ee5b7936d1..07c872bf89 100644 --- a/examples/view-padding.js +++ b/examples/view-padding.js @@ -50,24 +50,26 @@ const map = new Map({ view: view, }); -const zoomtoswitzerland = document.getElementById('zoomtoswitzerland'); -zoomtoswitzerland.addEventListener( - 'click', - function () { - const feature = source.getFeatures()[0]; - const polygon = feature.getGeometry(); - view.fit(polygon); - }, - false -); +vectorLayer.getSource().on('featuresloadend', function () { + const zoomtoswitzerland = document.getElementById('zoomtoswitzerland'); + zoomtoswitzerland.addEventListener( + 'click', + function () { + const feature = source.getFeatures()[0]; + const polygon = feature.getGeometry(); + view.fit(polygon); + }, + false + ); -const centerlausanne = document.getElementById('centerlausanne'); -centerlausanne.addEventListener( - 'click', - function () { - const feature = source.getFeatures()[1]; - const point = feature.getGeometry(); - view.setCenter(point.getCoordinates()); - }, - false -); + const centerlausanne = document.getElementById('centerlausanne'); + centerlausanne.addEventListener( + 'click', + function () { + const feature = source.getFeatures()[1]; + const point = feature.getGeometry(); + view.setCenter(point.getCoordinates()); + }, + false + ); +});