1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2 * license. See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3 * full text of the license. */
6 * @requires OpenLayers/Control.js
7 * @requires OpenLayers/Feature/Vector.js
11 * Class: OpenLayers.Control.Measure
12 * Allows for drawing of features for measurements.
15 * - <OpenLayers.Control>
17 OpenLayers.Control.Measure = OpenLayers.Class(OpenLayers.Control, {
20 * Constant: EVENT_TYPES
21 * {Array(String)} Supported application event types. Register a listener
22 * for a particular event with the following syntax:
24 * control.events.register(type, obj, listener);
27 * Listeners will be called with a reference to an event object. The
28 * properties of this event depends on exactly what happened.
30 * Supported control event types (in addition to those from <OpenLayers.Control>):
31 * measure - Triggered when a measurement sketch is complete. Listeners
32 * will receive an event with measure, units, order, and geometry
34 * measurepartial - Triggered when a new point is added to the
35 * measurement sketch. Listeners receive an event with measure,
36 * units, order, and geometry.
38 EVENT_TYPES: ['measure', 'measurepartial'],
41 * APIProperty: handlerOptions
42 * {Object} Used to set non-default properties on the control's handler
48 * {Object} The functions that are sent to the handler for callback
53 * Property: displaySystem
54 * {String} Display system for output measurements. Supported values
55 * are 'english', 'metric', and 'geographic'. Default is 'metric'.
57 displaySystem: 'metric',
61 * {Boolean} Calculate geodesic metrics instead of planar metrics. This
62 * requires that geometries can be transformed into Geographic/WGS84
63 * (if that is not already the map projection). Default is false.
68 * Property: displaySystemUnits
69 * {Object} Units for various measurement systems. Values are arrays
70 * of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
75 english: ['mi', 'ft', 'in'],
81 * {Number} Number of milliseconds between clicks before the event is
82 * considered a double-click. The "measurepartial" event will not
83 * be triggered if the sketch is completed within this time. This
84 * is required for IE where creating a browser reflow (if a listener
85 * is modifying the DOM by displaying the measurement values) messes
86 * with the dblclick listener in the sketch handler.
91 * Property: delayedTrigger
92 * {Number} Timeout id of trigger for measurepartial.
97 * APIProperty: persist
98 * {Boolean} Keep the temporary measurement sketch drawn after the
99 * measurement is complete. The geometry will persist until a new
100 * measurement is started, the control is deactivated, or <cancel> is
106 * Constructor: OpenLayers.Control.Measure
109 * handler - {<OpenLayers.Handler>}
112 initialize: function(handler, options) {
113 // concatenate events specific to measure with those from the base
115 OpenLayers.Control.Measure.prototype.EVENT_TYPES.concat(
116 OpenLayers.Control.prototype.EVENT_TYPES
118 OpenLayers.Control.prototype.initialize.apply(this, [options]);
119 this.callbacks = OpenLayers.Util.extend(
120 {done: this.measureComplete, point: this.measurePartial},
124 // let the handler options override, so old code that passes 'persist'
125 // directly to the handler does not need an update
126 this.handlerOptions = OpenLayers.Util.extend(
127 {persist: this.persist}, this.handlerOptions
129 this.handler = new handler(this, this.callbacks, this.handlerOptions);
134 * Stop the control from measuring. If <persist> is true, the temporary
135 * sketch will be erased.
138 this.handler.cancel();
142 * Method: updateHandler
145 * handler - {Function} One of the sketch handler constructors.
146 * options - {Object} Options for the handler.
148 updateHandler: function(handler, options) {
149 var active = this.active;
153 this.handler = new handler(this, this.callbacks, options);
160 * Method: measureComplete
161 * Called when the measurement sketch is done.
164 * geometry - {<OpenLayers.Geometry>}
166 measureComplete: function(geometry) {
167 if(this.delayedTrigger) {
168 window.clearTimeout(this.delayedTrigger);
170 this.measure(geometry, "measure");
174 * Method: measurePartial
175 * Called each time a new point is added to the measurement sketch.
178 * point - {<OpenLayers.Geometry.Point>} The last point added.
179 * geometry - {<OpenLayers.Geometry>} The sketch geometry.
181 measurePartial: function(point, geometry) {
182 this.delayedTrigger = window.setTimeout(
183 OpenLayers.Function.bind(function() {
184 this.measure(geometry, "measurepartial");
194 * geometry - {<OpenLayers.Geometry>}
195 * eventType - {String}
197 measure: function(geometry, eventType) {
199 if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
200 stat = this.getBestLength(geometry);
203 stat = this.getBestArea(geometry);
206 this.events.triggerEvent(eventType, {
215 * Method: getBestArea
216 * Based on the <displaySystem> returns the area of a geometry.
219 * geometry - {<OpenLayers.Geometry>}
222 * {Array([Float, String])} Returns a two item array containing the
223 * area and the units abbreviation.
225 getBestArea: function(geometry) {
226 var units = this.displaySystemUnits[this.displaySystem];
228 for(var i=0, len=units.length; i<len; ++i) {
230 area = this.getArea(geometry, unit);
242 * geometry - {<OpenLayers.Geometry>}
243 * units - {String} Unit abbreviation
246 * {Float} The geometry area in the given units.
248 getArea: function(geometry, units) {
251 area = geometry.getGeodesicArea(this.map.getProjectionObject());
254 area = geometry.getArea();
255 geomUnits = this.map.getUnits();
257 var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
258 if(inPerDisplayUnit) {
259 var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
260 area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
266 * Method: getBestLength
267 * Based on the <displaySystem> returns the length of a geometry.
270 * geometry - {<OpenLayers.Geometry>}
273 * {Array([Float, String])} Returns a two item array containing the
274 * length and the units abbreviation.
276 getBestLength: function(geometry) {
277 var units = this.displaySystemUnits[this.displaySystem];
279 for(var i=0, len=units.length; i<len; ++i) {
281 length = this.getLength(geometry, unit);
286 return [length, unit];
293 * geometry - {<OpenLayers.Geometry>}
294 * units - {String} Unit abbreviation
297 * {Float} The geometry length in the given units.
299 getLength: function(geometry, units) {
300 var length, geomUnits;
302 length = geometry.getGeodesicLength(this.map.getProjectionObject());
305 length = geometry.getLength();
306 geomUnits = this.map.getUnits();
308 var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
309 if(inPerDisplayUnit) {
310 var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
311 length *= (inPerMapUnit / inPerDisplayUnit);
316 CLASS_NAME: "OpenLayers.Control.Measure"