Project

General

Profile

Download (11.5 KB) Statistics
| Branch: | Tag: | Revision:
1
/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
2
 * full list of contributors). Published under the 2-clause BSD license.
3
 * See license.txt in the OpenLayers distribution or repository 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
     * APIProperty: events
22
     * {<OpenLayers.Events>} Events instance for listeners and triggering
23
     *     control specific events.
24
     *
25
     * Register a listener for a particular event with the following syntax:
26
     * (code)
27
     * control.events.register(type, obj, listener);
28
     * (end)
29
     *
30
     * Supported event types (in addition to those from <OpenLayers.Control.events>):
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 or if the <immediate> property is true and the
36
     *      measurement sketch is modified.  Listeners receive an event with measure,
37
     *      units, order, and geometry.
38
     */
39

    
40
    /**
41
     * APIProperty: handlerOptions
42
     * {Object} Used to set non-default properties on the control's handler
43
     */
44

    
45
    /**
46
     * Property: callbacks
47
     * {Object} The functions that are sent to the handler for callback
48
     */
49
    callbacks: null,
50

    
51
    /**
52
     * APIProperty: displaySystem
53
     * {String} Display system for output measurements.  Supported values
54
     *     are 'english', 'metric', and 'geographic'.  Default is 'metric'.
55
     */
56
    displaySystem: 'metric',
57

    
58
    /**
59
     * APIProperty: geodesic
60
     * {Boolean} Calculate geodesic metrics instead of planar metrics.  This
61
     *     requires that geometries can be transformed into Geographic/WGS84
62
     *     (if that is not already the map projection).  Default is false.
63
     */
64
    geodesic: false,
65

    
66
    /**
67
     * Property: displaySystemUnits
68
     * {Object} Units for various measurement systems.  Values are arrays
69
     *     of unit abbreviations (from OpenLayers.INCHES_PER_UNIT) in decreasing
70
     *     order of length.
71
     */
72
    displaySystemUnits: {
73
        geographic: ['dd'],
74
        english: ['mi', 'ft', 'in'],
75
        metric: ['km', 'm']
76
    },
77

    
78
    /**
79
     * Property: delay
80
     * {Number} Number of milliseconds between clicks before the event is
81
     *     considered a double-click.  The "measurepartial" event will not
82
     *     be triggered if the sketch is completed within this time.  This
83
     *     is required for IE where creating a browser reflow (if a listener
84
     *     is modifying the DOM by displaying the measurement values) messes
85
     *     with the dblclick listener in the sketch handler.
86
     */
87
    partialDelay: 300,
88

    
89
    /**
90
     * Property: delayedTrigger
91
     * {Number} Timeout id of trigger for measurepartial.
92
     */
93
    delayedTrigger: null,
94

    
95
    /**
96
     * APIProperty: persist
97
     * {Boolean} Keep the temporary measurement sketch drawn after the
98
     *     measurement is complete.  The geometry will persist until a new
99
     *     measurement is started, the control is deactivated, or <cancel> is
100
     *     called.
101
     */
102
    persist: false,
103

    
104
    /**
105
     * APIProperty: immediate
106
     * {Boolean} Activates the immediate measurement so that the "measurepartial"
107
     *     event is also fired once the measurement sketch is modified.
108
     *     Default is false.
109
     */
110
    immediate : false,
111

    
112
    /**
113
     * Constructor: OpenLayers.Control.Measure
114
     *
115
     * Parameters:
116
     * handler - {<OpenLayers.Handler>}
117
     * options - {Object}
118
     */
119
    initialize: function(handler, options) {
120
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
121
        var callbacks = {done: this.measureComplete,
122
            point: this.measurePartial};
123
        if (this.immediate){
124
            callbacks.modify = this.measureImmediate;
125
        }
126
        this.callbacks = OpenLayers.Util.extend(callbacks, this.callbacks);
127

    
128
        // let the handler options override, so old code that passes 'persist'
129
        // directly to the handler does not need an update
130
        this.handlerOptions = OpenLayers.Util.extend(
131
            {persist: this.persist}, this.handlerOptions
132
        );
133
        this.handler = new handler(this, this.callbacks, this.handlerOptions);
134
    },
