~dongpo-deng/sahana-eden/test

« back to all changes in this revision

Viewing changes to static/scripts/gis/openlayers/lib/OpenLayers/Geometry/Collection.js

  • Committer: Deng Dongpo
  • Date: 2010-08-01 09:29:44 UTC
  • Revision ID: dongpo@dhcp-21193.iis.sinica.edu.tw-20100801092944-8t9obt4xtl7otesb
initial

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/Geometry.js
 
7
 */
 
8
 
 
9
/**
 
10
 * Class: OpenLayers.Geometry.Collection
 
11
 * A Collection is exactly what it sounds like: A collection of different 
 
12
 * Geometries. These are stored in the local parameter <components> (which
 
13
 * can be passed as a parameter to the constructor). 
 
14
 * 
 
15
 * As new geometries are added to the collection, they are NOT cloned. 
 
16
 * When removing geometries, they need to be specified by reference (ie you 
 
17
 * have to pass in the *exact* geometry to be removed).
 
18
 * 
 
19
 * The <getArea> and <getLength> functions here merely iterate through
 
20
 * the components, summing their respective areas and lengths.
 
21
 *
 
22
 * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
 
23
 *
 
24
 * Inerhits from:
 
25
 *  - <OpenLayers.Geometry> 
 
26
 */
 
27
OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
 
28
 
 
29
    /**
 
30
     * APIProperty: components
 
31
     * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
 
32
     */
 
33
    components: null,
 
34
    
 
35
    /**
 
36
     * Property: componentTypes
 
37
     * {Array(String)} An array of class names representing the types of
 
38
     * components that the collection can include.  A null value means the
 
39
     * component types are not restricted.
 
40
     */
 
41
    componentTypes: null,
 
42
 
 
43
    /**
 
44
     * Constructor: OpenLayers.Geometry.Collection
 
45
     * Creates a Geometry Collection -- a list of geoms.
 
46
     *
 
47
     * Parameters: 
 
48
     * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
 
49
     *
 
50
     */
 
51
    initialize: function (components) {
 
52
        OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
 
53
        this.components = [];
 
54
        if (components != null) {
 
55
            this.addComponents(components);
 
56
        }
 
57
    },
 
58
 
 
59
    /**
 
60
     * APIMethod: destroy
 
61
     * Destroy this geometry.
 
62
     */
 
63
    destroy: function () {
 
64
        this.components.length = 0;
 
65
        this.components = null;
 
66
    },
 
67
 
 
68
    /**
 
69
     * APIMethod: clone
 
70
     * Clone this geometry.
 
71
     *
 
72
     * Returns:
 
73
     * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
 
74
     */
 
75
    clone: function() {
 
76
        var geometry = eval("new " + this.CLASS_NAME + "()");
 
77
        for(var i=0, len=this.components.length; i<len; i++) {
 
78
            geometry.addComponent(this.components[i].clone());
 
79
        }
 
80
        
 
81
        // catch any randomly tagged-on properties
 
82
        OpenLayers.Util.applyDefaults(geometry, this);
 
83
        
 
84
        return geometry;
 
85
    },
 
86
 
 
87
    /**
 
88
     * Method: getComponentsString
 
89
     * Get a string representing the components for this collection
 
90
     * 
 
91
     * Returns:
 
92
     * {String} A string representation of the components of this geometry
 
93
     */
 
94
    getComponentsString: function(){
 
95
        var strings = [];
 
96
        for(var i=0, len=this.components.length; i<len; i++) {
 
97
            strings.push(this.components[i].toShortString()); 
 
98
        }
 
99
        return strings.join(",");
 
100
    },
 
101
 
 
102
    /**
 
103
     * APIMethod: calculateBounds
 
104
     * Recalculate the bounds by iterating through the components and 
 
105
     * calling calling extendBounds() on each item.
 
106
     */
 
107
    calculateBounds: function() {
 
108
        this.bounds = null;
 
109
        if ( this.components && this.components.length > 0) {
 
110
            this.setBounds(this.components[0].getBounds());
 
111
            for (var i=1, len=this.components.length; i<len; i++) {
 
112
                this.extendBounds(this.components[i].getBounds());
 
113
            }
 
114
        }
 
115
    },
 
116
 
 
117
    /**
 
118
     * APIMethod: addComponents
 
119
     * Add components to this geometry.
 
120
     *
 
121
     * Parameters:
 
122
     * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
 
123
     */
 
124
    addComponents: function(components){
 
125
        if(!(components instanceof Array)) {
 
126
            components = [components];
 
127
        }
 
128
        for(var i=0, len=components.length; i<len; i++) {
 
129
            this.addComponent(components[i]);
 
130
        }
 
131
    },
 
