From 204dee113c00dcd8353b4864033236dffffb54e4 Mon Sep 17 00:00:00 2001 From: alessio Date: Fri, 11 Oct 2013 23:43:43 +0200 Subject: [PATCH 1/2] + ScaleBar and MousePosition Improvements --- .../gxp/src/script/widgets/ScaleOverlay.js | 95 ++- .../lib/OpenLayers/Control/Attribution.js | 104 +++ .../lib/OpenLayers/Control/ScaleBar.js | 681 ++++++++++++++++++ .../externals/proj4js/lib/defs/EPSG54012.js | 2 + .../app/static/script/app/GeoExplorer.js | 6 + .../app/static/theme/app/img/scalebar-bar.png | Bin 0 -> 197 bytes .../static/theme/app/img/scalebar-marker.png | Bin 0 -> 287 bytes .../app/static/theme/app/scalebar-fat.css | 67 ++ .../app/static/theme/app/scalebar-thin.css | 57 ++ 9 files changed, 996 insertions(+), 16 deletions(-) create mode 100644 mapcomposer/app/static/externals/openlayers-ext/lib/OpenLayers/Control/Attribution.js create mode 100644 mapcomposer/app/static/externals/openlayers-ext/lib/OpenLayers/Control/ScaleBar.js create mode 100644 mapcomposer/app/static/externals/proj4js/lib/defs/EPSG54012.js create mode 100644 mapcomposer/app/static/theme/app/img/scalebar-bar.png create mode 100644 mapcomposer/app/static/theme/app/img/scalebar-marker.png create mode 100644 mapcomposer/app/static/theme/app/scalebar-fat.css create mode 100644 mapcomposer/app/static/theme/app/scalebar-thin.css diff --git a/mapcomposer/app/static/externals/gxp/src/script/widgets/ScaleOverlay.js b/mapcomposer/app/static/externals/gxp/src/script/widgets/ScaleOverlay.js index d3c875e6d..6e5e4adde 100644 --- a/mapcomposer/app/static/externals/gxp/src/script/widgets/ScaleOverlay.js +++ b/mapcomposer/app/static/externals/gxp/src/script/widgets/ScaleOverlay.js @@ -37,6 +37,11 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { */ topInUnits: null, + /** api: config[displaySystem] + * {String} Units for zoomed in on scale bar. Default is nautical. + */ + displaySystem: null, + /** api: config[bottomInUnits] * {String} Units for zoomed in on bottom bar. Default is ft. */ @@ -50,8 +55,33 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { /** api: config[enableSetScaleUnits] * {boolean} Enable or disable ComboUnits. */ - enableSetScaleUnits: false, + enableSetScaleUnits: true, + + /** api: config[divisions] + * {integer} Number of Divisions. + */ + divisions: 2, + + /** api: config[subdivisions] + * {integer} Number of SubDivisions. + */ + subdivisions: 2, + + /** api: config[showMinorMeasures] + * {boolean} Enable or disable MinorMeasures. + */ + showMinorMeasures: false, + /** api: config[singleLine] + * {boolean} Enable or disable Single Line display. + */ + singleLine: false, + + /** api: config[abbreviateLabel] + * {boolean} Enable or disable Abbreviate Labels. + */ + abbreviateLabel: false, + /** i18n */ zoomLevelText: "Zoom level", @@ -73,6 +103,9 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { if(!this.bottomOutUnits){ this.bottomOutUnits = "mi"; } + if(!this.displaySystem){ + this.displaySystem = "nautical"; + } this.cls = 'map-overlay'; if(this.map) { @@ -121,7 +154,7 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { * * Create the scale line control and add it to the panel. */ - addScaleLine: function(topOutUnits,topInUnits,bottomInUnits,bottomOutUnits) { + addScaleLine: function(topOutUnits,topInUnits,bottomInUnits,bottomOutUnits,divisions,subdivisions,showMinorMeasures,singleLine,abbreviateLabel) { if(topOutUnits && topInUnits && bottomInUnits && bottomOutUnits){ Ext.getCmp("id_box").destroy(); } @@ -146,21 +179,51 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { }, this); this.scaleLinePanel.on('render', function(){ - var scaleLineControl = this.map.getControlsByClass('OpenLayers.Control.ScaleLine'); + var mousePositionControl = this.map.getControlsByClass('OpenLayers.Control.MousePosition'); + //var scaleLineControl = this.map.getControlsByClass('OpenLayers.Control.ScaleLine'); + var scaleBarControl = this.map.getControlsByClass('OpenLayers.Control.ScaleBar'); if(topOutUnits && topInUnits && bottomInUnits && bottomOutUnits){ - this.map.removeControl(scaleLineControl[0]); + this.map.removeControl(mousePositionControl[0]); + //this.map.removeControl(scaleLineControl[0]); + this.map.removeControl(scaleBarControl[0]); } - var scaleLine = new OpenLayers.Control.ScaleLine({ - geodesic: true, - topOutUnits:topOutUnits ? topOutUnits : this.topOutUnits, - topInUnits: topInUnits ? topInUnits : this.topInUnits, - bottomInUnits: bottomInUnits ? bottomInUnits : this.bottomInUnits, - bottomOutUnits: bottomOutUnits ? bottomOutUnits : this.bottomOutUnits, + var mousePosition = new OpenLayers.Control.MousePosition({ + prefix: '' + + 'EPSG:4326 lon/lat: ', + separator: '; ', + numDigits: 3, + //emptyString: ' - ', div: this.scaleLinePanel.getEl().dom }); - - this.map.addControl(scaleLine); - scaleLine.activate(); + /*var scaleLine = new OpenLayers.Control.ScaleLine({ + geodesic: true, + topOutUnits: topOutUnits ? topOutUnits : this.topOutUnits, + topInUnits: topInUnits ? topInUnits : this.topInUnits, + bottomInUnits: bottomInUnits ? bottomInUnits : this.bottomInUnits, + bottomOutUnits: bottomOutUnits ? bottomOutUnits : this.bottomOutUnits, + div: this.scaleLinePanel.getEl().dom + });*/ + + if (topOutUnits != "hide") { + var scalebar = new OpenLayers.Control.ScaleBar({ + geodesic: true, + displaySystem: topOutUnits ? (topOutUnits == "mi" || topOutUnits == "nmi" ? "nautical" : "metric") : this.displaySystem, + divisions: divisions ? divisions : this.divisions, + subdivisions: subdivisions ? subdivisions : this.subdivisions, + showMinorMeasures: showMinorMeasures ? showMinorMeasures : this.showMinorMeasures, + singleLine: singleLine ? singleLine : this.singleLine, + abbreviateLabel: abbreviateLabel ? abbreviateLabel : this.abbreviateLabel + //div: this.scaleLinePanel.getEl().dom + }); + + this.map.addControl(scalebar); + scalebar.activate(); + } + this.map.addControl(mousePosition); + //this.map.addControl(scaleLine); + mousePosition.activate(); + //scaleLine.activate(); }, this); this.add(this.scaleLinePanel); @@ -238,7 +301,7 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { valueField: 'unitsValue', store: new Ext.data.SimpleStore({ fields: ['unitsValue', 'unitsName'], - data: [['km;m;ft;mi','Km'],['nmi;nmi;m;km','Nmi']] + data: [['km;m;ft;mi','Km'],['nmi;nmi;m;km','Nmi'],['hide;hide;hide;hide','Hide']] }), width: 90 }); @@ -262,8 +325,8 @@ gxp.ScaleOverlay = Ext.extend(Ext.Panel, { /** private: method[updateScaleUnits] * :params topOutUnits,topInUnits,bottomInUnits,bottomOutUnits */ - updateScaleUnits: function(topOutUnits,topInUnits,bottomInUnits,bottomOutUnits) { - this.addScaleLine(topOutUnits,topInUnits,bottomInUnits,bottomOutUnits); + updateScaleUnits: function(topOutUnits,topInUnits,bottomInUnits,bottomOutUnits,divisions,subdivisions,showMinorMeasures,singleLine,abbreviateLabel) { + this.addScaleLine(topOutUnits,topInUnits,bottomInUnits,bottomOutUnits,divisions,subdivisions,showMinorMeasures,singleLine,abbreviateLabel); this.doLayout(); }, /** private: method[bind] diff --git a/mapcomposer/app/static/externals/openlayers-ext/lib/OpenLayers/Control/Attribution.js b/mapcomposer/app/static/externals/openlayers-ext/lib/OpenLayers/Control/Attribution.js new file mode 100644 index 000000000..e4ab04283 --- /dev/null +++ b/mapcomposer/app/static/externals/openlayers-ext/lib/OpenLayers/Control/Attribution.js @@ -0,0 +1,104 @@ +/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the 2-clause BSD license. + * See license.txt in the OpenLayers distribution or repository for the + * full text of the license. */ + +/** + * requires OpenLayers/Control.js + */ + +/** + * Class: OpenLayers.Control.Attribution + * The attribution control adds attribution from layers to the map display. + * It uses 'attribution' property of each layer. + * + * Inherits from: + * - + */ +OpenLayers.Control.Attribution = + OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: seperator + * {String} String used to seperate layers. + */ + separator: ", ", + + /** + * APIProperty: template + * {String} Template for the attribution. This has to include the substring + * "${layers}", which will be replaced by the layer specific + * attributions, separated by . The default is "${layers}". + */ + template: "${layers}", + + /** + * Constructor: OpenLayers.Control.Attribution + * + * Parameters: + * options - {Object} Options for control. + */ + + /** + * Method: destroy + * Destroy control. + */ + destroy: function() { + this.map.events.un({ + "removelayer": this.updateAttribution, + "addlayer": this.updateAttribution, + "changelayer": this.updateAttribution, + "changebaselayer": this.updateAttribution, + scope: this + }); + + OpenLayers.Control.prototype.destroy.apply(this, arguments); + }, + + /** + * Method: draw + * Initialize control. + * + * Returns: + * {DOMElement} A reference to the DIV DOMElement containing the control + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + + this.map.events.on({ + 'changebaselayer': this.updateAttribution, + 'changelayer': this.updateAttribution, + 'addlayer': this.updateAttribution, + 'removelayer': this.updateAttribution, + scope: this + }); + this.updateAttribution(); + + return this.div; + }, + + /** + * Method: updateAttribution + * Update attribution string. + */ + updateAttribution: function() { + var attributions = []; + if (this.map && this.map.layers) { + for(var i=0, len=this.map.layers.length; i + */ +OpenLayers.Control.ScaleBar = OpenLayers.Class(OpenLayers.Control, { + + /** + * Property: element + * {Element} + */ + element: null, + + /** + * Property: scale + * {Float} Scale denominator (1 / X) - set on update + */ + scale: 1, + + /** + * APIProperty: geodesic + * {Boolean} Use geodesic measurement. Default is false. The recommended + * setting for maps in EPSG:4326 is false, and true EPSG:900913. If set to + * true, the scale will be calculated based on the horizontal size of the + * pixel in the center of the map viewport. + */ + geodesic: false, + + /** + * APIProperty: displaySystem + * {String} Display system for scale bar - metric or english supported. + * Default is metric. + */ + displaySystem: 'metric', + + /** + * APIProperty: minWidth + * {Integer} Minimum width of the scale bar in pixels. Default is 100 px. + */ + minWidth: 100, + + /** + * APIProperty: maxWidth + * Maximum width of the scale bar in pixels. Default is 200 px. + */ + maxWidth: 200, + + /** + * APIProperty: divisions + * {Integer} Number of major divisions for the scale bar. Default is 2. + */ + divisions: 2, + + /** + * APIProperty: subdivisions + * {Integer} Number of subdivisions per major division. Default is 2. + */ + subdivisions: 2, + + /** + * APIProperty: showMinorMeasures + * {Boolean} Show measures for subdivisions. Default is false. + */ + showMinorMeasures: false, + + /** + * APIProperty: abbreviateLabel + * {Boolean} Show abbreviated measurement unit (ft, km). Default is false. + */ + abbreviateLabel: false, + + /** + * APIProperty: singleLine + * {Boolean} Display scale bar length and unit after scale bar. Default + * is false. + */ + singleLine: false, + + /** + * APIProperty: align + * {String} Determines how scale bar will be aligned within the element - + * left, center, or right supported + */ + align: 'left', + + /** + * APIProperty: div + * {Element} Optional DOM element to become the container for the scale + * bar. If not provided, one will be created. + */ + div: null, + + /** + * Property: scaleText + * Text to prefix the scale denominator used as a title for the scale bar + * element. Default is "scale 1:". + */ + scaleText: "scale 1:", + + /** + * Property: thousandsSeparator + * Thousands separator for formatted scale bar measures. The title + * attribute for the scale bar always uses + * for number formatting. To + * conserve space on measures displayed with markers, the default + * thousands separator for formatting is "" (no separator). + */ + thousandsSeparator: "", + + /** + * Property: measurementProperties + * {Object} Holds display units, abbreviations, and conversion to inches + * (since we're using dpi) per measurement sytem. + */ + measurementProperties: { + english: { + units: ['miles', 'feet', 'inches'], + abbr: ['mi', 'ft', 'in'], + inches: [63360, 12, 1] + }, + metric: { + units: ['kilometers', 'meters', 'centimeters'], + abbr: ['km', 'm', 'cm'], + inches: [39370.07874, 39.370079, 0.393701] + }, + nautical: { + units: ['nautical miles', 'nautical miles', 'nautical miles'], + abbr: ['nmi', 'nmi', 'nmi'], + inches: [72913.385826772, 72913.385826772, 72913.385826772] + } + }, + + /** + * Property: limitedStyle + * {Boolean} For browsers with limited CSS support, limitedStyle will be + * set to true. In addition, this property can be set to true in the + * options sent to the constructor. If true scale bar element offsets + * will be determined based on the object. + */ + limitedStyle: false, + + /** + * Property: customStyle + * {Object} For cases where is true, a customStyle property + * can be set on the options sent to the constructor. The + * object will be extended with this custom style + * object. + */ + customStyles: null, + + /** + * Property: defaultStyles + * {Object} For cases where is true, default scale bar + * element offsets are taken from this object. Values correspond to + * pixel dimensions given in the stylesheet. + */ + defaultStyles: { + Bar: { + height: 11, top: 12, + borderLeftWidth: 0, + borderRightWidth: 0 + }, + BarAlt: { + height: 11, top: 12, + borderLeftWidth: 0, + borderRightWidth: 0 + }, + MarkerMajor: { + height: 13, width: 13, top: 12, + borderLeftWidth: 0, + borderRightWidth: 0 + }, + MarkerMinor: { + height: 13, width: 13, top: 12, + borderLeftWidth: 0, + borderRightWidth: 0 + }, + NumbersBox: { + height: 13, width: 40, top: 24 + }, + LabelBox: { + height: 15, top: -2 + }, + LabelBoxSingleLine: { + height: 15, width: 35, top: 5, left: 10 + } + }, + + /** + * Property: appliedStyles + * For cases where is true, scale bar element offsets will + * be determined based on extended with any + * . + */ + appliedStyles: null, + + /** + * Constructor: OpenLayers.Control.ScaleBar + * Create a new scale bar instance. + * + * Parameters: + * options - {Object} Optional object whose properties will be set on this + * object. + */ + initialize: function(options) { + OpenLayers.Control.prototype.initialize.apply(this, [options]); + if(!document.styleSheets) { + this.limitedStyle = true; + } + if(this.limitedStyle) { + this.appliedStyles = OpenLayers.Util.extend({}, this.defaultStyles); + OpenLayers.Util.extend(this.appliedStyles, this.customStyles); + } + // create scalebar DOM elements + this.element = document.createElement('div'); + this.element.style.position = 'relative'; + this.element.className = this.displayClass + 'Wrapper'; + this.labelContainer = document.createElement('div'); + this.labelContainer.className = this.displayClass + 'Units'; + this.labelContainer.style.position = 'absolute'; + this.graphicsContainer = document.createElement('div'); + this.graphicsContainer.style.position = 'absolute'; + this.graphicsContainer.className = this.displayClass + 'Graphics'; + this.numbersContainer = document.createElement('div'); + this.numbersContainer.style.position = 'absolute'; + this.numbersContainer.className = this.displayClass + 'Numbers'; + this.element.appendChild(this.graphicsContainer); + this.element.appendChild(this.labelContainer); + this.element.appendChild(this.numbersContainer); + }, + + /** + * APIMethod: destroy + * Destroy the control. + */ + destroy: function() { + this.map.events.unregister('moveend', this, this.onMoveend); + this.div.innerHTML = ""; + OpenLayers.Control.prototype.destroy.apply(this); + }, + + /** + * Method: draw + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + // determine offsets for graphic elements + this.dxMarkerMajor = ( + this.styleValue('MarkerMajor', 'borderLeftWidth') + + this.styleValue('MarkerMajor', 'width') + + this.styleValue('MarkerMajor', 'borderRightWidth') + ) / 2; + this.dxMarkerMinor = ( + this.styleValue('MarkerMinor', 'borderLeftWidth') + + this.styleValue('MarkerMinor', 'width') + + this.styleValue('MarkerMinor', 'borderRightWidth') + ) / 2; + this.dxBar = ( + this.styleValue('Bar', 'borderLeftWidth') + + this.styleValue('Bar', 'borderRightWidth') + ) / 2; + this.dxBarAlt = ( + this.styleValue('BarAlt', 'borderLeftWidth') + + this.styleValue('BarAlt', 'borderRightWidth') + ) / 2; + this.dxNumbersBox = this.styleValue('NumbersBox', 'width') / 2; + // set scale bar element height + var classNames = ['Bar', 'BarAlt', 'MarkerMajor', 'MarkerMinor']; + if(this.singleLine) { + classNames.push('LabelBoxSingleLine'); + } else { + classNames.push('NumbersBox', 'LabelBox'); + } + var vertDisp = 0; + for(var classIndex = 0; classIndex < classNames.length; ++classIndex) { + var cls = classNames[classIndex]; + vertDisp = Math.max( + vertDisp, + this.styleValue(cls, 'top') + this.styleValue(cls, 'height') + ); + } + this.element.style.height = vertDisp + 'px'; + this.xOffsetSingleLine = this.styleValue('LabelBoxSingleLine', 'width') + + this.styleValue('LabelBoxSingleLine', 'left'); + + this.div.appendChild(this.element); + this.map.events.register('moveend', this, this.onMoveend); + this.update(); + return this.div; + }, + + /** + * Method: onMoveend + * Registered as a listener for "moveend". + */ + onMoveend: function() { + this.update(); + }, + + /** + * APIMethod: update + * Update the scale bar after modifying properties. + * + * Parameters: + * scale - {Float} Optional scale denominator. If not specified, the + * map scale will be used. + */ + update: function(scale) { + if(this.map.baseLayer == null || !this.map.getScale()) { + return; + } + this.scale = (scale != undefined) ? scale : this.map.getScale(); + // update the element title and width + this.element.title = this.scaleText + OpenLayers.Number.format(this.scale); + this.element.style.width = this.maxWidth + 'px'; + // check each measurement unit in the display system + var comp = this.getComp(); + // get the value (subdivision length) with the lowest cumulative score + this.setSubProps(comp); + // clean out any old content from containers + this.labelContainer.innerHTML = ""; + this.graphicsContainer.innerHTML = ""; + this.numbersContainer.innerHTML = ""; + // create all divisions + var numDiv = this.divisions * this.subdivisions; + var alignmentOffset = { + left: 0 + (this.singleLine ? 0 : this.dxNumbersBox), + center: (this.maxWidth / 2) - + (numDiv * this.subProps.pixels / 2) - + (this.singleLine ? this.xOffsetSingleLine / 2 : 0), + right: this.maxWidth - + (numDiv *this.subProps.pixels) - + (this.singleLine ? this.xOffsetSingleLine : this.dxNumbersBox) + } + var xPos, measure, divNum, cls, left; + for(var di=0; di