diff options
Diffstat (limited to 'misc/openlayers/lib/OpenLayers/Geometry/LinearRing.js')
-rw-r--r-- | misc/openlayers/lib/OpenLayers/Geometry/LinearRing.js | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/misc/openlayers/lib/OpenLayers/Geometry/LinearRing.js b/misc/openlayers/lib/OpenLayers/Geometry/LinearRing.js new file mode 100644 index 0000000..b0a694c --- /dev/null +++ b/misc/openlayers/lib/OpenLayers/Geometry/LinearRing.js @@ -0,0 +1,433 @@ +/* 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/LineString.js + */ + +/** + * Class: OpenLayers.Geometry.LinearRing + * + * A Linear Ring is a special LineString which is closed. It closes itself + * automatically on every addPoint/removePoint by adding a copy of the first + * point as the last point. + * + * Also, as it is the first in the line family to close itself, a getArea() + * function is defined to calculate the enclosed area of the linearRing + * + * Inherits: + * - <OpenLayers.Geometry.LineString> + */ +OpenLayers.Geometry.LinearRing = OpenLayers.Class( + OpenLayers.Geometry.LineString, { + + /** + * 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: ["OpenLayers.Geometry.Point"], + + /** + * Constructor: OpenLayers.Geometry.LinearRing + * Linear rings are constructed with an array of points. This array + * can represent a closed or open ring. If the ring is open (the last + * point does not equal the first point), the constructor will close + * the ring. If the ring is already closed (the last point does equal + * the first point), it will be left closed. + * + * Parameters: + * points - {Array(<OpenLayers.Geometry.Point>)} points + */ + + /** + * APIMethod: addComponent + * Adds a point to geometry components. If the point is to be added to + * the end of the components array and it is the same as the last point + * already in that array, the duplicate point is not added. This has + * the effect of closing the ring if it is not already closed, and + * doing the right thing if it is already closed. This behavior can + * be overridden by calling the method with a non-null index as the + * second argument. + * + * Parameters: + * point - {<OpenLayers.Geometry.Point>} + * index - {Integer} Index into the array to insert the component + * + * Returns: + * {Boolean} Was the Point successfully added? + */ + addComponent: function(point, index) { + var added = false; + + //remove last point + var lastPoint = this.components.pop(); + + // given an index, add the point + // without an index only add non-duplicate points + if(index != null || !point.equals(lastPoint)) { + added = OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, + arguments); + } + + //append copy of first point + var firstPoint = this.components[0]; + OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, + [firstPoint]); + + return added; + }, + + /** + * APIMethod: removeComponent + * Removes a point from geometry components. + * + * Parameters: + * point - {<OpenLayers.Geometry.Point>} + * + * Returns: + * {Boolean} The component was removed. + */ + removeComponent: function(point) { + var removed = this.components && (this.components.length > 3); + if (removed) { + //remove last point + this.components.pop(); + + //remove our point + OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this, + arguments); + //append copy of first point + var firstPoint = this.components[0]; + OpenLayers.Geometry.Collection.prototype.addComponent.apply(this, + [firstPoint]); + } + return removed; + }, + + /** + * 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 - 1; 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 - 1; ++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, len=this.components.length; i<len - 1; ++i) { + this.components[i].resize(scale, origin, ratio); + } + return this; + }, + + /** + * 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 - 1; i++) { + var component = this.components[i]; + component.transform(source, dest); + } + this.bounds = null; + } + return this; + }, + + /** + * APIMethod: getCentroid + * + * Returns: + * {<OpenLayers.Geometry.Point>} The centroid of the collection + */ + getCentroid: function() { + if (this.components) { + var len = this.components.length; + if (len > 0 && len <= 2) { + return this.components[0].clone(); + } else if (len > 2) { + var sumX = 0.0; + var sumY = 0.0; + var x0 = this.components[0].x; + var y0 = this.components[0].y; + var area = -1 * this.getArea(); + if (area != 0) { + for (var i = 0; i < len - 1; i++) { + var b = this.components[i]; + var c = this.components[i+1]; + sumX += (b.x + c.x - 2 * x0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); + sumY += (b.y + c.y - 2 * y0) * ((b.x - x0) * (c.y - y0) - (c.x - x0) * (b.y - y0)); + } + var x = x0 + sumX / (6 * area); + var y = y0 + sumY / (6 * area); + } else { + for (var i = 0; i < len - 1; i++) { + sumX += this.components[i].x; + sumY += this.components[i].y; + } + var x = sumX / (len - 1); + var y = sumY / (len - 1); + } + return new OpenLayers.Geometry.Point(x, y); + } else { + return null; + } + } + }, + + /** + * APIMethod: getArea + * Note - The area is positive if the ring is oriented CW, otherwise + * it will be negative. + * + * Returns: + * {Float} The signed area for a ring. + */ + getArea: function() { + var area = 0.0; + if ( this.components && (this.components.length > 2)) { + var sum = 0.0; + for (var i=0, len=this.components.length; i<len - 1; i++) { + var b = this.components[i]; + var c = this.components[i+1]; + sum += (b.x + c.x) * (c.y - b.y); + } + area = - sum / 2.0; + } + return area; + }, + + /** + * APIMethod: getGeodesicArea + * Calculate the approximate area of the polygon were it projected onto + * the earth. Note that this area will be positive if ring is oriented + * clockwise, otherwise it will be negative. + * + * 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 signed geodesic area of the polygon in square + * meters. + */ + getGeodesicArea: function(projection) { + var ring = this; // so we can work with a clone if needed + if(projection) { + var gg = new OpenLayers.Projection("EPSG:4326"); + if(!gg.equals(projection)) { + ring = this.clone().transform(projection, gg); + } + } + var area = 0.0; + var len = ring.components && ring.components.length; + if(len > 2) { + var p1, p2; + for(var i=0; i<len-1; i++) { + p1 = ring.components[i]; + p2 = ring.components[i+1]; + area += OpenLayers.Util.rad(p2.x - p1.x) * + (2 + Math.sin(OpenLayers.Util.rad(p1.y)) + + Math.sin(OpenLayers.Util.rad(p2.y))); + } + area = area * 6378137.0 * 6378137.0 / 2.0; + } + return area; + }, + + /** + * Method: containsPoint + * Test if a point is inside a linear ring. For the case where a point + * is coincident with a linear ring edge, returns 1. Otherwise, + * returns boolean. + * + * Parameters: + * point - {<OpenLayers.Geometry.Point>} + * + * Returns: + * {Boolean | Number} The point is inside the linear ring. Returns 1 if + * the point is coincident with an edge. Returns boolean otherwise. + */ + containsPoint: function(point) { + var approx = OpenLayers.Number.limitSigDigs; + var digs = 14; + var px = approx(point.x, digs); + var py = approx(point.y, digs); + function getX(y, x1, y1, x2, y2) { + return (y - y2) * ((x2 - x1) / (y2 - y1)) + x2; + } + var numSeg = this.components.length - 1; + var start, end, x1, y1, x2, y2, cx, cy; + var crosses = 0; + for(var i=0; i<numSeg; ++i) { + start = this.components[i]; + x1 = approx(start.x, digs); + y1 = approx(start.y, digs); + end = this.components[i + 1]; + x2 = approx(end.x, digs); + y2 = approx(end.y, digs); + + /** + * The following conditions enforce five edge-crossing rules: + * 1. points coincident with edges are considered contained; + * 2. an upward edge includes its starting endpoint, and + * excludes its final endpoint; + * 3. a downward edge excludes its starting endpoint, and + * includes its final endpoint; + * 4. horizontal edges are excluded; and + * 5. the edge-ray intersection point must be strictly right + * of the point P. + */ + if(y1 == y2) { + // horizontal edge + if(py == y1) { + // point on horizontal line + if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert + x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert + // point on edge + crosses = -1; + break; + } + } + // ignore other horizontal edges + continue; + } + cx = approx(getX(py, x1, y1, x2, y2), digs); + if(cx == px) { + // point on line + if(y1 < y2 && (py >= y1 && py <= y2) || // upward + y1 > y2 && (py <= y1 && py >= y2)) { // downward + // point on edge + crosses = -1; + break; + } + } + if(cx <= px) { + // no crossing to the right + continue; + } + if(x1 != x2 && (cx < Math.min(x1, x2) || cx > Math.max(x1, x2))) { + // no crossing + continue; + } + if(y1 < y2 && (py >= y1 && py < y2) || // upward + y1 > y2 && (py < y1 && py >= y2)) { // downward + ++crosses; + } + } + var contained = (crosses == -1) ? + // on edge + 1 : + // even (out) or odd (in) + !!(crosses & 1); + + return contained; + }, + + /** + * 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; + if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + intersect = this.containsPoint(geometry); + } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { + intersect = geometry.intersects(this); + } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { + intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply( + this, [geometry] + ); + } else { + // check for component intersections + for(var i=0, len=geometry.components.length; i<len; ++ i) { + intersect = geometry.components[i].intersects(this); + 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) { + return (nodes === true) ? [] : this.components.slice(0, this.components.length-1); + }, + + CLASS_NAME: "OpenLayers.Geometry.LinearRing" +}); |