132
 
 
133
    /**
 
134
     * Method: addComponent
 
135
     * Add a new component (geometry) to the collection.  If this.componentTypes
 
136
     * is set, then the component class name must be in the componentTypes array.
 
137
     *
 
138
     * The bounds cache is reset.
 
139
     * 
 
140
     * Parameters:
 
141
     * component - {<OpenLayers.Geometry>} A geometry to add
 
142
     * index - {int} Optional index into the array to insert the component
 
143
     *
 
144
     * Returns:
 
145
     * {Boolean} The component geometry was successfully added
 
146
     */    
 
147
    addComponent: function(component, index) {
 
148
        var added = false;
 
149
        if(component) {
 
150
            if(this.componentTypes == null ||
 
151
               (OpenLayers.Util.indexOf(this.componentTypes,
 
152
                                        component.CLASS_NAME) > -1)) {
 
153
 
 
154
                if(index != null && (index < this.components.length)) {
 
155
                    var components1 = this.components.slice(0, index);
 
156
                    var components2 = this.components.slice(index, 
 
157
                                                           this.components.length);
 
158
                    components1.push(component);
 
159
                    this.components = components1.concat(components2);
 
160
                } else {
 
161
                    this.components.push(component);
 
162
                }
 
163
                component.parent = this;
 
164
                this.clearBounds();
 
165
                added = true;
 
166
            }
 
167
        }
 
168
        return added;
 
169
    },
 
170
    
 
171
    /**
 
172
     * APIMethod: removeComponents
 
173
     * Remove components from this geometry.
 
174
     *
 
175
     * Parameters:
 
176
     * components - {Array(<OpenLayers.Geometry>)} The components to be removed
 
177
     */
 
178
    removeComponents: function(components) {
 
179
        if(!(components instanceof Array)) {
 
180
            components = [components];
 
181
        }
 
182
        for(var i=components.length-1; i>=0; --i) {
 
183
            this.removeComponent(components[i]);
 
184
        }
 
185
    },
 
186
    
 
187
    /**
 
188
     * Method: removeComponent
 
189
     * Remove a component from this geometry.
 
190
     *
 
191
     * Parameters:
 
192
     * component - {<OpenLayers.Geometry>} 
 
193
     */
 
194
    removeComponent: function(component) {
 
195
        
 
196
        OpenLayers.Util.removeItem(this.components, component);
 
197
        
 
198
        // clearBounds() so that it gets recalculated on the next call
 
199
        // to this.getBounds();
 
200
        this.clearBounds();
 
201
    },
 
202
 
 
203
    /**
 
204
     * APIMethod: getLength
 
205
     * Calculate the length of this geometry
 
206
     *
 
207
     * Returns:
 
208
     * {Float} The length of the geometry
 
209
     */
 
210
    getLength: function() {
 
211
        var length = 0.0;
 
212
        for (var i=0, len=this.components.length; i<len; i++) {
 
213
            length += this.components[i].getLength();
 
214
        }
 
215
        return length;
 
216
    },
 
217
    
 
218
    /**
 
219
     * APIMethod: getArea
 
220
     * Calculate the area of this geometry. Note how this function is overridden
 
221
     * in <OpenLayers.Geometry.Polygon>.
 
222
     *
 
223
     * Returns:
 
224
     * {Float} The area of the collection by summing its parts
 
225
     */
 
226
    getArea: function() {
 
227
        var area = 0.0;
 
228
        for (var i=0, len=this.components.length; i<len; i++) {
 
229
            area += this.components[i].getArea();
 
230
        }
 
231
        return area;
 
232
    },
 
233
 
 
234
    /** 
 
235
     * APIMethod: getGeodesicArea
 
236
     * Calculate the approximate area of the polygon were it projected onto
 
237
     *     the earth.
 
238
     *
 
239
     * Parameters:
 
240
     * projection - {<OpenLayers.Projection>} The spatial reference system
 
241
     *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
 
242
     *     assumed.
 
243
     * 
 
244
     * Reference:
 
245
     * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
 
246
     *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
 
247
     *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
 
248
     *
 
249
     * Returns:
 
250
     * {float} The approximate geodesic area of the geometry in square meters.
 
251
     */
 
252
    getGeodesicArea: function(projection) {
 
253
        var area = 0.0;
 
254
        for(var i=0, len=this.components.length; i<len; i++) {
 
255
            area += this.components[i].getGeodesicArea(projection);
 
256
        }
 
257
        return area;
 
258
    },
 
259
    
 
260
    /**
 
261
     * APIMethod: getCentroid
 
262
     *
 
263
     * Returns:
 
264
     * {<OpenLayers.Geometry.Point>} The centroid of the collection
 
265
     */
 
