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/Geometry.js
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).
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).
19
* The <getArea> and <getLength> functions here merely iterate through
20
* the components, summing their respective areas and lengths.
22
* Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
25
* - <OpenLayers.Geometry>
27
OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
30
* APIProperty: components
31
* {Array(<OpenLayers.Geometry>)} The component parts of this geometry
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.
44
* Constructor: OpenLayers.Geometry.Collection
45
* Creates a Geometry Collection -- a list of geoms.
48
* components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
51
initialize: function (components) {
52
OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
54
if (components != null) {
55
this.addComponents(components);
61
* Destroy this geometry.
63
destroy: function () {
64
this.components.length = 0;
65
this.components = null;
70
* Clone this geometry.
73
* {<OpenLayers.Geometry.Collection>} An exact clone of this collection
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());
81
// catch any randomly tagged-on properties
82
OpenLayers.Util.applyDefaults(geometry, this);
88
* Method: getComponentsString
89
* Get a string representing the components for this collection
92
* {String} A string representation of the components of this geometry
94
getComponentsString: function(){
96
for(var i=0, len=this.components.length; i<len; i++) {
97
strings.push(this.components[i].toShortString());
99
return strings.join(",");
103
* APIMethod: calculateBounds
104
* Recalculate the bounds by iterating through the components and
105
* calling calling extendBounds() on each item.
107
calculateBounds: function() {
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());
118
* APIMethod: addComponents
119
* Add components to this geometry.
122
* components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
124
addComponents: function(components){
125
if(!(components instanceof Array)) {
126
components = [components];
128
for(var i=0, len=components.length; i<len; i++) {
129
this.addComponent(components[i]);
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.
138
* The bounds cache is reset.
141
* component - {<OpenLayers.Geometry>} A geometry to add
142
* index - {int} Optional index into the array to insert the component
145
* {Boolean} The component geometry was successfully added
147
addComponent: function(component, index) {
150
if(this.componentTypes == null ||
151
(OpenLayers.Util.indexOf(this.componentTypes,
152
component.CLASS_NAME) > -1)) {
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);
161
this.components.push(component);
163
component.parent = this;
172
* APIMethod: removeComponents
173
* Remove components from this geometry.
176
* components - {Array(<OpenLayers.Geometry>)} The components to be removed
178
removeComponents: function(components) {
179
if(!(components instanceof Array)) {
180
components = [components];
182
for(var i=components.length-1; i>=0; --i) {
183
this.removeComponent(components[i]);
188
* Method: removeComponent
189
* Remove a component from this geometry.
192
* component - {<OpenLayers.Geometry>}
194
removeComponent: function(component) {
196
OpenLayers.Util.removeItem(this.components, component);
198
// clearBounds() so that it gets recalculated on the next call
199
// to this.getBounds();
204
* APIMethod: getLength
205
* Calculate the length of this geometry
208
* {Float} The length of the geometry
210
getLength: function() {
212
for (var i=0, len=this.components.length; i<len; i++) {
213
length += this.components[i].getLength();
220
* Calculate the area of this geometry. Note how this function is overridden
221
* in <OpenLayers.Geometry.Polygon>.
224
* {Float} The area of the collection by summing its parts
226
getArea: function() {
228
for (var i=0, len=this.components.length; i<len; i++) {
229
area += this.components[i].getArea();
235
* APIMethod: getGeodesicArea
236
* Calculate the approximate area of the polygon were it projected onto
240
* projection - {<OpenLayers.Projection>} The spatial reference system
241
* for the geometry coordinates. If not provided, Geographic/WGS84 is
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
250
* {float} The approximate geodesic area of the geometry in square meters.
252
getGeodesicArea: function(projection) {
254
for(var i=0, len=this.components.length; i<len; i++) {
255
area += this.components[i].getGeodesicArea(projection);
261
* APIMethod: getCentroid
264
* {<OpenLayers.Geometry.Point>} The centroid of the collection
266
getCentroid: function() {
267
return this.components.length && this.components[0].getCentroid();
270
for (var i=0, len=this.components.length; i<len; i++) {
272
centroid = this.components[i].getCentroid();
274
centroid.resize(this.components[i].getCentroid(), 0.5);
282
* APIMethod: getGeodesicLength
283
* Calculate the approximate length of the geometry were it projected onto
286
* projection - {<OpenLayers.Projection>} The spatial reference system
287
* for the geometry coordinates. If not provided, Geographic/WGS84 is
291
* {Float} The appoximate geodesic length of the geometry in meters.
293
getGeodesicLength: function(projection) {
295
for(var i=0, len=this.components.length; i<len; i++) {
296
length += this.components[i].getGeodesicLength(projection);
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
308
* x - {Float} Distance to move geometry in positive x direction.
309
* y - {Float} Distance to move geometry in positive y direction.
311
move: function(x, y) {
312
for(var i=0, len=this.components.length; i<len; i++) {
313
this.components[i].move(x, y);
319
* Rotate a geometry around some origin
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
326
rotate: function(angle, origin) {
327
for(var i=0, len=this.components.length; i<len; ++i) {
328
this.components[i].rotate(angle, origin);
334
* Resize a geometry relative to some origin. Use this method to apply
335
* a uniform scaling to a geometry.
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.
346
* {OpenLayers.Geometry} - The current geometry.
348
resize: function(scale, origin, ratio) {
349
for(var i=0; i<this.components.length; ++i) {
350
this.components[i].resize(scale, origin, ratio);
356
* APIMethod: distanceTo
357
* Calculate the closest distance between two geometries (on the x-y plane).
360
* geometry - {<OpenLayers.Geometry>} The target geometry.
361
* options - {Object} Optional properties for configuring the distance
365
* details - {Boolean} Return details from the distance calculation.
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.
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
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;
403
* Determine whether another geometry is equivalent to this one. Geometries
404
* are considered equivalent if all components have the same coordinates.
407
* geom - {<OpenLayers.Geometry>} The geometry to test.
410
* {Boolean} The supplied geometry is equivalent to this geometry.
412
equals: function(geometry) {
413
var equivalent = true;
414
if(!geometry || !geometry.CLASS_NAME ||
415
(this.CLASS_NAME != geometry.CLASS_NAME)) {
417
} else if(!(geometry.components instanceof Array) ||
418
(geometry.components.length != this.components.length)) {
421
for(var i=0, len=this.components.length; i<len; ++i) {
422
if(!this.components[i].equals(geometry.components[i])) {
432
* APIMethod: transform
433
* Reproject the components geometry from source to dest.
436
* source - {<OpenLayers.Projection>}
437
* dest - {<OpenLayers.Projection>}
440
* {<OpenLayers.Geometry>}
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);
454
* APIMethod: intersects
455
* Determine if the input geometry intersects this one.
458
* geometry - {<OpenLayers.Geometry>} Any type of geometry.
461
* {Boolean} The input geometry intersects this one.
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]);
475
* APIMethod: getVertices
476
* Return a list of all points in this geometry.
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
485
* {Array} A list of all vertices in the geometry.
487
getVertices: function(nodes) {
489
for(var i=0, len=this.components.length; i<len; ++i) {
490
Array.prototype.push.apply(
491
vertices, this.components[i].getVertices(nodes)
498
CLASS_NAME: "OpenLayers.Geometry.Collection"