From 631afb07abfea3afcc7f64be1149c04e231f8a81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Fri, 5 Apr 2013 11:17:33 +0200 Subject: [PATCH 1/5] Add value/resolution functions to View2D --- src/ol/view2d.js | 90 +++++++++++++++++++++++++++++++++---- test/spec/ol/view2d.test.js | 18 ++++++-- 2 files changed, 96 insertions(+), 12 deletions(-) diff --git a/src/ol/view2d.js b/src/ol/view2d.js index fa8d0eb8eb..6fe6b0c949 100644 --- a/src/ol/view2d.js +++ b/src/ol/view2d.js @@ -12,6 +12,7 @@ goog.require('ol.IView3D'); goog.require('ol.Projection'); goog.require('ol.ResolutionConstraint'); goog.require('ol.RotationConstraint'); +goog.require('ol.RotationConstraintType'); goog.require('ol.Size'); goog.require('ol.View'); goog.require('ol.animation'); @@ -64,11 +65,29 @@ ol.View2D = function(opt_options) { values[ol.View2DProperty.ROTATION] = options.rotation; this.setValues(values); + var parts = ol.View2D.createResolutionConstraint_(options); + + /** + * @private + * @type {number} + */ + this.maxResolution_ = parts[1]; + + /** + * @private + * @type {number} + */ + this.minResolution_ = parts[2]; + + var resolutionConstraint = parts[0]; + var rotationConstraint = ol.View2D.createRotationConstraint_(options); + /** * @private * @type {ol.Constraints} */ - this.constraints_ = ol.View2D.createConstraints_(options); + this.constraints_ = new ol.Constraints(resolutionConstraint, + rotationConstraint); }; goog.inherits(ol.View2D, ol.View); @@ -141,6 +160,26 @@ ol.View2D.prototype.getResolutionForExtent = function(extent, size) { }; +/** + * Return a function that returns a value between 0 and 1 for a + * resolution. Exponential scaling is assumed. + * @param {number=} opt_power Power. + * @return {function(number): number} Resolution for value function. + */ +ol.View2D.prototype.getResolutionForValueFunction = function(opt_power) { + var power = opt_power || 2; + var maxResolution = this.maxResolution_; + var minResolution = this.minResolution_; + var max = Math.log(maxResolution / minResolution) / Math.log(power); + return function(value) { + var resolution = maxResolution / Math.pow(power, value * max); + goog.asserts.assert(resolution >= minResolution && + resolution <= maxResolution); + return resolution; + }; +}; + + /** * @return {number} Map rotation. */ @@ -154,6 +193,25 @@ goog.exportProperty( ol.View2D.prototype.getRotation); +/** + * Return a function that returns a resolution for a value between + * 0 and 1. Exponential scaling is assumed. + * @param {number=} opt_power Power. + * @return {function(number): number} Value for resolution function. + */ +ol.View2D.prototype.getValueForResolutionFunction = function(opt_power) { + var power = opt_power || 2; + var maxResolution = this.maxResolution_; + var minResolution = this.minResolution_; + var max = Math.log(maxResolution / minResolution) / Math.log(power); + return function(resolution) { + var value = (Math.log(maxResolution / resolution) / Math.log(power)) / max; + goog.asserts.assert(value >= 0 && value <= 1); + return value; + }; +}; + + /** * @inheritDoc */ @@ -420,15 +478,21 @@ ol.View2D.prototype.zoomWithoutConstraints = /** * @private * @param {ol.View2DOptions} options View2D options. - * @return {ol.Constraints} Constraints. + * @return {Array} Array of three elements: the resolution constraint, + * maxResolution, and minResolution. */ -ol.View2D.createConstraints_ = function(options) { +ol.View2D.createResolutionConstraint_ = function(options) { var resolutionConstraint; + var maxResolution; + var minResolution; if (goog.isDef(options.resolutions)) { + var resolutions = options.resolutions; + maxResolution = resolutions[0]; + minResolution = resolutions[resolutions.length - 1]; resolutionConstraint = ol.ResolutionConstraint.createSnapToResolutions( - options.resolutions); + resolutions); } else { - var maxResolution, numZoomLevels, zoomFactor; + var numZoomLevels, zoomFactor; if (goog.isDef(options.maxResolution) && goog.isDef(options.numZoomLevels) && goog.isDef(options.zoomFactor)) { @@ -444,10 +508,20 @@ ol.View2D.createConstraints_ = function(options) { numZoomLevels = 29; zoomFactor = 2; } + minResolution = maxResolution / Math.pow(zoomFactor, numZoomLevels - 1); resolutionConstraint = ol.ResolutionConstraint.createSnapToPower( zoomFactor, maxResolution, numZoomLevels - 1); } - // FIXME rotation constraint is not configurable at the moment - var rotationConstraint = ol.RotationConstraint.createSnapToZero(); - return new ol.Constraints(resolutionConstraint, rotationConstraint); + return [resolutionConstraint, maxResolution, minResolution]; +}; + + +/** + * @private + * @param {ol.View2DOptions} options View2D options. + * @return {ol.RotationConstraintType} Rotation constraint. + */ +ol.View2D.createRotationConstraint_ = function(options) { + // FIXME rotation constraint is not configurable at the moment + return ol.RotationConstraint.createSnapToZero(); }; diff --git a/test/spec/ol/view2d.test.js b/test/spec/ol/view2d.test.js index f95e90fb72..95dfab872f 100644 --- a/test/spec/ol/view2d.test.js +++ b/test/spec/ol/view2d.test.js @@ -8,7 +8,7 @@ describe('ol.View2D', function() { describe('with no options', function() { it('gives a correct resolution constraint function', function() { var options = {}; - var fn = ol.View2D.createConstraints_(options).resolution; + var fn = ol.View2D.createResolutionConstraint_(options)[0]; expect(fn(156543.03392804097, 0, 0)) .to.roughlyEqual(156543.03392804097, 1e-9); expect(fn(78271.51696402048, 0, 0)) @@ -24,7 +24,12 @@ describe('ol.View2D', function() { numZoomLevels: 4, zoomFactor: 3 }; - var fn = ol.View2D.createConstraints_(options).resolution; + var parts = ol.View2D.createResolutionConstraint_(options); + var maxResolution = parts[1]; + expect(maxResolution).to.eql(81); + var minResolution = parts[2]; + expect(minResolution).to.eql(3); + var fn = parts[0]; expect(fn(82, 0, 0)).to.eql(81); expect(fn(81, 0, 0)).to.eql(81); expect(fn(27, 0, 0)).to.eql(27); @@ -39,7 +44,12 @@ describe('ol.View2D', function() { var options = { resolutions: [97, 76, 65, 54, 0.45] }; - var fn = ol.View2D.createConstraints_(options).resolution; + var parts = ol.View2D.createResolutionConstraint_(options); + var maxResolution = parts[1]; + expect(maxResolution).to.eql(97); + var minResolution = parts[2]; + expect(minResolution).to.eql(0.45); + var fn = parts[0]; expect(fn(97, 0, 0)).to.eql(97); expect(fn(76, 0, 0)).to.eql(76); expect(fn(65, 0, 0)).to.eql(65); @@ -53,7 +63,7 @@ describe('ol.View2D', function() { describe('create rotation constraint', function() { it('gives a correct rotation constraint function', function() { var options = {}; - var fn = ol.View2D.createConstraints_(options).rotation; + var fn = ol.View2D.createRotationConstraint_(options); expect(fn(0.01, 0)).to.eql(0); expect(fn(0.15, 0)).to.eql(0.15); }); From 6163f05b2b01f407e76dade9f3cdac6fe53d5f51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 8 Apr 2013 12:50:12 +0200 Subject: [PATCH 2/5] ZoomSlider setMap deals with undefined resolution --- src/ol/control/zoomslidercontrol.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index 111e3a617e..3ac392fe20 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -152,9 +152,12 @@ ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION = 156543.0339; */ ol.control.ZoomSlider.prototype.setMap = function(map) { goog.base(this, 'setMap', map); - this.currentResolution_ = map.getView().getResolution(); this.initSlider_(); - this.positionThumbForResolution_(this.currentResolution_); + var resolution = map.getView().getResolution(); + if (goog.isDef(resolution)) { + this.currentResolution_ = resolution; + this.positionThumbForResolution_(resolution); + } }; From 516f21eb17d4ec7b4468edff7096e2363d404e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 8 Apr 2013 12:51:14 +0200 Subject: [PATCH 3/5] Make ZoomSlider use View2D value/resolution funcs --- examples/zoomslider.js | 7 +-- src/ol/control/zoomslidercontrol.js | 72 ++++--------------------- test/spec/ol/control/zoomslider.test.js | 49 +---------------- 3 files changed, 13 insertions(+), 115 deletions(-) diff --git a/examples/zoomslider.js b/examples/zoomslider.js index 9835bb30a0..84911fa81b 100644 --- a/examples/zoomslider.js +++ b/examples/zoomslider.js @@ -12,12 +12,9 @@ goog.require('ol.source.MapQuestOpenAerial'); * @return {ol.Map} The ol.Map instance. */ var createMap = function(divId) { - var source, layer, map, zoomslider, resolutions, minRes, maxRes; + var source, layer, map, zoomslider, resolutions; source = new ol.source.MapQuestOpenAerial(); - // These are the min and max resolutions of MapQuestOpenAerial - minRes = 0.5971642834779395; - maxRes = 156543.03392804097; layer = new ol.layer.TileLayer({ source: source }); @@ -30,8 +27,6 @@ var createMap = function(divId) { }) }); zoomslider = new ol.control.ZoomSlider({ - minResolution: minRes, - maxResolution: maxRes, map: map }); return map; diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index 3ac392fe20..ae565475b5 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -5,7 +5,6 @@ goog.provide('ol.control.ZoomSlider'); goog.require('goog.array'); -goog.require('goog.asserts'); goog.require('goog.dom'); goog.require('goog.dom.TagName'); goog.require('goog.events'); @@ -26,38 +25,6 @@ goog.require('ol.css'); * @param {ol.control.ZoomSliderOptions} options Zoom slider options. */ ol.control.ZoomSlider = function(options) { - // FIXME these should be read out from a map if not given, and only then - // fallback to the constants if they weren't defined on the map. - /** - * The minimum resolution that one can set with this control. - * - * @type {number} - * @private - */ - this.maxResolution_ = goog.isDef(options.maxResolution) ? - options.maxResolution : ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION; - - /** - * The maximum resolution that one can set with this control. - * - * @type {number} - * @private - */ - this.minResolution_ = goog.isDef(options.minResolution) ? - options.minResolution : ol.control.ZoomSlider.DEFAULT_MIN_RESOLUTION; - - goog.asserts.assert( - this.minResolution_ < this.maxResolution_, - 'minResolution must be smaller than maxResolution.' - ); - - /** - * The range of resolutions we are handling in this slider. - * - * @type {number} - * @private - */ - this.range_ = this.maxResolution_ - this.minResolution_; /** * Will hold the current resolution of the view. @@ -127,26 +94,6 @@ ol.control.ZoomSlider.CSS_CLASS_THUMB = ol.control.ZoomSlider.CSS_CLASS_CONTAINER + '-thumb'; -/** - * The default value for minResolution_ when the control isn't instanciated with - * an explicit value. The default value is the resolution of the standard OSM - * tiles at zoomlevel 18. - * - * @const {number} - */ -ol.control.ZoomSlider.DEFAULT_MIN_RESOLUTION = 0.5971642833948135; - - -/** - * The default value for maxResolution_ when the control isn't instanciated with - * an explicit value. The default value is the resolution of the standard OSM - * tiles at zoomlevel 0. - * - * @const {number} - */ -ol.control.ZoomSlider.DEFAULT_MAX_RESOLUTION = 156543.0339; - - /** * @inheritDoc */ @@ -262,13 +209,14 @@ ol.control.ZoomSlider.prototype.amountDragged_ = function(e) { * been dragged from its minimum. * * @param {number} amount The amount the thumb has been dragged. - * @return {number} a resolution between this.minResolution_ and - * this.maxResolution_. + * @return {number} The corresponding resolution. * @private */ ol.control.ZoomSlider.prototype.resolutionForAmount_ = function(amount) { - var saneAmount = goog.math.clamp(amount, 0, 1); - return this.minResolution_ + this.range_ * saneAmount; + // FIXME do we really need this affine transform? + amount = (goog.math.clamp(amount, 0, 1) - 1) * -1; + var fn = this.getMap().getView().getResolutionForValueFunction(); + return fn(amount); }; @@ -277,12 +225,14 @@ ol.control.ZoomSlider.prototype.resolutionForAmount_ = function(amount) { * given resolution. * * @param {number} res The resolution to get the amount for. - * @return {number} an amount between 0 and 1. + * @return {number} The corresponding value (between 0 and 1). * @private */ ol.control.ZoomSlider.prototype.amountForResolution_ = function(res) { - var saneRes = goog.math.clamp(res, this.minResolution_, this.maxResolution_); - return (saneRes - this.minResolution_) / this.range_; + var fn = this.getMap().getView().getValueForResolutionFunction(); + var value = fn(res); + // FIXME do we really need this affine transform? + return (value - 1) * -1; }; @@ -298,8 +248,6 @@ ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) { var map = this.getMap(), amountDragged = this.amountDragged_(e), res = this.resolutionForAmount_(amountDragged); - goog.asserts.assert(res >= this.minResolution_ && res <= this.maxResolution_, - 'calculated new resolution is in allowed bounds.'); if (res !== this.currentResolution_) { this.currentResolution_ = res; map.getView().setResolution(res); diff --git a/test/spec/ol/control/zoomslider.test.js b/test/spec/ol/control/zoomslider.test.js index 43f4048570..c1cb4134d6 100644 --- a/test/spec/ol/control/zoomslider.test.js +++ b/test/spec/ol/control/zoomslider.test.js @@ -10,8 +10,6 @@ describe('ol.control.ZoomSlider', function() { target: target }); zoomslider = new ol.control.ZoomSlider({ - minResolution: 5000, - maxResolution: 100000, map: map }); }); @@ -25,43 +23,6 @@ describe('ol.control.ZoomSlider', function() { target = null; }); - describe('configuration & defaults', function() { - - it('has valid defaults for min and maxresolution', function() { - var zoomslider, - expectedMin = 0.5971642833948135, - expectedMax = 156543.0339, - expectedRange = expectedMax - expectedMin; - expect(function() { - zoomslider = new ol.control.ZoomSlider({}); - }).not.to.throwException(); - expect(zoomslider.minResolution_).to.be(expectedMin); - expect(zoomslider.maxResolution_).to.be(expectedMax); - expect(zoomslider.range_).to.be(expectedRange); - }); - - it('throws exception when configured with wrong resolutions', function() { - expect(function() { - zoomslider = new ol.control.ZoomSlider({ - minResolution: 50, - maxResolution: 0 - }); - }).to.throwException(); - }); - - it('can be configured with valid resolutions', function() { - expect(function() { - zoomslider = new ol.control.ZoomSlider({ - minResolution: 790, - maxResolution: 91000 - }); - }).not.to.throwException(); - expect(zoomslider.minResolution_).to.be(790); - expect(zoomslider.maxResolution_).to.be(91000); - expect(zoomslider.range_).to.be(90210); - }); - }); - describe('DOM creation', function() { it('creates the expected DOM elements', function() { var zoomSliderContainers = goog.dom.getElementsByClass( @@ -103,10 +64,7 @@ describe('ol.control.ZoomSlider', function() { describe('#direction_', function() { it('is horizontal for wide containers', function() { - var control = new ol.control.ZoomSlider({ - minResolution: 5000, - maxResolution: 100000 - }); + var control = new ol.control.ZoomSlider({}); control.element.style.width = '1000px'; control.element.style.height = '10px'; control.setMap(map); @@ -118,10 +76,7 @@ describe('ol.control.ZoomSlider', function() { }); it('is vertical for tall containers', function() { - var control = new ol.control.ZoomSlider({ - minResolution: 5000, - maxResolution: 100000 - }); + var control = new ol.control.ZoomSlider({}); control.element.style.width = '10px'; control.element.style.height = '1000px'; From 8c9153ffcae5b141e07088e8240723597055bdbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 8 Apr 2013 12:51:29 +0200 Subject: [PATCH 4/5] ZoomSlider animates to final resolution --- src/ol/control/zoomslidercontrol.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index ae565475b5..4738a9ff4c 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -18,6 +18,12 @@ goog.require('ol.control.Control'); goog.require('ol.css'); +/** + * @define {number} Animation duration. + */ +ol.control.ZOOMSLIDER_ANIMATION_DURATION = 200; + + /** * @constructor @@ -248,9 +254,14 @@ ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) { var map = this.getMap(), amountDragged = this.amountDragged_(e), res = this.resolutionForAmount_(amountDragged); - if (res !== this.currentResolution_) { - this.currentResolution_ = res; - map.getView().setResolution(res); + if (e.type === goog.fx.Dragger.EventType.DRAG) { + if (res !== this.currentResolution_) { + this.currentResolution_ = res; + map.getView().zoomWithoutConstraints(map, res); + } + } else { + map.getView().zoom(map, this.currentResolution_, undefined, + ol.control.ZOOMSLIDER_ANIMATION_DURATION); } }; From fb5ca531d0abdb4b5466a47d407db15fdf316902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89ric=20Lemoine?= Date: Mon, 8 Apr 2013 12:55:00 +0200 Subject: [PATCH 5/5] Do not assume map.getView returns an ol.View2D --- src/ol/control/zoomslidercontrol.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ol/control/zoomslidercontrol.js b/src/ol/control/zoomslidercontrol.js index 4738a9ff4c..c9bc0fe648 100644 --- a/src/ol/control/zoomslidercontrol.js +++ b/src/ol/control/zoomslidercontrol.js @@ -106,7 +106,7 @@ ol.control.ZoomSlider.CSS_CLASS_THUMB = ol.control.ZoomSlider.prototype.setMap = function(map) { goog.base(this, 'setMap', map); this.initSlider_(); - var resolution = map.getView().getResolution(); + var resolution = map.getView().getView2D().getResolution(); if (goog.isDef(resolution)) { this.currentResolution_ = resolution; this.positionThumbForResolution_(resolution); @@ -221,7 +221,7 @@ ol.control.ZoomSlider.prototype.amountDragged_ = function(e) { ol.control.ZoomSlider.prototype.resolutionForAmount_ = function(amount) { // FIXME do we really need this affine transform? amount = (goog.math.clamp(amount, 0, 1) - 1) * -1; - var fn = this.getMap().getView().getResolutionForValueFunction(); + var fn = this.getMap().getView().getView2D().getResolutionForValueFunction(); return fn(amount); }; @@ -235,7 +235,7 @@ ol.control.ZoomSlider.prototype.resolutionForAmount_ = function(amount) { * @private */ ol.control.ZoomSlider.prototype.amountForResolution_ = function(res) { - var fn = this.getMap().getView().getValueForResolutionFunction(); + var fn = this.getMap().getView().getView2D().getValueForResolutionFunction(); var value = fn(res); // FIXME do we really need this affine transform? return (value - 1) * -1; @@ -257,10 +257,10 @@ ol.control.ZoomSlider.prototype.handleSliderChange_ = function(e) { if (e.type === goog.fx.Dragger.EventType.DRAG) { if (res !== this.currentResolution_) { this.currentResolution_ = res; - map.getView().zoomWithoutConstraints(map, res); + map.getView().getView2D().zoomWithoutConstraints(map, res); } } else { - map.getView().zoom(map, this.currentResolution_, undefined, + map.getView().getView2D().zoom(map, this.currentResolution_, undefined, ol.control.ZOOMSLIDER_ANIMATION_DURATION); } };