266
    getCentroid: function() {
 
267
        return this.components.length && this.components[0].getCentroid();
 
268
        /*
 
269
        var centroid;
 
270
        for (var i=0, len=this.components.length; i<len; i++) {
 
271
            if (!centroid) {
 
272
                centroid = this.components[i].getCentroid();
 
273
            } else {
 
274
                centroid.resize(this.components[i].getCentroid(), 0.5);
 
275
            }
 
276
        }
 
277
        return centroid;
 
278
        */
 
279
    },
 
280
 
 
281
    /**
 
282
     * APIMethod: getGeodesicLength
 
283
     * Calculate the approximate length of the geometry were it projected onto
 
284
     *     the earth.
 
285
     *
 
286
     * projection - {<OpenLayers.Projection>} The spatial reference system
 
287
     *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
 
288
     *     assumed.
 
289
     * 
 
290
     * Returns:
 
291
     * {Float} The appoximate geodesic length of the geometry in meters.
 
292
     */
 
293
    getGeodesicLength: function(projection) {
 
294
        var length = 0.0;
 
295
        for(var i=0, len=this.components.length; i<len; i++) {
 
296
            length += this.components[i].getGeodesicLength(projection);
 
297
        }
 
298
        return length;
 
299
    },
 
300
 
 
301
    /**
 
302
     * APIMethod: move
 
303
     * Moves a geometry by the given displacement along positive x and y axes.
 
304
     *     This modifies the position of the geometry and clears the cached
 
305
     *     bounds.
 
306
     *
 
307
     * Parameters:
 
308
     * x - {Float} Distance to move geometry in positive x direction. 
 
309
     * y - {Float} Distance to move geometry in positive y direction.
 
310
     */
 
311
    move: function(x, y) {
 
312
        for(var i=0, len=this.components.length; i<len; i++) {
 
313
            this.components[i].move(x, y);
 
314
        }
 
315
    },
 
316
 
 
317
    /**
 
318
     * APIMethod: rotate
 
319
     * Rotate a geometry around some origin
 
320
     *
 
321
     * Parameters:
 
322
     * angle - {Float} Rotation angle in degrees (measured counterclockwise
 
323
     *                 from the positive x-axis)
 
324
     * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
 
325
     */
 
326
    rotate: function(angle, origin) {
 
327
        for(var i=0, len=this.components.length; i<len; ++i) {
 
328
            this.components[i].rotate(angle, origin);
 
329
        }
 
330
    },
 
331
 
 
332
    /**
 
333
     * APIMethod: resize
 
334
     * Resize a geometry relative to some origin.  Use this method to apply
 
335
     *     a uniform scaling to a geometry.
 
336
     *
 
337
     * Parameters:
 
338
     * scale - {Float} Factor by which to scale the geometry.  A scale of 2
 
339
     *                 doubles the size of the geometry in each dimension
 
340
     *                 (lines, for example, will be twice as long, and polygons
 
341
     *                 will have four times the area).
 
342
     * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
 
343
     * ratio - {Float} Optional x:y ratio for resizing.  Default ratio is 1.
 
344
     * 
 
345
     * Returns:
 
346
     * {OpenLayers.Geometry} - The current geometry. 
 
347
     */
 
348
    resize: function(scale, origin, ratio) {
 
349
        for(var i=0; i<this.components.length; ++i) {
 
350
            this.components[i].resize(scale, origin, ratio);
 
351
        }
 
352
        return this;
 
353
    },
 
354
 
 
355
    /**
 
356
     * APIMethod: distanceTo
 
357
     * Calculate the closest distance between two geometries (on the x-y plane).
 
358
     *
 
359
     * Parameters:
 
360
     * geometry - {<OpenLayers.Geometry>} The target geometry.
 
361
     * options - {Object} Optional properties for configuring the distance
 
362
     *     calculation.
 
363
     *
 
364
     * Valid options:
 
365
     * details - {Boolean} Return details from the distance calculation.
 
366
     *     Default is false.
 
367
     * edge - {Boolean} Calculate the distance from this geometry to the
 
368
     *     nearest edge of the target geometry.  Default is true.  If true,
 
369
     *     calling distanceTo from a geometry that is wholly contained within
 
370
     *     the target will result in a non-zero distance.  If false, whenever
 
371
     *     geometries intersect, calling distanceTo will return 0.  If false,
 
372
     *     details cannot be returned.
 
373
     *
 
374
     * Returns:
 
375
     * {Number | Object} The distance between this geometry and the target.
 
376
     *     If details is true, the return will be an object with distance,
 
377
     *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
 
378
     *     the coordinates of the closest point on this geometry. The x1 and y1
 
379
     *     properties represent the coordinates of the closest point on the
 
380
     *     target geometry.
 
381
     */
 