135

    
136
    /**
137
     * APIMethod: deactivate
138
     */
139
    deactivate: function() {
140
        this.cancelDelay();
141
        return OpenLayers.Control.prototype.deactivate.apply(this, arguments);
142
    },
143

    
144
    /**
145
     * APIMethod: cancel
146
     * Stop the control from measuring.  If <persist> is true, the temporary
147
     *     sketch will be erased.
148
     */
149
    cancel: function() {
150
        this.cancelDelay();
151
        this.handler.cancel();
152
    },
153

    
154
    /**
155
     * APIMethod: setImmediate
156
     * Sets the <immediate> property. Changes the activity of immediate
157
     * measurement.
158
     */
159
    setImmediate: function(immediate) {
160
        this.immediate = immediate;
161
        if (this.immediate){
162
            this.callbacks.modify = this.measureImmediate;
163
        } else {
164
            delete this.callbacks.modify;
165
        }
166
    },
167

    
168
    /**
169
     * Method: updateHandler
170
     *
171
     * Parameters:
172
     * handler - {Function} One of the sketch handler constructors.
173
     * options - {Object} Options for the handler.
174
     */
175
    updateHandler: function(handler, options) {
176
        var active = this.active;
177
        if(active) {
178
            this.deactivate();
179
        }
180
        this.handler = new handler(this, this.callbacks, options);
181
        if(active) {
182
            this.activate();
183
        }
184
    },
185

    
186
    /**
187
     * Method: measureComplete
188
     * Called when the measurement sketch is done.
189
     *
190
     * Parameters:
191
     * geometry - {<OpenLayers.Geometry>}
192
     */
193
    measureComplete: function(geometry) {
194
        this.cancelDelay();
195
        this.measure(geometry, "measure");
196
    },
197

    
198
    /**
199
     * Method: measurePartial
200
     * Called each time a new point is added to the measurement sketch.
201
     *
202
     * Parameters:
203
     * point - {<OpenLayers.Geometry.Point>} The last point added.
204
     * geometry - {<OpenLayers.Geometry>} The sketch geometry.
205
     */
206
    measurePartial: function(point, geometry) {
207
        this.cancelDelay();
208
        geometry = geometry.clone();
209
        // when we're wating for a dblclick, we have to trigger measurepartial
210
        // after some delay to deal with reflow issues in IE
211
        if (this.handler.freehandMode(this.handler.evt)) {
212
            // no dblclick in freehand mode
213
            this.measure(geometry, "measurepartial");
214
        } else {
215
            this.delayedTrigger = window.setTimeout(
216
                OpenLayers.Function.bind(function() {
217
                    this.delayedTrigger = null;
218
                    this.measure(geometry, "measurepartial");
219
                }, this),
220
                this.partialDelay
221
            );
222
        }
223
    },
224

    
225
    /**
226
     * Method: measureImmediate
227
     * Called each time the measurement sketch is modified.
228
     *
229
     * Parameters:
230
     * point - {<OpenLayers.Geometry.Point>} The point at the mouse position.
231
     * feature - {<OpenLayers.Feature.Vector>} The sketch feature.
232
     * drawing - {Boolean} Indicates whether we're currently drawing.
233
     */
234
    measureImmediate : function(point, feature, drawing) {
235
        if (drawing && !this.handler.freehandMode(this.handler.evt)) {
236
            this.cancelDelay();
237
            this.measure(feature.geometry, "measurepartial");
238
        }
239
    },
240

    
241
    /**
242
     * Method: cancelDelay
243
     * Cancels the delay measurement that measurePartial began.
244
     */
245
    cancelDelay: function() {
246
        if (this.delayedTrigger !== null) {
247
            window.clearTimeout(this.delayedTrigger);
248
            this.delayedTrigger = null;
249
        }
250
    },
251

    
252
    /**
253
     * Method: measure
254
     *
255
     * Parameters:
256
     * geometry - {<OpenLayers.Geometry>}
257
     * eventType - {String}
258
     */
