summaryrefslogtreecommitdiff
path: root/misc/openlayers/lib/OpenLayers/Geometry/Collection.js
diff options
context:
space:
mode:
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Geometry/Collection.js')
-rw-r--r--misc/openlayers/lib/OpenLayers/Geometry/Collection.js563
1 files changed, 563 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Geometry/Collection.js b/misc/openlayers/lib/OpenLayers/Geometry/Collection.js
new file mode 100644
index 0000000..f76cc85
--- /dev/null
+++ b/misc/openlayers/lib/OpenLayers/Geometry/Collection.js
@@ -0,0 +1,563 @@
+/* Copyright (c) 2006-2013 by OpenLayers Contributors (see authors.txt for
+ * full list of contributors). Published under the 2-clause BSD license.
+ * See license.txt in the OpenLayers distribution or repository for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Geometry.js
+ */
+
+/**
+ * Class: OpenLayers.Geometry.Collection
+ * A Collection is exactly what it sounds like: A collection of different
+ * Geometries. These are stored in the local parameter <components> (which
+ * can be passed as a parameter to the constructor).
+ *
+ * As new geometries are added to the collection, they are NOT cloned.
+ * When removing geometries, they need to be specified by reference (ie you
+ * have to pass in the *exact* geometry to be removed).
+ *
+ * The <getArea> and <getLength> functions here merely iterate through
+ * the components, summing their respective areas and lengths.
+ *
+ * Create a new instance with the <OpenLayers.Geometry.Collection> constructor.
+ *
+ * Inherits from:
+ * - <OpenLayers.Geometry>
+ */
+OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, {
+
+ /**
+ * APIProperty: components
+ * {Array(<OpenLayers.Geometry>)} The component parts of this geometry
+ */
+ components: null,
+
+ /**
+ * Property: componentTypes
+ * {Array(String)} An array of class names representing the types of
+ * components that the collection can include. A null value means the
+ * component types are not restricted.
+ */
+ componentTypes: null,
+
+ /**
+ * Constructor: OpenLayers.Geometry.Collection
+ * Creates a Geometry Collection -- a list of geoms.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} Optional array of geometries
+ *
+ */
+ initialize: function (components) {
+ OpenLayers.Geometry.prototype.initialize.apply(this, arguments);
+ this.components = [];
+ if (components != null) {
+ this.addComponents(components);
+ }
+ },
+
+ /**
+ * APIMethod: destroy
+ * Destroy this geometry.
+ */
+ destroy: function () {
+ this.components.length = 0;
+ this.components = null;
+ OpenLayers.Geometry.prototype.destroy.apply(this, arguments);
+ },
+
+ /**
+ * APIMethod: clone
+ * Clone this geometry.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Collection>} An exact clone of this collection
+ */
+ clone: function() {
+ var geometry = eval("new " + this.CLASS_NAME + "()");
+ for(var i=0, len=this.components.length; i<len; i++) {
+ geometry.addComponent(this.components[i].clone());
+ }
+
+ // catch any randomly tagged-on properties
+ OpenLayers.Util.applyDefaults(geometry, this);
+
+ return geometry;
+ },
+
+ /**
+ * Method: getComponentsString
+ * Get a string representing the components for this collection
+ *
+ * Returns:
+ * {String} A string representation of the components of this geometry
+ */
+ getComponentsString: function(){
+ var strings = [];
+ for(var i=0, len=this.components.length; i<len; i++) {
+ strings.push(this.components[i].toShortString());
+ }
+ return strings.join(",");
+ },
+
+ /**
+ * APIMethod: calculateBounds
+ * Recalculate the bounds by iterating through the components and
+ * calling calling extendBounds() on each item.
+ */
+ calculateBounds: function() {
+ this.bounds = null;
+ var bounds = new OpenLayers.Bounds();
+ var components = this.components;
+ if (components) {
+ for (var i=0, len=components.length; i<len; i++) {
+ bounds.extend(components[i].getBounds());
+ }
+ }
+ // to preserve old behavior, we only set bounds if non-null
+ // in the future, we could add bounds.isEmpty()
+ if (bounds.left != null && bounds.bottom != null &&
+ bounds.right != null && bounds.top != null) {
+ this.setBounds(bounds);
+ }
+ },
+
+ /**
+ * APIMethod: addComponents
+ * Add components to this geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} An array of geometries to add
+ */
+ addComponents: function(components){
+ if(!(OpenLayers.Util.isArray(components))) {
+ components = [components];
+ }
+ for(var i=0, len=components.length; i<len; i++) {
+ this.addComponent(components[i]);
+ }
+ },
+
+ /**
+ * Method: addComponent
+ * Add a new component (geometry) to the collection. If this.componentTypes
+ * is set, then the component class name must be in the componentTypes array.
+ *
+ * The bounds cache is reset.
+ *
+ * Parameters:
+ * component - {<OpenLayers.Geometry>} A geometry to add
+ * index - {int} Optional index into the array to insert the component
+ *
+ * Returns:
+ * {Boolean} The component geometry was successfully added
+ */
+ addComponent: function(component, index) {
+ var added = false;
+ if(component) {
+ if(this.componentTypes == null ||
+ (OpenLayers.Util.indexOf(this.componentTypes,
+ component.CLASS_NAME) > -1)) {
+
+ if(index != null && (index < this.components.length)) {
+ var components1 = this.components.slice(0, index);
+ var components2 = this.components.slice(index,
+ this.components.length);
+ components1.push(component);
+ this.components = components1.concat(components2);
+ } else {
+ this.components.push(component);
+ }
+ component.parent = this;
+ this.clearBounds();
+ added = true;
+ }
+ }
+ return added;
+ },
+
+ /**
+ * APIMethod: removeComponents
+ * Remove components from this geometry.
+ *
+ * Parameters:
+ * components - {Array(<OpenLayers.Geometry>)} The components to be removed
+ *
+ * Returns:
+ * {Boolean} A component was removed.
+ */
+ removeComponents: function(components) {
+ var removed = false;
+
+ if(!(OpenLayers.Util.isArray(components))) {
+ components = [components];
+ }
+ for(var i=components.length-1; i>=0; --i) {
+ removed = this.removeComponent(components[i]) || removed;
+ }
+ return removed;
+ },
+
+ /**
+ * Method: removeComponent
+ * Remove a component from this geometry.
+ *
+ * Parameters:
+ * component - {<OpenLayers.Geometry>}
+ *
+ * Returns:
+ * {Boolean} The component was removed.
+ */
+ removeComponent: function(component) {
+
+ OpenLayers.Util.removeItem(this.components, component);
+
+ // clearBounds() so that it gets recalculated on the next call
+ // to this.getBounds();
+ this.clearBounds();
+ return true;
+ },
+
+ /**
+ * APIMethod: getLength
+ * Calculate the length of this geometry
+ *
+ * Returns:
+ * {Float} The length of the geometry
+ */
+ getLength: function() {
+ var length = 0.0;
+ for (var i=0, len=this.components.length; i<len; i++) {
+ length += this.components[i].getLength();
+ }
+ return length;
+ },
+
+ /**
+ * APIMethod: getArea
+ * Calculate the area of this geometry. Note how this function is overridden
+ * in <OpenLayers.Geometry.Polygon>.
+ *
+ * Returns:
+ * {Float} The area of the collection by summing its parts
+ */
+ getArea: function() {
+ var area = 0.0;
+ for (var i=0, len=this.components.length; i<len; i++) {
+ area += this.components[i].getArea();
+ }
+ return area;
+ },
+
+ /**
+ * APIMethod: getGeodesicArea
+ * Calculate the approximate area of the polygon were it projected onto
+ * the earth.
+ *
+ * Parameters:
+ * projection - {<OpenLayers.Projection>} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Reference:
+ * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
+ * Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
+ * Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
+ *
+ * Returns:
+ * {float} The approximate geodesic area of the geometry in square meters.
+ */
+ getGeodesicArea: function(projection) {
+ var area = 0.0;
+ for(var i=0, len=this.components.length; i<len; i++) {
+ area += this.components[i].getGeodesicArea(projection);
+ }
+ return area;
+ },
+
+ /**
+ * APIMethod: getCentroid
+ *
+ * Compute the centroid for this geometry collection.
+ *
+ * Parameters:
+ * weighted - {Boolean} Perform the getCentroid computation recursively,
+ * returning an area weighted average of all geometries in this collection.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry.Point>} The centroid of the collection
+ */
+ getCentroid: function(weighted) {
+ if (!weighted) {
+ return this.components.length && this.components[0].getCentroid();
+ }
+ var len = this.components.length;
+ if (!len) {
+ return false;
+ }
+
+ var areas = [];
+ var centroids = [];
+ var areaSum = 0;
+ var minArea = Number.MAX_VALUE;
+ var component;
+ for (var i=0; i<len; ++i) {
+ component = this.components[i];
+ var area = component.getArea();
+ var centroid = component.getCentroid(true);
+ if (isNaN(area) || isNaN(centroid.x) || isNaN(centroid.y)) {
+ continue;
+ }
+ areas.push(area);
+ areaSum += area;
+ minArea = (area < minArea && area > 0) ? area : minArea;
+ centroids.push(centroid);
+ }
+ len = areas.length;
+ if (areaSum === 0) {
+ // all the components in this collection have 0 area
+ // probably a collection of points -- weight all the points the same
+ for (var i=0; i<len; ++i) {
+ areas[i] = 1;
+ }
+ areaSum = areas.length;
+ } else {
+ // normalize all the areas where the smallest area will get
+ // a value of 1
+ for (var i=0; i<len; ++i) {
+ areas[i] /= minArea;
+ }
+ areaSum /= minArea;
+ }
+
+ var xSum = 0, ySum = 0, centroid, area;
+ for (var i=0; i<len; ++i) {
+ centroid = centroids[i];
+ area = areas[i];
+ xSum += centroid.x * area;
+ ySum += centroid.y * area;
+ }
+
+ return new OpenLayers.Geometry.Point(xSum/areaSum, ySum/areaSum);
+ },
+
+ /**
+ * APIMethod: getGeodesicLength
+ * Calculate the approximate length of the geometry were it projected onto
+ * the earth.
+ *
+ * projection - {<OpenLayers.Projection>} The spatial reference system
+ * for the geometry coordinates. If not provided, Geographic/WGS84 is
+ * assumed.
+ *
+ * Returns:
+ * {Float} The appoximate geodesic length of the geometry in meters.
+ */
+ getGeodesicLength: function(projection) {
+ var length = 0.0;
+ for(var i=0, len=this.components.length; i<len; i++) {
+ length += this.components[i].getGeodesicLength(projection);
+ }
+ return length;
+ },
+
+ /**
+ * APIMethod: move
+ * Moves a geometry by the given displacement along positive x and y axes.
+ * This modifies the position of the geometry and clears the cached
+ * bounds.
+ *
+ * Parameters:
+ * x - {Float} Distance to move geometry in positive x direction.
+ * y - {Float} Distance to move geometry in positive y direction.
+ */
+ move: function(x, y) {
+ for(var i=0, len=this.components.length; i<len; i++) {
+ this.components[i].move(x, y);
+ }
+ },
+
+ /**
+ * APIMethod: rotate
+ * Rotate a geometry around some origin
+ *
+ * Parameters:
+ * angle - {Float} Rotation angle in degrees (measured counterclockwise
+ * from the positive x-axis)
+ * origin - {<OpenLayers.Geometry.Point>} Center point for the rotation
+ */
+ rotate: function(angle, origin) {
+ for(var i=0, len=this.components.length; i<len; ++i) {
+ this.components[i].rotate(angle, origin);
+ }
+ },
+
+ /**
+ * APIMethod: resize
+ * Resize a geometry relative to some origin. Use this method to apply
+ * a uniform scaling to a geometry.
+ *
+ * Parameters:
+ * scale - {Float} Factor by which to scale the geometry. A scale of 2
+ * doubles the size of the geometry in each dimension
+ * (lines, for example, will be twice as long, and polygons
+ * will have four times the area).
+ * origin - {<OpenLayers.Geometry.Point>} Point of origin for resizing
+ * ratio - {Float} Optional x:y ratio for resizing. Default ratio is 1.
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>} - The current geometry.
+ */
+ resize: function(scale, origin, ratio) {
+ for(var i=0; i<this.components.length; ++i) {
+ this.components[i].resize(scale, origin, ratio);
+ }
+ return this;
+ },
+
+ /**
+ * APIMethod: distanceTo
+ * Calculate the closest distance between two geometries (on the x-y plane).
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>} The target geometry.
+ * options - {Object} Optional properties for configuring the distance
+ * calculation.
+ *
+ * Valid options:
+ * details - {Boolean} Return details from the distance calculation.
+ * Default is false.
+ * edge - {Boolean} Calculate the distance from this geometry to the
+ * nearest edge of the target geometry. Default is true. If true,
+ * calling distanceTo from a geometry that is wholly contained within
+ * the target will result in a non-zero distance. If false, whenever
+ * geometries intersect, calling distanceTo will return 0. If false,
+ * details cannot be returned.
+ *
+ * Returns:
+ * {Number | Object} The distance between this geometry and the target.
+ * If details is true, the return will be an object with distance,
+ * x0, y0, x1, and y1 properties. The x0 and y0 properties represent
+ * the coordinates of the closest point on this geometry. The x1 and y1
+ * properties represent the coordinates of the closest point on the
+ * target geometry.
+ */
+ distanceTo: function(geometry, options) {
+ var edge = !(options && options.edge === false);
+ var details = edge && options && options.details;
+ var result, best, distance;
+ var min = Number.POSITIVE_INFINITY;
+ for(var i=0, len=this.components.length; i<len; ++i) {
+ result = this.components[i].distanceTo(geometry, options);
+ distance = details ? result.distance : result;
+ if(distance < min) {
+ min = distance;
+ best = result;
+ if(min == 0) {
+ break;
+ }
+ }
+ }
+ return best;
+ },
+
+ /**
+ * APIMethod: equals
+ * Determine whether another geometry is equivalent to this one. Geometries
+ * are considered equivalent if all components have the same coordinates.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>} The geometry to test.
+ *
+ * Returns:
+ * {Boolean} The supplied geometry is equivalent to this geometry.
+ */
+ equals: function(geometry) {
+ var equivalent = true;
+ if(!geometry || !geometry.CLASS_NAME ||
+ (this.CLASS_NAME != geometry.CLASS_NAME)) {
+ equivalent = false;
+ } else if(!(OpenLayers.Util.isArray(geometry.components)) ||
+ (geometry.components.length != this.components.length)) {
+ equivalent = false;
+ } else {
+ for(var i=0, len=this.components.length; i<len; ++i) {
+ if(!this.components[i].equals(geometry.components[i])) {
+ equivalent = false;
+ break;
+ }
+ }
+ }
+ return equivalent;
+ },
+
+ /**
+ * APIMethod: transform
+ * Reproject the components geometry from source to dest.
+ *
+ * Parameters:
+ * source - {<OpenLayers.Projection>}
+ * dest - {<OpenLayers.Projection>}
+ *
+ * Returns:
+ * {<OpenLayers.Geometry>}
+ */
+ transform: function(source, dest) {
+ if (source && dest) {
+ for (var i=0, len=this.components.length; i<len; i++) {
+ var component = this.components[i];
+ component.transform(source, dest);
+ }
+ this.bounds = null;
+ }
+ return this;
+ },
+
+ /**
+ * APIMethod: intersects
+ * Determine if the input geometry intersects this one.
+ *
+ * Parameters:
+ * geometry - {<OpenLayers.Geometry>} Any type of geometry.
+ *
+ * Returns:
+ * {Boolean} The input geometry intersects this one.
+ */
+ intersects: function(geometry) {
+ var intersect = false;
+ for(var i=0, len=this.components.length; i<len; ++ i) {
+ intersect = geometry.intersects(this.components[i]);
+ if(intersect) {
+ break;
+ }
+ }
+ return intersect;
+ },
+
+ /**
+ * APIMethod: getVertices
+ * Return a list of all points in this geometry.
+ *
+ * Parameters:
+ * nodes - {Boolean} For lines, only return vertices that are
+ * endpoints. If false, for lines, only vertices that are not
+ * endpoints will be returned. If not provided, all vertices will
+ * be returned.
+ *
+ * Returns:
+ * {Array} A list of all vertices in the geometry.
+ */
+ getVertices: function(nodes) {
+ var vertices = [];
+ for(var i=0, len=this.components.length; i<len; ++i) {
+ Array.prototype.push.apply(
+ vertices, this.components[i].getVertices(nodes)
+ );
+ }
+ return vertices;
+ },
+
+
+ CLASS_NAME: "OpenLayers.Geometry.Collection"
+});