]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Geometry/Polygon.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Geometry / Polygon.js
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. */
4
5 /**
6  * @requires OpenLayers/Geometry/Collection.js
7  * @requires OpenLayers/Geometry/LinearRing.js
8  */
9
10 /**
11  * Class: OpenLayers.Geometry.Polygon 
12  * Polygon is a collection of Geometry.LinearRings. 
13  * 
14  * Inherits from:
15  *  - <OpenLayers.Geometry.Collection> 
16  *  - <OpenLayers.Geometry> 
17  */
18 OpenLayers.Geometry.Polygon = OpenLayers.Class(
19   OpenLayers.Geometry.Collection, {
20
21     /**
22      * Property: componentTypes
23      * {Array(String)} An array of class names representing the types of
24      * components that the collection can include.  A null value means the
25      * component types are not restricted.
26      */
27     componentTypes: ["OpenLayers.Geometry.LinearRing"],
28
29     /**
30      * Constructor: OpenLayers.Geometry.Polygon
31      * Constructor for a Polygon geometry. 
32      * The first ring (this.component[0])is the outer bounds of the polygon and 
33      * all subsequent rings (this.component[1-n]) are internal holes.
34      *
35      *
36      * Parameters:
37      * components - {Array(<OpenLayers.Geometry.LinearRing>)} 
38      */
39     initialize: function(components) {
40         OpenLayers.Geometry.Collection.prototype.initialize.apply(this, 
41                                                                   arguments);
42     },
43     
44     /** 
45      * APIMethod: getArea
46      * Calculated by subtracting the areas of the internal holes from the 
47      *   area of the outer hole.
48      * 
49      * Returns:
50      * {float} The area of the geometry
51      */
52     getArea: function() {
53         var area = 0.0;
54         if ( this.components && (this.components.length > 0)) {
55             area += Math.abs(this.components[0].getArea());
56             for (var i=1, len=this.components.length; i<len; i++) {
57                 area -= Math.abs(this.components[i].getArea());
58             }
59         }
60         return area;
61     },
62
63     /** 
64      * APIMethod: getGeodesicArea
65      * Calculate the approximate area of the polygon were it projected onto
66      *     the earth.
67      *
68      * Parameters:
69      * projection - {<OpenLayers.Projection>} The spatial reference system
70      *     for the geometry coordinates.  If not provided, Geographic/WGS84 is
71      *     assumed.
72      * 
73      * Reference:
74      * Robert. G. Chamberlain and William H. Duquette, "Some Algorithms for
75      *     Polygons on a Sphere", JPL Publication 07-03, Jet Propulsion
76      *     Laboratory, Pasadena, CA, June 2007 http://trs-new.jpl.nasa.gov/dspace/handle/2014/40409
77      *
78      * Returns:
79      * {float} The approximate geodesic area of the polygon in square meters.
80      */
81     getGeodesicArea: function(projection) {
82         var area = 0.0;
83         if(this.components && (this.components.length > 0)) {
84             area += Math.abs(this.components[0].getGeodesicArea(projection));
85             for(var i=1, len=this.components.length; i<len; i++) {
86                 area -= Math.abs(this.components[i].getGeodesicArea(projection));
87             }
88         }
89         return area;
90     },
91
92     /**
93      * Method: containsPoint
94      * Test if a point is inside a polygon.  Points on a polygon edge are
95      *     considered inside.
96      *
97      * Parameters:
98      * point - {<OpenLayers.Geometry.Point>}
99      *
100      * Returns:
101      * {Boolean | Number} The point is inside the polygon.  Returns 1 if the
102      *     point is on an edge.  Returns boolean otherwise.
103      */
104     containsPoint: function(point) {
105         var numRings = this.components.length;
106         var contained = false;
107         if(numRings > 0) {
108             // check exterior ring - 1 means on edge, boolean otherwise
109             contained = this.components[0].containsPoint(point);
110             if(contained !== 1) {
111                 if(contained && numRings > 1) {
112                     // check interior rings
113                     var hole;
114                     for(var i=1; i<numRings; ++i) {
115                         hole = this.components[i].containsPoint(point);
116                         if(hole) {
117                             if(hole === 1) {
118                                 // on edge
119                                 contained = 1;
120                             } else {
121                                 // in hole
122                                 contained = false;
123                             }                            
124                             break;
125                         }
126                     }
127                 }
128             }
129         }
130         return contained;
131     },
132
133     /**
134      * APIMethod: intersects
135      * Determine if the input geometry intersects this one.
136      *
137      * Parameters:
138      * geometry - {<OpenLayers.Geometry>} Any type of geometry.
139      *
140      * Returns:
141      * {Boolean} The input geometry intersects this one.
142      */
143     intersects: function(geometry) {
144         var intersect = false;
145         var i, len;
146         if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
147             intersect = this.containsPoint(geometry);
148         } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" ||
149                   geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") {
150             // check if rings/linestrings intersect
151             for(i=0, len=this.components.length; i<len; ++i) {
152                 intersect = geometry.intersects(this.components[i]);
153                 if(intersect) {
154                     break;
155                 }
156             }
157             if(!intersect) {
158                 // check if this poly contains points of the ring/linestring
159                 for(i=0, len=geometry.components.length; i<len; ++i) {
160                     intersect = this.containsPoint(geometry.components[i]);
161                     if(intersect) {
162                         break;
163                     }
164                 }
165             }
166         } else {
167             for(i=0, len=geometry.components.length; i<len; ++ i) {
168                 intersect = this.intersects(geometry.components[i]);
169                 if(intersect) {
170                     break;
171                 }
172             }
173         }
174         // check case where this poly is wholly contained by another
175         if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
176             // exterior ring points will be contained in the other geometry
177             var ring = this.components[0];
178             for(i=0, len=ring.components.length; i<len; ++i) {
179                 intersect = geometry.containsPoint(ring.components[i]);
180                 if(intersect) {
181                     break;
182                 }
183             }
184         }
185         return intersect;
186     },
187
188     /**
189      * APIMethod: distanceTo
190      * Calculate the closest distance between two geometries (on the x-y plane).
191      *
192      * Parameters:
193      * geometry - {<OpenLayers.Geometry>} The target geometry.
194      * options - {Object} Optional properties for configuring the distance
195      *     calculation.
196      *
197      * Valid options:
198      * details - {Boolean} Return details from the distance calculation.
199      *     Default is false.
200      * edge - {Boolean} Calculate the distance from this geometry to the
201      *     nearest edge of the target geometry.  Default is true.  If true,
202      *     calling distanceTo from a geometry that is wholly contained within
203      *     the target will result in a non-zero distance.  If false, whenever
204      *     geometries intersect, calling distanceTo will return 0.  If false,
205      *     details cannot be returned.
206      *
207      * Returns:
208      * {Number | Object} The distance between this geometry and the target.
209      *     If details is true, the return will be an object with distance,
210      *     x0, y0, x1, and y1 properties.  The x0 and y0 properties represent
211      *     the coordinates of the closest point on this geometry. The x1 and y1
212      *     properties represent the coordinates of the closest point on the
213      *     target geometry.
214      */
215     distanceTo: function(geometry, options) {
216         var edge = !(options && options.edge === false);
217         var result;
218         // this is the case where we might not be looking for distance to edge
219         if(!edge && this.intersects(geometry)) {
220             result = 0;
221         } else {
222             result = OpenLayers.Geometry.Collection.prototype.distanceTo.apply(
223                 this, [geometry, options]
224             );
225         }
226         return result;
227     },
228
229     CLASS_NAME: "OpenLayers.Geometry.Polygon"
230 });
231
232 /**
233  * APIMethod: createRegularPolygon
234  * Create a regular polygon around a radius. Useful for creating circles 
235  * and the like.
236  *
237  * Parameters:
238  * origin - {<OpenLayers.Geometry.Point>} center of polygon.
239  * radius - {Float} distance to vertex, in map units.
240  * sides - {Integer} Number of sides. 20 approximates a circle.
241  * rotation - {Float} original angle of rotation, in degrees.
242  */
243 OpenLayers.Geometry.Polygon.createRegularPolygon = function(origin, radius, sides, rotation) {  
244     var angle = Math.PI * ((1/sides) - (1/2));
245     if(rotation) {
246         angle += (rotation / 180) * Math.PI;
247     }
248     var rotatedAngle, x, y;
249     var points = [];
250     for(var i=0; i<sides; ++i) {
251         rotatedAngle = angle + (i * 2 * Math.PI / sides);
252         x = origin.x + (radius * Math.cos(rotatedAngle));
253         y = origin.y + (radius * Math.sin(rotatedAngle));
254         points.push(new OpenLayers.Geometry.Point(x, y));
255     }
256     var ring = new OpenLayers.Geometry.LinearRing(points);
257     return new OpenLayers.Geometry.Polygon([ring]);
258 };