[76] | 1 | /* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for |
---|
| 2 | * full list of contributors). Published under the Clear BSD license. |
---|
| 3 | * See http://svn.openlayers.org/trunk/openlayers/license.txt for the |
---|
| 4 | * full text of the license. */ |
---|
| 5 | |
---|
| 6 | /** |
---|
| 7 | * @requires OpenLayers/Control.js |
---|
| 8 | * @requires OpenLayers/Feature/Vector.js |
---|
| 9 | */ |
---|
| 10 | |
---|
| 11 | /** |
---|
| 12 | * Class: OpenLayers.Control.Measure |
---|
| 13 | * Allows for drawing of features for measurements. |
---|
| 14 | * |
---|
| 15 | * Inherits from: |
---|
| 16 | * - <OpenLayers.Control> |
---|
| 17 | */ |
---|
| 18 | OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, { |
---|
| 19 | |
---|
| 20 | /** |
---|
| 21 | * Constant: EVENT_TYPES |
---|
| 22 | * {Array(String)} Supported application event types. Register a listener |
---|
| 23 | * for a particular event with the following syntax: |
---|
| 24 | * (code) |
---|
| 25 | * control.events.register(type, obj, listener); |
---|
| 26 | * (end) |
---|
| 27 | * |
---|
| 28 | * Listeners will be called with a reference to an event object. The |
---|
| 29 | * properties of this event depends on exactly what happened. |
---|
| 30 | * |
---|
| 31 | * Supported control event types (in addition to those from <OpenLayers.Control>): |
---|
| 32 | * measure - Triggered when a measurement sketch is complete. Listeners |
---|
| 33 | * will receive an event with measure, units, order, and geometry |
---|
| 34 | * properties. |
---|
| 35 | * measurepartial - Triggered when a new point is added to the |
---|
| 36 | * measurement sketch. Listeners receive an event with measure, |
---|
| 37 | * units, order, and geometry. |
---|
| 38 | */ |
---|
| 39 | EVENT_TYPES: ['measure', 'measurepartial'], |
---|
| 40 | |
---|
| 41 | /** |
---|
| 42 | * APIProperty: handlerOptions |
---|
| 43 | * {Object} Used to set non-default properties on the control's handler |
---|
| 44 | */ |
---|
| 45 | handlerOptions: null, |
---|
| 46 | |
---|
| 47 | /** |
---|
| 48 | * Property: callbacks |
---|
| 49 | * {Object} The functions that are sent to the handler for callback |
---|
| 50 | */ |
---|
| 51 | callbacks: null, |
---|
| 52 | |
---|
| 53 | /** |
---|
| 54 | * Property: displaySystem |
---|
| 55 | * {String} Display system for output measurements. Supported values |
---|
| 56 | * are 'english', 'metric', and 'geographic'. Default is 'metric'. |
---|
| 57 | */ |
---|
| 58 | displaySystem: 'metric', |
---|
| 59 | |
---|
| 60 | /** |
---|
| 61 | * Property: geodesic |
---|
| 62 | * {Boolean} Calculate geodesic metrics instead of planar metrics. This |
---|
| 63 | * requires that geometries can be transformed into Geographic/WGS84 |
---|
| 64 | * (if that is not already the map projection). Default is false. |
---|
| 65 | */ |
---|
| 66 | geodesic: false, |
---|
| 67 | |
---|
| 68 | /** |
---|
| 69 | * Property: displaySystemUnits |
---|
| 70 | * {Object} Units for various measurement systems. Values are arrays |
---|
| 71 | * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing |
---|
| 72 | * order of length. |
---|
| 73 | */ |
---|
| 74 | displaySystemUnits: { |
---|
| 75 | geographic: ['dd'], |
---|
| 76 | english: ['mi', 'ft', 'in'], |
---|
| 77 | metric: ['km', 'm'] |
---|
| 78 | }, |
---|
| 79 | |
---|
| 80 | /** |
---|
| 81 | * Property: delay |
---|
| 82 | * {Number} Number of milliseconds between clicks before the event is |
---|
| 83 | * considered a double-click. The "measurepartial" event will not |
---|
| 84 | * be triggered if the sketch is completed within this time. This |
---|
| 85 | * is required for IE where creating a browser reflow (if a listener |
---|
| 86 | * is modifying the DOM by displaying the measurement values) messes |
---|
| 87 | * with the dblclick listener in the sketch handler. |
---|
| 88 | */ |
---|
| 89 | partialDelay: 300, |
---|
| 90 | |
---|
| 91 | /** |
---|
| 92 | * Property: delayedTrigger |
---|
| 93 | * {Number} Timeout id of trigger for measurepartial. |
---|
| 94 | */ |
---|
| 95 | delayedTrigger: null, |
---|
| 96 | |
---|
| 97 | /** |
---|
| 98 | * APIProperty: persist |
---|
| 99 | * {Boolean} Keep the temporary measurement sketch drawn after the |
---|
| 100 | * measurement is complete. The geometry will persist until a new |
---|
| 101 | * measurement is started, the control is deactivated, or <cancel> is |
---|
| 102 | * called. |
---|
| 103 | */ |
---|
| 104 | persist: false, |
---|
| 105 | |
---|
| 106 | /** |
---|
| 107 | * Constructor: OpenLayers.Control.Measure |
---|
| 108 | * |
---|
| 109 | * Parameters: |
---|
| 110 | * handler - {<OpenLayers.Handler>} |
---|
| 111 | * options - {Object} |
---|
| 112 | */ |
---|
| 113 | initialize: function(handler, options) { |
---|
| 114 | // concatenate events specific to measure with those from the base |
---|
| 115 | this.EVENT_TYPES = |
---|
| 116 | OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat( |
---|
| 117 | OpenLayers.Control.prototype.EVENT_TYPES |
---|
| 118 | ); |
---|
| 119 | OpenLayers.Control.prototype.initialize.apply(this, [options]); |
---|
| 120 | this.callbacks = OpenLayers.Util.extend( |
---|
| 121 | {done: this.measureComplete, point: this.measurePartial}, |
---|
| 122 | this.callbacks |
---|
| 123 | ); |
---|
| 124 | |
---|
| 125 | // let the handler options override, so old code that passes 'persist' |
---|
| 126 | // directly to the handler does not need an update |
---|
| 127 | this.handlerOptions = OpenLayers.Util.extend( |
---|
| 128 | {persist: this.persist}, this.handlerOptions |
---|
| 129 | ); |
---|
| 130 | this.handler = new handler(this, this.callbacks, this.handlerOptions); |
---|
| 131 | }, |
---|
| 132 | |
---|
| 133 | /** |
---|
| 134 | * APIMethod: cancel |
---|
| 135 | * Stop the control from measuring. If <persist> is true, the temporary |
---|
| 136 | * sketch will be erased. |
---|
| 137 | */ |
---|
| 138 | cancel: function() { |
---|
| 139 | this.handler.cancel(); |
---|
| 140 | }, |
---|
| 141 | |
---|
| 142 | /** |
---|
| 143 | * Method: updateHandler |
---|
| 144 | * |
---|
| 145 | * Parameters: |
---|
| 146 | * handler - {Function} One of the sketch handler constructors. |
---|
| 147 | * options - {Object} Options for the handler. |
---|
| 148 | */ |
---|
| 149 | updateHandler: function(handler, options) { |
---|
| 150 | var active = this.active; |
---|
| 151 | if(active) { |
---|
| 152 | this.deactivate(); |
---|
| 153 | } |
---|
| 154 | this.handler = new handler(this, this.callbacks, options); |
---|
| 155 | if(active) { |
---|
| 156 | this.activate(); |
---|
| 157 | } |
---|
| 158 | }, |
---|
| 159 | |
---|
| 160 | /** |
---|
| 161 | * Method: measureComplete |
---|
| 162 | * Called when the measurement sketch is done. |
---|
| 163 | * |
---|
| 164 | * Parameters: |
---|
| 165 | * geometry - {<OpenLayers.Geometry>} |
---|
| 166 | */ |
---|
| 167 | measureComplete: function(geometry) { |
---|
| 168 | if(this.delayedTrigger) { |
---|
| 169 | window.clearTimeout(this.delayedTrigger); |
---|
| 170 | } |
---|
| 171 | this.measure(geometry, "measure"); |
---|
| 172 | }, |
---|
| 173 | |
---|
| 174 | /** |
---|
| 175 | * Method: measurePartial |
---|
| 176 | * Called each time a new point is added to the measurement sketch. |
---|
| 177 | * |
---|
| 178 | * Parameters: |
---|
| 179 | * point - {<OpenLayers.Geometry.Point>} The last point added. |
---|
| 180 | * geometry - {<OpenLayers.Geometry>} The sketch geometry. |
---|
| 181 | */ |
---|
| 182 | measurePartial: function(point, geometry) { |
---|
| 183 | if (geometry.getLength() > 0) { |
---|
| 184 | geometry = geometry.clone(); |
---|
| 185 | this.delayedTrigger = window.setTimeout( |
---|
| 186 | OpenLayers.Function.bind(function() { |
---|
| 187 | this.measure(geometry, "measurepartial"); |
---|
| 188 | }, this), |
---|
| 189 | this.partialDelay |
---|
| 190 | ); |
---|
| 191 | } |
---|
| 192 | }, |
---|
| 193 | |
---|
| 194 | /** |
---|
| 195 | * Method: measure |
---|
| 196 | * |
---|
| 197 | * Parameters: |
---|
| 198 | * geometry - {<OpenLayers.Geometry>} |
---|
| 199 | * eventType - {String} |
---|
| 200 | */ |
---|
| 201 | measure: function(geometry, eventType) { |
---|
| 202 | var stat, order; |
---|
| 203 | if(geometry.CLASS_NAME.indexOf('LineString') > -1) { |
---|
| 204 | stat = this.getBestLength(geometry); |
---|
| 205 | order = 1; |
---|
| 206 | } else { |
---|
| 207 | stat = this.getBestArea(geometry); |
---|
| 208 | order = 2; |
---|
| 209 | } |
---|
| 210 | this.events.triggerEvent(eventType, { |
---|
| 211 | measure: stat[0], |
---|
| 212 | units: stat[1], |
---|
| 213 | order: order, |
---|
| 214 | geometry: geometry |
---|
| 215 | }); |
---|
| 216 | }, |
---|
| 217 | |
---|
| 218 | /** |
---|
| 219 | * Method: getBestArea |
---|
| 220 | * Based on the <displaySystem> returns the area of a geometry. |
---|
| 221 | * |
---|
| 222 | * Parameters: |
---|
| 223 | * geometry - {<OpenLayers.Geometry>} |
---|
| 224 | * |
---|
| 225 | * Returns: |
---|
| 226 | * {Array([Float, String])} Returns a two item array containing the |
---|
| 227 | * area and the units abbreviation. |
---|
| 228 | */ |
---|
| 229 | getBestArea: function(geometry) { |
---|
| 230 | var units = this.displaySystemUnits[this.displaySystem]; |
---|
| 231 | var unit, area; |
---|
| 232 | for(var i=0, len=units.length; i<len; ++i) { |
---|
| 233 | unit = units[i]; |
---|
| 234 | area = this.getArea(geometry, unit); |
---|
| 235 | if(area > 1) { |
---|
| 236 | break; |
---|
| 237 | } |
---|
| 238 | } |
---|
| 239 | return [area, unit]; |
---|
| 240 | }, |
---|
| 241 | |
---|
| 242 | /** |
---|
| 243 | * Method: getArea |
---|
| 244 | * |
---|
| 245 | * Parameters: |
---|
| 246 | * geometry - {<OpenLayers.Geometry>} |
---|
| 247 | * units - {String} Unit abbreviation |
---|
| 248 | * |
---|
| 249 | * Returns: |
---|
| 250 | * {Float} The geometry area in the given units. |
---|
| 251 | */ |
---|
| 252 | getArea: function(geometry, units) { |
---|
| 253 | var area, geomUnits; |
---|
| 254 | if(this.geodesic) { |
---|
| 255 | area = geometry.getGeodesicArea(this.map.getProjectionObject()); |
---|
| 256 | geomUnits = "m"; |
---|
| 257 | } else { |
---|
| 258 | area = geometry.getArea(); |
---|
| 259 | geomUnits = this.map.getUnits(); |
---|
| 260 | } |
---|
| 261 | var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; |
---|
| 262 | if(inPerDisplayUnit) { |
---|
| 263 | var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; |
---|
| 264 | area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2); |
---|
| 265 | } |
---|
| 266 | return area; |
---|
| 267 | }, |
---|
| 268 | |
---|
| 269 | /** |
---|
| 270 | * Method: getBestLength |
---|
| 271 | * Based on the <displaySystem> returns the length of a geometry. |
---|
| 272 | * |
---|
| 273 | * Parameters: |
---|
| 274 | * geometry - {<OpenLayers.Geometry>} |
---|
| 275 | * |
---|
| 276 | * Returns: |
---|
| 277 | * {Array([Float, String])} Returns a two item array containing the |
---|
| 278 | * length and the units abbreviation. |
---|
| 279 | */ |
---|
| 280 | getBestLength: function(geometry) { |
---|
| 281 | var units = this.displaySystemUnits[this.displaySystem]; |
---|
| 282 | var unit, length; |
---|
| 283 | for(var i=0, len=units.length; i<len; ++i) { |
---|
| 284 | unit = units[i]; |
---|
| 285 | length = this.getLength(geometry, unit); |
---|
| 286 | if(length > 1) { |
---|
| 287 | break; |
---|
| 288 | } |
---|
| 289 | } |
---|
| 290 | return [length, unit]; |
---|
| 291 | }, |
---|
| 292 | |
---|
| 293 | /** |
---|
| 294 | * Method: getLength |
---|
| 295 | * |
---|
| 296 | * Parameters: |
---|
| 297 | * geometry - {<OpenLayers.Geometry>} |
---|
| 298 | * units - {String} Unit abbreviation |
---|
| 299 | * |
---|
| 300 | * Returns: |
---|
| 301 | * {Float} The geometry length in the given units. |
---|
| 302 | */ |
---|
| 303 | getLength: function(geometry, units) { |
---|
| 304 | var length, geomUnits; |
---|
| 305 | if(this.geodesic) { |
---|
| 306 | length = geometry.getGeodesicLength(this.map.getProjectionObject()); |
---|
| 307 | geomUnits = "m"; |
---|
| 308 | } else { |
---|
| 309 | length = geometry.getLength(); |
---|
| 310 | geomUnits = this.map.getUnits(); |
---|
| 311 | } |
---|
| 312 | var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units]; |
---|
| 313 | if(inPerDisplayUnit) { |
---|
| 314 | var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits]; |
---|
| 315 | length *= (inPerMapUnit / inPerDisplayUnit); |
---|
| 316 | } |
---|
| 317 | return length; |
---|
| 318 | }, |
---|
| 319 | |
---|
| 320 | CLASS_NAME: "OpenLayers.Control.Measure" |
---|
| 321 | }); |
---|