--- a/labs/openlayers/lib/OpenLayers/Control/Graticule.js +++ b/labs/openlayers/lib/OpenLayers/Control/Graticule.js @@ -1,1 +1,374 @@ - +/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for + * full list of contributors). Published under the Clear BSD license. + * See http://svn.openlayers.org/trunk/openlayers/license.txt for the + * full text of the license. */ + +/** + * @requires OpenLayers/Control.js + */ + +/** + * Class: OpenLayers.Control.Graticule + * The Graticule displays a grid of latitude/longitude lines reprojected on + * the map. + * + * Inherits from: + * - + * + */ +OpenLayers.Control.Graticule = OpenLayers.Class(OpenLayers.Control, { + + /** + * APIProperty: autoActivate + * {Boolean} Activate the control when it is added to a map. Default is + * true. + */ + autoActivate: true, + + /** + * APIProperty: intervals + * {Array(Float)} A list of possible graticule widths in degrees. + */ + intervals: [ 45, 30, 20, 10, 5, 2, 1, + 0.5, 0.2, 0.1, 0.05, 0.01, + 0.005, 0.002, 0.001 ], + + /** + * APIProperty: displayInLayerSwitcher + * {Boolean} Allows the Graticule control to be switched on and off by + * LayerSwitcher control. Defaults is true. + */ + displayInLayerSwitcher: true, + + /** + * APIProperty: visible + * {Boolean} should the graticule be initially visible (default=true) + */ + visible: true, + + /** + * APIProperty: numPoints + * {Integer} The number of points to use in each graticule line. Higher + * numbers result in a smoother curve for projected maps + */ + numPoints: 50, + + /** + * APIProperty: targetSize + * {Integer} The maximum size of the grid in pixels on the map + */ + targetSize: 200, + + /** + * APIProperty: layerName + * {String} The name to be displayed in the layer switcher, default is set + * by {}. + */ + layerName: null, + + /** + * APIProperty: labelled + * {Boolean} Should the graticule lines be labelled?. default=true + */ + labelled: true, + + /** + * APIProperty: labelFormat + * {String} the format of the labels, default = 'dm'. See + * for other options. + */ + labelFormat: 'dm', + + /** + * APIProperty: lineSymbolizer + * {symbolizer} the symbolizer used to render lines + */ + lineSymbolizer: { + strokeColor: "#333", + strokeWidth: 1, + strokeOpacity: 0.5 + }, + + /** + * APIProperty: labelSymbolizer + * {symbolizer} the symbolizer used to render labels + */ + labelSymbolizer: {}, + + /** + * Property: gratLayer + * {OpenLayers.Layer.Vector} vector layer used to draw the graticule on + */ + gratLayer: null, + + /** + * Constructor: OpenLayers.Control.Graticule + * Create a new graticule control to display a grid of latitude longitude + * lines. + * + * Parameters: + * options - {Object} An optional object whose properties will be used + * to extend the control. + */ + initialize: function(options) { + options = options || {}; + options.layerName = options.layerName || OpenLayers.i18n("graticule"); + OpenLayers.Control.prototype.initialize.apply(this, [options]); + + this.labelSymbolizer.stroke = false; + this.labelSymbolizer.fill = false; + this.labelSymbolizer.label = "${label}"; + this.labelSymbolizer.labelAlign = "${labelAlign}"; + this.labelSymbolizer.labelXOffset = "${xOffset}"; + this.labelSymbolizer.labelYOffset = "${yOffset}"; + }, + + /** + * APIMethod: destroy + */ + destroy: function() { + this.deactivate(); + OpenLayers.Control.prototype.destroy.apply(this, arguments); + if (this.gratLayer) { + this.gratLayer.destroy(); + this.gratLayer = null; + } + }, + + /** + * Method: draw + * + * initializes the graticule layer and does the initial update + * + * Returns: + * {DOMElement} + */ + draw: function() { + OpenLayers.Control.prototype.draw.apply(this, arguments); + if (!this.gratLayer) { + var gratStyle = new OpenLayers.Style({},{ + rules: [new OpenLayers.Rule({'symbolizer': + {"Point":this.labelSymbolizer, + "Line":this.lineSymbolizer} + })] + }); + this.gratLayer = new OpenLayers.Layer.Vector(this.layerName, { + styleMap: new OpenLayers.StyleMap({'default':gratStyle}), + visibility: this.visible, + displayInLayerSwitcher: this.displayInLayerSwitcher + }); + } + return this.div; + }, + + /** + * APIMethod: activate + */ + activate: function() { + if (OpenLayers.Control.prototype.activate.apply(this, arguments)) { + this.map.addLayer(this.gratLayer); + this.map.events.register('moveend', this, this.update); + this.update(); + return true; + } else { + return false; + } + }, + + /** + * APIMethod: deactivate + */ + deactivate: function() { + if (OpenLayers.Control.prototype.deactivate.apply(this, arguments)) { + this.map.events.unregister('moveend', this, this.update); + this.map.removeLayer(this.gratLayer); + return true; + } else { + return false; + } + }, + /** + * Method: update + * + * calculates the grid to be displayed and actually draws it + * + * Returns: + * {DOMElement} + */ + update: function() { + //wait for the map to be initialized before proceeding + var mapBounds = this.map.getExtent(); + if (!mapBounds) { + return; + } + + //clear out the old grid + this.gratLayer.destroyFeatures(); + + //get the projection objects required + var llProj = new OpenLayers.Projection("EPSG:4326"); + var mapProj = this.map.getProjectionObject(); + var mapRes = this.map.getResolution(); + + //if the map is in lon/lat, then the lines are straight and only one + //point is required + if (mapProj.proj && mapProj.proj.projName == "longlat") { + this.numPoints = 1; + } + + //get the map center in EPSG:4326 + var mapCenter = this.map.getCenter(); //lon and lat here are really map x and y + var mapCenterLL = new OpenLayers.Pixel(mapCenter.lon, mapCenter.lat); + OpenLayers.Projection.transform(mapCenterLL, mapProj, llProj); + + /* This block of code determines the lon/lat interval to use for the + * grid by calculating the diagonal size of one grid cell at the map + * center. Iterates through the intervals array until the diagonal + * length is less than the targetSize option. + */ + //find lat/lon interval that results in a grid of less than the target size + var testSq = this.targetSize*mapRes; + testSq *= testSq; //compare squares rather than doing a square root to save time + var llInterval; + for (var i=0; i= mapBounds.bottom && !labelPoint) { + labelPoint = gridPoint; + } + } + if (this.labelled) { + //keep track of when this grid line crosses the map bounds to set + //the label position + //labels along the bottom, add 10 pixel offset up into the map + //TODO add option for labels on top + var labelPos = new OpenLayers.Geometry.Point(labelPoint.x,mapBounds.bottom); + var labelAttrs = { + value: lon, + label: this.labelled?OpenLayers.Util.getFormattedLonLat(lon, "lon", this.labelFormat):"", + labelAlign: "cb", + xOffset: 0, + yOffset: 2 + }; + this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs)); + } + var geom = new OpenLayers.Geometry.LineString(pointList); + lines.push(new OpenLayers.Feature.Vector(geom)); + } + + //now draw the lines of constant latitude + for (var j=0; j < centerLonPoints.length; ++j) { + lat = centerLonPoints[j].y; + if (lat<-90 || lat>90) { //latitudes only valid between -90 and 90 + continue; + } + var pointList = []; + var lonStart = centerLatPoints[0].x; + var lonEnd = centerLatPoints[centerLatPoints.length - 1].x; + var lonDelta = (lonEnd - lonStart)/this.numPoints; + var lon = lonStart; + var labelPoint = null; + for(var i=0; i <= this.numPoints ; ++i) { + var gridPoint = new OpenLayers.Geometry.Point(lon,lat); + gridPoint.transform(llProj, mapProj); + pointList.push(gridPoint); + lon += lonDelta; + if (gridPoint.x < mapBounds.right) { + labelPoint = gridPoint; + } + } + if (this.labelled) { + //keep track of when this grid line crosses the map bounds to set + //the label position + //labels along the right, 30 pixel offset left into the map + //TODO add option for labels on left + var labelPos = new OpenLayers.Geometry.Point(mapBounds.right, labelPoint.y); + var labelAttrs = { + value: lat, + label: this.labelled?OpenLayers.Util.getFormattedLonLat(lat, "lat", this.labelFormat):"", + labelAlign: "rb", + xOffset: -2, + yOffset: 2 + }; + this.gratLayer.addFeatures(new OpenLayers.Feature.Vector(labelPos,labelAttrs)); + } + var geom = new OpenLayers.Geometry.LineString(pointList); + lines.push(new OpenLayers.Feature.Vector(geom)); + } + this.gratLayer.addFeatures(lines); + }, + + CLASS_NAME: "OpenLayers.Control.Graticule" +}); + +