Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exponential scaling to ZoomSlider #473

Merged
merged 5 commits into from Apr 8, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 1 addition & 6 deletions examples/zoomslider.js
Expand Up @@ -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
});
Expand All @@ -30,8 +27,6 @@ var createMap = function(divId) {
})
});
zoomslider = new ol.control.ZoomSlider({
minResolution: minRes,
maxResolution: maxRes,
map: map
});
return map;
Expand Down
96 changes: 29 additions & 67 deletions src/ol/control/zoomslidercontrol.js
Expand Up @@ -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');
Expand All @@ -19,45 +18,19 @@ goog.require('ol.control.Control');
goog.require('ol.css');


/**
* @define {number} Animation duration.
*/
ol.control.ZOOMSLIDER_ANIMATION_DURATION = 200;



/**
* @constructor
* @extends {ol.control.Control}
* @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.
Expand Down Expand Up @@ -127,34 +100,17 @@ 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
*/
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().getView2D().getResolution();
if (goog.isDef(resolution)) {
this.currentResolution_ = resolution;
this.positionThumbForResolution_(resolution);
}
};


Expand Down Expand Up @@ -259,13 +215,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().getView2D().getResolutionForValueFunction();
return fn(amount);
};


Expand All @@ -274,12 +231,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().getView2D().getValueForResolutionFunction();
var value = fn(res);
// FIXME do we really need this affine transform?
return (value - 1) * -1;
};


Expand All @@ -295,11 +254,14 @@ 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);
if (e.type === goog.fx.Dragger.EventType.DRAG) {
if (res !== this.currentResolution_) {
this.currentResolution_ = res;
map.getView().getView2D().zoomWithoutConstraints(map, res);
}
} else {
map.getView().getView2D().zoom(map, this.currentResolution_, undefined,
ol.control.ZOOMSLIDER_ANIMATION_DURATION);
}
};

Expand Down
88 changes: 81 additions & 7 deletions src/ol/view2d.js
Expand Up @@ -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');
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
*/
Expand All @@ -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
*/
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
}
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
var rotationConstraint = ol.RotationConstraint.createSnapToZero();
return new ol.Constraints(resolutionConstraint, rotationConstraint);
return ol.RotationConstraint.createSnapToZero();
};
49 changes: 2 additions & 47 deletions test/spec/ol/control/zoomslider.test.js
Expand Up @@ -10,8 +10,6 @@ describe('ol.control.ZoomSlider', function() {
target: target
});
zoomslider = new ol.control.ZoomSlider({
minResolution: 5000,
maxResolution: 100000,
map: map
});
});
Expand All @@ -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(
Expand Down Expand Up @@ -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);
Expand All @@ -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';

Expand Down