382
    distanceTo: function(geometry, options) {
 
383
        var edge = !(options && options.edge === false);
 
384
        var details = edge && options && options.details;
 
385
        var result, best, distance;
 
386
        var min = Number.POSITIVE_INFINITY;
 
387
        for(var i=0, len=this.components.length; i<len; ++i) {
 
388
            result = this.components[i].distanceTo(geometry, options);
 
389
            distance = details ? result.distance : result;
 
390
            if(distance < min) {
 
391
                min = distance;
 
392
                best = result;
 
393
                if(min == 0) {
 
394
                    break;
 
395
                }
 
396
            }
 
397
        }
 
398
        return best;
 
399
    },
 
400
 
 
401
    /** 
 
402
     * APIMethod: equals
 
403
     * Determine whether another geometry is equivalent to this one.  Geometries
 
404
     *     are considered equivalent if all components have the same coordinates.
 
405
     * 
 
406
     * Parameters:
 
407
     * geom - {<OpenLayers.Geometry>} The geometry to test. 
 
408
     *
 
409
     * Returns:
 
410
     * {Boolean} The supplied geometry is equivalent to this geometry.
 
411
     */
 
412
    equals: function(geometry) {
 
413
        var equivalent = true;
 
414
        if(!geometry || !geometry.CLASS_NAME ||
 
415
           (this.CLASS_NAME != geometry.CLASS_NAME)) {
 
416
            equivalent = false;
 
417
        } else if(!(geometry.components instanceof Array) ||
 
418
                  (geometry.components.length != this.components.length)) {
 
419
            equivalent = false;
 
420
        } else {
 
421
            for(var i=0, len=this.components.length; i<len; ++i) {
 
422
                if(!this.components[i].equals(geometry.components[i])) {
 
423
                    equivalent = false;
 
424
                    break;
 
425
                }
 
426
            }
 
427
        }
 
428
        return equivalent;
 
429
    },
 
430
 
 
431
    /**
 
432
     * APIMethod: transform
 
433
     * Reproject the components geometry from source to dest.
 
434
     * 
 
435
     * Parameters:
 
436
     * source - {<OpenLayers.Projection>} 
 
437
     * dest - {<OpenLayers.Projection>}
 
438
     * 
 
439
     * Returns:
 
440
     * {<OpenLayers.Geometry>} 
 
441
     */
 
442
    transform: function(source, dest) {
 
443
        if (source && dest) {
 
444
            for (var i=0, len=this.components.length; i<len; i++) {  
 
445
                var component = this.components[i];
 
446
                component.transform(source, dest);
 
447
            }
 
448
            this.bounds = null;
 
449
        }
 
450
        return this;
 
451
    },
 
452
 
 
453
    /**
 
454
     * APIMethod: intersects
 
455
     * Determine if the input geometry intersects this one.
 
456
     *
 
457
     * Parameters:
 
458
     * geometry - {<OpenLayers.Geometry>} Any type of geometry.
 
459
     *
 
460
     * Returns:
 
461
     * {Boolean} The input geometry intersects this one.
 
462
     */
 
463
    intersects: function(geometry) {
 
464
        var intersect = false;
 
465
        for(var i=0, len=this.components.length; i<len; ++ i) {
 
466
            intersect = geometry.intersects(this.components[i]);
 
467
            if(intersect) {
 
468
                break;
 
469
            }
 
470
        }
 
471
        return intersect;
 
472
    },
 
473
 
 
474
    /**
 
475
     * APIMethod: getVertices
 
476
     * Return a list of all points in this geometry.
 
477
     *
 
478
     * Parameters:
 
479
     * nodes - {Boolean} For lines, only return vertices that are
 
480
     *     endpoints.  If false, for lines, only vertices that are not
 
481
     *     endpoints will be returned.  If not provided, all vertices will
 
482
     *     be returned.
 
483
     *
 
484
     * Returns:
 
485
     * {Array} A list of all vertices in the geometry.
 
486
     */
 
487
    getVertices: function(nodes) {
 
488
        var vertices = [];
 
489
        for(var i=0, len=this.components.length; i<len; ++i) {
 
490
            Array.prototype.push.apply(
 
491
                vertices, this.components[i].getVertices(nodes)
 
492
            );
 
493
        }
 
494
        return vertices;
 
495
    },
 
496
 
 
497
 
 
498
    CLASS_NAME: "OpenLayers.Geometry.Collection"
 
499
});