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