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;
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"