| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- /* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
- * full list of contributors). Published under the Clear BSD license.
- * See http://svn.openlayers.org/trunk/openlayers/license.txt 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
- */
- initialize: function(points) {
- OpenLayers.Geometry.LineString.prototype.initialize.apply(this,
- arguments);
- },
- /**
- * 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.
- *
- * Parameter:
- * 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 && (this.components.length > 2)) {
- var sumX = 0.0;
- var sumY = 0.0;
- for (var i = 0; i < this.components.length - 1; i++) {
- var b = this.components[i];
- var c = this.components[i+1];
- sumX += (b.x + c.x) * (b.x * c.y - c.x * b.y);
- sumY += (b.y + c.y) * (b.x * c.y - c.x * b.y);
- }
- var area = -1 * this.getArea();
- var x = sumX / (6 * area);
- var y = sumY / (6 * area);
- 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 (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2);
- }
- 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"
- });
|