From 8c2b01948bdbecb996ae8ec639b16c6010241eec Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 20:50:13 +0100 Subject: [PATCH 1/4] Add ScaleLine control --- css/ol.css | 18 ++ src/objectliterals.exports | 6 + src/ol/control/scaleline.exports | 9 + src/ol/control/scalelinecontrol.js | 278 +++++++++++++++++++++++++++++ 4 files changed, 311 insertions(+) create mode 100644 src/ol/control/scaleline.exports create mode 100644 src/ol/control/scalelinecontrol.js diff --git a/css/ol.css b/css/ol.css index 197439505e..1a2ede8923 100644 --- a/css/ol.css +++ b/css/ol.css @@ -28,6 +28,24 @@ width: 100%; height: 100%; } +.ol-scale-line { + background: rgba(0,60,136,0.3); + border-radius: 4px; + bottom: 8px; + left: 8px; + padding: 2px; + position: absolute; +} +.ol-scale-line-inner { + border: 1px solid #eeeeee; + border-top: none; + color: #eeeeee; + font-size: 10px; + font-family: 'Lucida Grande',Verdana,Geneva,Lucida,Arial,Helvetica,sans-serif; + text-align: center; + margin: 1px; + padding: 0px 2px; +} .ol-viewport .ol-unselectable { -webkit-touch-callout: none; -webkit-user-select: none; diff --git a/src/objectliterals.exports b/src/objectliterals.exports index 3e0d0e4f1a..aa6bd4376e 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -57,6 +57,12 @@ @exportObjectLiteralProperty ol.control.AttributionOptions.map ol.Map|undefined @exportObjectLiteralProperty ol.control.AttributionOptions.target Element|undefined +@exportObjectLiteral ol.control.ScaleLineOptions +@exportObjectLiteralProperty ol.control.ScaleLineOptions.map ol.Map|undefined +@exportObjectLiteralProperty ol.control.ScaleLineOptions.minimumWidth number|undefined +@exportObjectLiteralProperty ol.control.ScaleLineOptions.target Element|undefined +@exportObjectLiteralProperty ol.control.ScaleLineOptions.units ol.control.ScaleLineUnits|undefined + @exportObjectLiteral ol.control.MousePositionOptions @exportObjectLiteralProperty ol.control.MousePositionOptions.coordinateFormat ol.CoordinateFormatType|undefined @exportObjectLiteralProperty ol.control.MousePositionOptions.map ol.Map|undefined diff --git a/src/ol/control/scaleline.exports b/src/ol/control/scaleline.exports new file mode 100644 index 0000000000..6d0cbe1cd1 --- /dev/null +++ b/src/ol/control/scaleline.exports @@ -0,0 +1,9 @@ +@exportClass ol.control.ScaleLine ol.control.ScaleLineOptions +@exportProperty ol.control.ScaleLine.prototype.setMap + +@exportSymbol ol.control.ScaleLineUnits +@exportProperty ol.control.ScaleLineUnits.DEGREES +@exportProperty ol.control.ScaleLineUnits.IMPERIAL +@exportProperty ol.control.ScaleLineUnits.NAUTICAL +@exportProperty ol.control.ScaleLineUnits.METRIC +@exportProperty ol.control.ScaleLineUnits.US diff --git a/src/ol/control/scalelinecontrol.js b/src/ol/control/scalelinecontrol.js new file mode 100644 index 0000000000..a9a0c707cf --- /dev/null +++ b/src/ol/control/scalelinecontrol.js @@ -0,0 +1,278 @@ +goog.provide('ol.control.ScaleLine'); +goog.provide('ol.control.ScaleLineUnits'); + +goog.require('goog.dom'); +goog.require('goog.style'); +goog.require('ol.FrameState'); +goog.require('ol.MapEvent'); +goog.require('ol.MapEventType'); +goog.require('ol.ProjectionUnits'); +goog.require('ol.TransformFunction'); +goog.require('ol.control.Control'); +goog.require('ol.projection'); +goog.require('ol.sphere.NORMAL'); + + +/** + * @enum {string} + */ +ol.control.ScaleLineUnits = { + DEGREES: 'degrees', + IMPERIAL: 'imperial', + NAUTICAL: 'nautical', + METRIC: 'metric', + US: 'us' +}; + + + +/** + * @constructor + * @extends {ol.control.Control} + * @param {ol.control.ScaleLineOptions=} opt_options Options. + */ +ol.control.ScaleLine = function(opt_options) { + + var options = opt_options || {}; + + /** + * @private + * @type {Element} + */ + this.innerElement_ = goog.dom.createDom(goog.dom.TagName.DIV, { + 'class': 'ol-scale-line-inner' + }); + + /** + * @private + * @type {Element} + */ + this.element_ = goog.dom.createDom(goog.dom.TagName.DIV, { + 'class': 'ol-scale-line ol-unselectable' + }, this.innerElement_); + + /** + * @private + * @type {number} + */ + this.minimumWidth_ = goog.isDef(options.minimumWidth) ? + options.minimumWidth : 64; + + /** + * @private + * @type {ol.control.ScaleLineUnits} + */ + this.units_ = goog.isDef(options.units) ? + options.units : ol.control.ScaleLineUnits.METRIC; + + /** + * @private + * @type {Array.} + */ + this.listenerKeys_ = null; + + /** + * @private + * @type {boolean} + */ + this.renderedVisible_ = false; + + /** + * @private + * @type {number|undefined} + */ + this.renderedWidth_; + + /** + * @private + * @type {string} + */ + this.renderedHTML_ = ''; + + /** + * @private + * @type {?ol.TransformFunction} + */ + this.toEPSG4326_ = null; + + goog.base(this, { + element: this.element_, + map: options.map, + target: options.target + }); + +}; +goog.inherits(ol.control.ScaleLine, ol.control.Control); + + +/** + * @const + * @type {Array.} + */ +ol.control.ScaleLine.LEADING_DIGITS = [1, 2, 5]; + + +/** + * @param {ol.MapEvent} mapEvent Map event. + */ +ol.control.ScaleLine.prototype.handleMapPostrender = function(mapEvent) { + var frameState = mapEvent.frameState; + this.updateElement_(mapEvent.frameState); +}; + + +/** + * @inheritDoc + */ +ol.control.ScaleLine.prototype.setMap = function(map) { + if (!goog.isNull(this.listenerKeys_)) { + goog.array.forEach(this.listenerKeys_, goog.events.unlistenByKey); + this.listenerKeys_ = null; + } + goog.base(this, 'setMap', map); + if (!goog.isNull(map)) { + this.listenerKeys_ = [ + goog.events.listen(map, ol.MapEventType.POSTRENDER, + this.handleMapPostrender, false, this) + ]; + } +}; + + +/** + * @param {?ol.FrameState} frameState Frame state. + * @private + */ +ol.control.ScaleLine.prototype.updateElement_ = function(frameState) { + + if (goog.isNull(frameState)) { + if (this.renderedVisible_) { + goog.style.showElement(this.element_, false); + this.renderedVisible_ = false; + } + return; + } + + var view2DState = frameState.view2DState; + var center = view2DState.center; + var projection = view2DState.projection; + var pointResolution = + projection.getPointResolution(view2DState.resolution, center); + var projectionUnits = projection.getUnits(); + + var cosLatitude; + if (projectionUnits == ol.ProjectionUnits.DEGREES && + (this.units_ == ol.control.ScaleLineUnits.METRIC || + this.units_ == ol.control.ScaleLineUnits.IMPERIAL)) { + + // Convert pointResolution from degrees to meters + this.toEPSG4326_ = null; + cosLatitude = Math.cos(goog.math.toRadians(center.y)); + pointResolution *= Math.PI * cosLatitude * ol.sphere.NORMAL.radius / 180; + + } else if (projectionUnits == ol.ProjectionUnits.METERS && + this.units_ == ol.control.ScaleLineUnits.DEGREES) { + + // Convert pointResolution from meters to degrees + if (goog.isNull(this.toEPSG4326_)) { + this.toEPSG4326_ = ol.projection.getTransform( + projection, ol.projection.getFromCode('EPSG:4326')); + } + var vertex = [center.x, center.y]; + vertex = this.toEPSG4326_(vertex, vertex, 2); + cosLatitude = Math.cos(goog.math.toRadians(vertex[1])); + pointResolution *= 180 / (Math.PI * cosLatitude * ol.sphere.NORMAL.radius); + + } else { + + this.toEPSG4326_ = null; + goog.asserts.assert( + ((this.units_ == ol.control.ScaleLineUnits.METRIC || + this.units_ == ol.control.ScaleLineUnits.IMPERIAL) && + projectionUnits == ol.ProjectionUnits.METERS) || + (this.units_ == ol.control.ScaleLineUnits.DEGREES && + projectionUnits == ol.ProjectionUnits.DEGREES)); + + } + + var nominalCount = this.minimumWidth_ * pointResolution; + var suffix = ''; + if (this.units_ == ol.control.ScaleLineUnits.DEGREES) { + if (nominalCount < 1 / 60) { + suffix = '\u2033'; // seconds + pointResolution *= 3600; + } else if (nominalCount < 1) { + suffix = '\u2032'; // minutes + pointResolution *= 60; + } else { + suffix = '\u00b0'; // degrees + } + } else if (this.units_ == ol.control.ScaleLineUnits.IMPERIAL) { + if (nominalCount < 0.9144) { + suffix = 'in'; + pointResolution /= 0.0254; + } else if (nominalCount < 1609.344) { + suffix = 'ft'; + pointResolution /= 0.3048; + } else { + suffix = 'mi'; + pointResolution /= 1609.344; + } + } else if (this.units_ == ol.control.ScaleLineUnits.NAUTICAL) { + pointResolution /= 1852; + suffix = 'nm'; + } else if (this.units_ == ol.control.ScaleLineUnits.METRIC) { + if (nominalCount < 1) { + suffix = 'mm'; + pointResolution *= 1000; + } else if (nominalCount < 1000) { + suffix = 'm'; + } else { + suffix = 'km'; + pointResolution /= 1000; + } + } else if (this.units_ == ol.control.ScaleLineUnits.US) { + if (nominalCount < 0.9144) { + suffix = 'in'; + pointResolution *= 39.37; + } else if (nominalCount < 1609.344) { + suffix = 'ft'; + pointResolution /= 0.30480061; + } else { + suffix = 'mi'; + pointResolution /= 1609.3472; + } + } else { + goog.asserts.assert(false); + } + + var i = 3 * Math.floor( + Math.log(this.minimumWidth_ * pointResolution) / Math.log(10)); + var count, width; + while (true) { + count = ol.control.ScaleLine.LEADING_DIGITS[i % 3] * + Math.pow(10, Math.floor(i / 3)); + width = Math.round(count / pointResolution); + if (width >= this.minimumWidth_) { + break; + } + ++i; + } + + var html = count + suffix; + if (this.renderedHTML_ != html) { + this.innerElement_.innerHTML = html; + this.renderedHTML_ = html; + } + + if (this.renderedWidth_ != width) { + this.innerElement_.style.width = width + 'px'; + this.renderedWidth_ = width; + } + + if (!this.renderedVisible_) { + goog.style.showElement(this.element_, true); + this.renderedVisible_ = true; + } + +}; From b663fd69f184146c761a1cefb0aee7d7fbbb3ec4 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 20:51:52 +0100 Subject: [PATCH 2/4] Add scale line control options to map options --- src/objectliterals.exports | 2 ++ src/ol/map.js | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index aa6bd4376e..abaa190619 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -10,6 +10,8 @@ @exportObjectLiteralProperty ol.MapOptions.mouseWheelZoomDelta number|undefined @exportObjectLiteralProperty ol.MapOptions.renderer ol.RendererHint|undefined @exportObjectLiteralProperty ol.MapOptions.renderers Array.|undefined +@exportObjectLiteralProperty ol.MapOptions.scaleLineControl boolean|undefined +@exportObjectLiteralProperty ol.MapOptions.scaleLineUnits ol.control.ScaleLineUnits|undefined @exportObjectLiteralProperty ol.MapOptions.shiftDragZoom boolean|undefined @exportObjectLiteralProperty ol.MapOptions.target Element|string @exportObjectLiteralProperty ol.MapOptions.touchPan boolean|undefined diff --git a/src/ol/map.js b/src/ol/map.js index 7978e829d4..b0600bcdeb 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -45,6 +45,7 @@ goog.require('ol.View'); goog.require('ol.View2D'); goog.require('ol.control.Attribution'); goog.require('ol.control.Control'); +goog.require('ol.control.ScaleLine'); goog.require('ol.control.Zoom'); goog.require('ol.interaction.DblClickZoom'); goog.require('ol.interaction.DragPan'); @@ -925,6 +926,16 @@ ol.Map.createControls_ = function(mapOptions) { controls.push(new ol.control.Attribution({})); } + var scaleLineControl = goog.isDef(mapOptions.scaleLineControl) ? + mapOptions.scaleLineControl : false; + if (scaleLineControl) { + var scaleLineUnits = goog.isDef(mapOptions.scaleLineUnits) ? + mapOptions.scaleLineUnits : undefined; + controls.push(new ol.control.ScaleLine({ + units: scaleLineUnits + })); + } + var zoomControl = goog.isDef(mapOptions.zoomControl) ? mapOptions.zoomControl : true; if (zoomControl) { From f3d01dcbd3b21b0ac0836d80ea3fc04b3eef4aed Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Sun, 3 Mar 2013 20:52:03 +0100 Subject: [PATCH 3/4] Add scale line to examples --- examples/epsg-4326.js | 3 +++ examples/full-screen.js | 1 + 2 files changed, 4 insertions(+) diff --git a/examples/epsg-4326.js b/examples/epsg-4326.js index 9acf4517f8..ba95c06146 100644 --- a/examples/epsg-4326.js +++ b/examples/epsg-4326.js @@ -3,6 +3,7 @@ goog.require('ol.Coordinate'); goog.require('ol.Map'); goog.require('ol.RendererHint'); goog.require('ol.View2D'); +goog.require('ol.control.ScaleLineUnits'); goog.require('ol.layer.TileLayer'); goog.require('ol.projection'); goog.require('ol.source.TiledWMS'); @@ -39,6 +40,8 @@ var map = new ol.Map({ layers: layers, // The OSgeo server does not set cross origin headers, so we cannot use WebGL renderers: [ol.RendererHint.CANVAS, ol.RendererHint.DOM], + scaleLineControl: true, + scaleLineUnits: ol.control.ScaleLineUnits.DEGREES, target: 'map', view: new ol.View2D({ projection: epsg4326, diff --git a/examples/full-screen.js b/examples/full-screen.js index e696c2fea1..8ab4ca96ea 100644 --- a/examples/full-screen.js +++ b/examples/full-screen.js @@ -15,6 +15,7 @@ var layer = new ol.layer.TileLayer({ var map = new ol.Map({ layers: new ol.Collection([layer]), renderers: ol.RendererHints.createFromQueryData(), + scaleLineControl: true, target: 'map', view: new ol.View2D({ center: new ol.Coordinate(0, 0), From c35d07d481220b7acd00fa891c9f7cb60aec6472 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Mon, 4 Mar 2013 09:58:09 +0100 Subject: [PATCH 4/4] Rename minimumWidth to minWidth --- src/objectliterals.exports | 2 +- src/ol/control/scalelinecontrol.js | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/objectliterals.exports b/src/objectliterals.exports index abaa190619..fb5672147f 100644 --- a/src/objectliterals.exports +++ b/src/objectliterals.exports @@ -61,7 +61,7 @@ @exportObjectLiteral ol.control.ScaleLineOptions @exportObjectLiteralProperty ol.control.ScaleLineOptions.map ol.Map|undefined -@exportObjectLiteralProperty ol.control.ScaleLineOptions.minimumWidth number|undefined +@exportObjectLiteralProperty ol.control.ScaleLineOptions.minWidth number|undefined @exportObjectLiteralProperty ol.control.ScaleLineOptions.target Element|undefined @exportObjectLiteralProperty ol.control.ScaleLineOptions.units ol.control.ScaleLineUnits|undefined diff --git a/src/ol/control/scalelinecontrol.js b/src/ol/control/scalelinecontrol.js index a9a0c707cf..a2144e502c 100644 --- a/src/ol/control/scalelinecontrol.js +++ b/src/ol/control/scalelinecontrol.js @@ -55,8 +55,7 @@ ol.control.ScaleLine = function(opt_options) { * @private * @type {number} */ - this.minimumWidth_ = goog.isDef(options.minimumWidth) ? - options.minimumWidth : 64; + this.minWidth_ = goog.isDef(options.minWidth) ? options.minWidth : 64; /** * @private @@ -195,7 +194,7 @@ ol.control.ScaleLine.prototype.updateElement_ = function(frameState) { } - var nominalCount = this.minimumWidth_ * pointResolution; + var nominalCount = this.minWidth_ * pointResolution; var suffix = ''; if (this.units_ == ol.control.ScaleLineUnits.DEGREES) { if (nominalCount < 1 / 60) { @@ -247,13 +246,13 @@ ol.control.ScaleLine.prototype.updateElement_ = function(frameState) { } var i = 3 * Math.floor( - Math.log(this.minimumWidth_ * pointResolution) / Math.log(10)); + Math.log(this.minWidth_ * pointResolution) / Math.log(10)); var count, width; while (true) { count = ol.control.ScaleLine.LEADING_DIGITS[i % 3] * Math.pow(10, Math.floor(i / 3)); width = Math.round(count / pointResolution); - if (width >= this.minimumWidth_) { + if (width >= this.minWidth_) { break; } ++i;