259
    measure: function(geometry, eventType) {
260
        var stat, order;
261
        if(geometry.CLASS_NAME.indexOf('LineString') > -1) {
262
            stat = this.getBestLength(geometry);
263
            order = 1;
264
        } else {
265
            stat = this.getBestArea(geometry);
266
            order = 2;
267
        }
268
        this.events.triggerEvent(eventType, {
269
            measure: stat[0],
270
            units: stat[1],
271
            order: order,
272
            geometry: geometry
273
        });
274
    },
275

    
276
    /**
277
     * Method: getBestArea
278
     * Based on the <displaySystem> returns the area of a geometry.
279
     *
280
     * Parameters:
281
     * geometry - {<OpenLayers.Geometry>}
282
     *
283
     * Returns:
284
     * {Array([Float, String])}  Returns a two item array containing the
285
     *     area and the units abbreviation.
286
     */
287
    getBestArea: function(geometry) {
288
        var units = this.displaySystemUnits[this.displaySystem];
289
        var unit, area;
290
        for(var i=0, len=units.length; i<len; ++i) {
291
            unit = units[i];
292
            area = this.getArea(geometry, unit);
293
            if(area > 1) {
294
                break;
295
            }
296
        }
297
        return [area, unit];
298
    },
299

    
300
    /**
301
     * Method: getArea
302
     *
303
     * Parameters:
304
     * geometry - {<OpenLayers.Geometry>}
305
     * units - {String} Unit abbreviation
306
     *
307
     * Returns:
308
     * {Float} The geometry area in the given units.
309
     */
310
    getArea: function(geometry, units) {
311
        var area, geomUnits;
312
        if(this.geodesic) {
313
            area = geometry.getGeodesicArea(this.map.getProjectionObject());
314
            geomUnits = "m";
315
        } else {
316
            area = geometry.getArea();
317
            geomUnits = this.map.getUnits();
318
        }
319
        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
320
        if(inPerDisplayUnit) {
321
            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
322
            area *= Math.pow((inPerMapUnit / inPerDisplayUnit), 2);
323
        }
324
        return area;
325
    },
326

    
327
    /**
328
     * Method: getBestLength
329
     * Based on the <displaySystem> returns the length of a geometry.
330
     *
331
     * Parameters:
332
     * geometry - {<OpenLayers.Geometry>}
333
     *
334
     * Returns:
335
     * {Array([Float, String])}  Returns a two item array containing the
336
     *     length and the units abbreviation.
337
     */
338
    getBestLength: function(geometry) {
339
        var units = this.displaySystemUnits[this.displaySystem];
340
        var unit, length;
341
        for(var i=0, len=units.length; i<len; ++i) {
342
            unit = units[i];
343
            length = this.getLength(geometry, unit);
344
            if(length > 1) {
345
                break;
346
            }
347
        }
348
        return [length, unit];
349
    },
350

    
351
    /**
352
     * Method: getLength
353
     *
354
     * Parameters:
355
     * geometry - {<OpenLayers.Geometry>}
356
     * units - {String} Unit abbreviation
357
     *
358
     * Returns:
359
     * {Float} The geometry length in the given units.
360
     */
361
    getLength: function(geometry, units) {
362
        var length, geomUnits;
363
        if(this.geodesic) {
364
            length = geometry.getGeodesicLength(this.map.getProjectionObject());
365
            geomUnits = "m";
366
        } else {
367
            length = geometry.getLength();
368
            geomUnits = this.map.getUnits();
369
        }
370
        var inPerDisplayUnit = OpenLayers.INCHES_PER_UNIT[units];
371
        if(inPerDisplayUnit) {
372
            var inPerMapUnit = OpenLayers.INCHES_PER_UNIT[geomUnits];
373
            length *= (inPerMapUnit / inPerDisplayUnit);
374
        }
375
        return length;
376
    },
377

    
378
    CLASS_NAME: "OpenLayers.Control.Measure"
379
});
(15-15/45)