]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/Geometry.js
initial commit
[syp.git] / openlayers / lib / OpenLayers / Geometry.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/Format/WKT.js
7  * @requires OpenLayers/Feature/Vector.js
8  */
9
10 /**
11  * Class: OpenLayers.Geometry
12  * A Geometry is a description of a geographic object.  Create an instance of
13  * this class with the <OpenLayers.Geometry> constructor.  This is a base class,
14  * typical geometry types are described by subclasses of this class.
15  */
16 OpenLayers.Geometry = OpenLayers.Class({
17
18     /**
19      * Property: id
20      * {String} A unique identifier for this geometry.
21      */
22     id: null,
23
24     /**
25      * Property: parent
26      * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
27      * of another geometry
28      */
29     parent: null,
30
31     /**
32      * Property: bounds 
33      * {<OpenLayers.Bounds>} The bounds of this geometry
34      */
35     bounds: null,
36
37     /**
38      * Constructor: OpenLayers.Geometry
39      * Creates a geometry object.  
40      */
41     initialize: function() {
42         this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
43     },
44     
45     /**
46      * Method: destroy
47      * Destroy this geometry.
48      */
49     destroy: function() {
50         this.id = null;
51         this.bounds = null;
52     },
53     
54     /**
55      * APIMethod: clone
56      * Create a clone of this geometry.  Does not set any non-standard
57      *     properties of the cloned geometry.
58      * 
59      * Returns:
60      * {<OpenLayers.Geometry>} An exact clone of this geometry.
61      */
62     clone: function() {
63         return new OpenLayers.Geometry();
64     },
65     
66     /**
67      * Set the bounds for this Geometry.
68      * 
69      * Parameters:
70      * object - {<OpenLayers.Bounds>} 
71      */
72     setBounds: function(bounds) {
73         if (bounds) {
74             this.bounds = bounds.clone();
75         }
76     },
77     
78     /**
79      * Method: clearBounds
80      * Nullify this components bounds and that of its parent as well.
81      */
82     clearBounds: function() {
83         this.bounds = null;
84         if (this.parent) {
85             this.parent.clearBounds();
86         }    
87     },
88     
89     /**
90      * Method: extendBounds
91      * Extend the existing bounds to include the new bounds. 
92      * If geometry's bounds is not yet set, then set a new Bounds.
93      * 
94      * Parameters:
95      * newBounds - {<OpenLayers.Bounds>} 
96      */
97     extendBounds: function(newBounds){
98         var bounds = this.getBounds();
99         if (!bounds) {
100             this.setBounds(newBounds);
101         } else {
102             this.bounds.extend(newBounds);
103         }
104     },
105     
106     /**
107      * APIMethod: getBounds
108      * Get the bounds for this Geometry. If bounds is not set, it 
109      * is calculated again, this makes queries faster.
110      * 
111      * Returns:
112      * {<OpenLayers.Bounds>}
113      */
114     getBounds: function() {
115         if (this.bounds == null) {
116             this.calculateBounds();
117         }
118         return this.bounds;
119     },
120     
121     /** 
122      * APIMethod: calculateBounds
123      * Recalculate the bounds for the geometry. 
124      */
125     calculateBounds: function() {
126         //
127         // This should be overridden by subclasses.
128         //
129     },
130     
131     /**
132      * APIMethod: distanceTo
133      * Calculate the closest distance between two geometries (on the x-y plane).
134      *
135      * Parameters:
136      * geometry - {<OpenLayers.Geometry>} The target geometry.
137      * options - {Object} Optional properties for configuring the distance
138      *     calculation.
139      *
140      * Valid options depend on the specific geometry type.
141      * 
142      * Returns:
143      * {Number | Object} The distance between this geometry and the target.
144      *     If details is true, the return will be an object with distance,
145      *     x0, y0, x1, and x2 properties.  The x0 and y0 properties represent
146      *     the coordinates of the closest point on this geometry. The x1 and y1
147      *     properties represent the coordinates of the closest point on the
148      *     target geometry.
149      */
150     distanceTo: function(geometry, options) {
151     },
152     
153     /**
154      * APIMethod: getVertices
155      * Return a list of all points in this geometry.
156      *
157      * Parameters:
158      * nodes - {Boolean} For lines, only return vertices that are
159      *     endpoints.  If false, for lines, only vertices that are not
160      *     endpoints will be returned.  If not provided, all vertices will
161      *     be returned.
162      *
163      * Returns:
164      * {Array} A list of all vertices in the geometry.
165      */
166     getVertices: function(nodes) {
167     },
168
169     /**
170      * Method: atPoint
171      * Note - This is only an approximation based on the bounds of the 
172      * geometry.
173      * 
174      * Parameters:
175      * lonlat - {<OpenLayers.LonLat>} 
176      * toleranceLon - {float} Optional tolerance in Geometric Coords
177      * toleranceLat - {float} Optional tolerance in Geographic Coords
178      * 
179      * Returns:
180      * {Boolean} Whether or not the geometry is at the specified location
181      */
182     atPoint: function(lonlat, toleranceLon, toleranceLat) {
183         var atPoint = false;
184         var bounds = this.getBounds();
185         if ((bounds != null) && (lonlat != null)) {
186
187             var dX = (toleranceLon != null) ? toleranceLon : 0;
188             var dY = (toleranceLat != null) ? toleranceLat : 0;
189     
190             var toleranceBounds = 
191                 new OpenLayers.Bounds(this.bounds.left - dX,
192                                       this.bounds.bottom - dY,
193                                       this.bounds.right + dX,
194                                       this.bounds.top + dY);
195
196             atPoint = toleranceBounds.containsLonLat(lonlat);
197         }
198         return atPoint;
199     },
200     
201     /**
202      * Method: getLength
203      * Calculate the length of this geometry. This method is defined in
204      * subclasses.
205      * 
206      * Returns:
207      * {Float} The length of the collection by summing its parts
208      */
209     getLength: function() {
210         //to be overridden by geometries that actually have a length
211         //
212         return 0.0;
213     },
214
215     /**
216      * Method: getArea
217      * Calculate the area of this geometry. This method is defined in subclasses.
218      * 
219      * Returns:
220      * {Float} The area of the collection by summing its parts
221      */
222     getArea: function() {
223         //to be overridden by geometries that actually have an area
224         //
225         return 0.0;
226     },
227     
228     /**
229      * APIMethod: getCentroid
230      * Calculate the centroid of this geometry. This method is defined in subclasses.
231      *
232      * Returns:
233      * {<OpenLayers.Geometry.Point>} The centroid of the collection
234      */
235     getCentroid: function() {
236         return null;
237     },
238
239     /**
240      * Method: toString
241      * Returns the Well-Known Text representation of a geometry
242      *
243      * Returns:
244      * {String} Well-Known Text
245      */
246     toString: function() {
247         return OpenLayers.Format.WKT.prototype.write(
248             new OpenLayers.Feature.Vector(this)
249         );
250     },
251
252     CLASS_NAME: "OpenLayers.Geometry"
253 });
254
255 /**
256  * Function: OpenLayers.Geometry.fromWKT
257  * Generate a geometry given a Well-Known Text string.
258  *
259  * Parameters:
260  * wkt - {String} A string representing the geometry in Well-Known Text.
261  *
262  * Returns:
263  * {<OpenLayers.Geometry>} A geometry of the appropriate class.
264  */
265 OpenLayers.Geometry.fromWKT = function(wkt) {
266     var format = arguments.callee.format;
267     if(!format) {
268         format = new OpenLayers.Format.WKT();
269         arguments.callee.format = format;
270     }
271     var geom;
272     var result = format.read(wkt);
273     if(result instanceof OpenLayers.Feature.Vector) {
274         geom = result.geometry;
275     } else if(result instanceof Array) {
276         var len = result.length;
277         var components = new Array(len);
278         for(var i=0; i<len; ++i) {
279             components[i] = result[i].geometry;
280         }
281         geom = new OpenLayers.Geometry.Collection(components);
282     }
283     return geom;
284 };
285     
286 /**
287  * Method: OpenLayers.Geometry.segmentsIntersect
288  * Determine whether two line segments intersect.  Optionally calculates
289  *     and returns the intersection point.  This function is optimized for
290  *     cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1.  In those
291  *     obvious cases where there is no intersection, the function should
292  *     not be called.
293  *
294  * Parameters:
295  * seg1 - {Object} Object representing a segment with properties x1, y1, x2,
296  *     and y2.  The start point is represented by x1 and y1.  The end point
297  *     is represented by x2 and y2.  Start and end are ordered so that x1 < x2.
298  * seg2 - {Object} Object representing a segment with properties x1, y1, x2,
299  *     and y2.  The start point is represented by x1 and y1.  The end point
300  *     is represented by x2 and y2.  Start and end are ordered so that x1 < x2.
301  * options - {Object} Optional properties for calculating the intersection.
302  *
303  * Valid options:
304  * point - {Boolean} Return the intersection point.  If false, the actual
305  *     intersection point will not be calculated.  If true and the segments
306  *     intersect, the intersection point will be returned.  If true and
307  *     the segments do not intersect, false will be returned.  If true and
308  *     the segments are coincident, true will be returned.
309  * tolerance - {Number} If a non-null value is provided, if the segments are
310  *     within the tolerance distance, this will be considered an intersection.
311  *     In addition, if the point option is true and the calculated intersection
312  *     is within the tolerance distance of an end point, the endpoint will be
313  *     returned instead of the calculated intersection.  Further, if the
314  *     intersection is within the tolerance of endpoints on both segments, or
315  *     if two segment endpoints are within the tolerance distance of eachother
316  *     (but no intersection is otherwise calculated), an endpoint on the
317  *     first segment provided will be returned.
318  *
319  * Returns:
320  * {Boolean | <OpenLayers.Geometry.Point>}  The two segments intersect.
321  *     If the point argument is true, the return will be the intersection
322  *     point or false if none exists.  If point is true and the segments
323  *     are coincident, return will be true (and the instersection is equal
324  *     to the shorter segment).
325  */
326 OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) {
327     var point = options && options.point;
328     var tolerance = options && options.tolerance;
329     var intersection = false;
330     var x11_21 = seg1.x1 - seg2.x1;
331     var y11_21 = seg1.y1 - seg2.y1;
332     var x12_11 = seg1.x2 - seg1.x1;
333     var y12_11 = seg1.y2 - seg1.y1;
334     var y22_21 = seg2.y2 - seg2.y1;
335     var x22_21 = seg2.x2 - seg2.x1;
336     var d = (y22_21 * x12_11) - (x22_21 * y12_11);
337     var n1 = (x22_21 * y11_21) - (y22_21 * x11_21);
338     var n2 = (x12_11 * y11_21) - (y12_11 * x11_21);
339     if(d == 0) {
340         // parallel
341         if(n1 == 0 && n2 == 0) {
342             // coincident
343             intersection = true;
344         }
345     } else {
346         var along1 = n1 / d;
347         var along2 = n2 / d;
348         if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
349             // intersect
350             if(!point) {
351                 intersection = true;
352             } else {
353                 // calculate the intersection point
354                 var x = seg1.x1 + (along1 * x12_11);
355                 var y = seg1.y1 + (along1 * y12_11);
356                 intersection = new OpenLayers.Geometry.Point(x, y);
357             }
358         }
359     }
360     if(tolerance) {
361         var dist;
362         if(intersection) {
363             if(point) {
364                 var segs = [seg1, seg2];
365                 var seg, x, y;
366                 // check segment endpoints for proximity to intersection
367                 // set intersection to first endpoint within the tolerance
368                 outer: for(var i=0; i<2; ++i) {
369                     seg = segs[i];
370                     for(var j=1; j<3; ++j) {
371                         x = seg["x" + j];
372                         y = seg["y" + j];
373                         dist = Math.sqrt(
374                             Math.pow(x - intersection.x, 2) +
375                             Math.pow(y - intersection.y, 2)
376                         );
377                         if(dist < tolerance) {
378                             intersection.x = x;
379                             intersection.y = y;
380                             break outer;
381                         }
382                     }
383                 }
384                 
385             }
386         } else {
387             // no calculated intersection, but segments could be within
388             // the tolerance of one another
389             var segs = [seg1, seg2];
390             var source, target, x, y, p, result;
391             // check segment endpoints for proximity to intersection
392             // set intersection to first endpoint within the tolerance
393             outer: for(var i=0; i<2; ++i) {
394                 source = segs[i];
395                 target = segs[(i+1)%2];
396                 for(var j=1; j<3; ++j) {
397                     p = {x: source["x"+j], y: source["y"+j]};
398                     result = OpenLayers.Geometry.distanceToSegment(p, target);
399                     if(result.distance < tolerance) {
400                         if(point) {
401                             intersection = new OpenLayers.Geometry.Point(p.x, p.y);
402                         } else {
403                             intersection = true;
404                         }
405                         break outer;
406                     }
407                 }
408             }
409         }
410     }
411     return intersection;
412 };
413
414 /**
415  * Function: OpenLayers.Geometry.distanceToSegment
416  *
417  * Parameters:
418  * point - {Object} An object with x and y properties representing the
419  *     point coordinates.
420  * segment - {Object} An object with x1, y1, x2, and y2 properties
421  *     representing endpoint coordinates.
422  *
423  * Returns:
424  * {Object} An object with distance, x, and y properties.  The distance
425  *     will be the shortest distance between the input point and segment.
426  *     The x and y properties represent the coordinates along the segment
427  *     where the shortest distance meets the segment.
428  */
429 OpenLayers.Geometry.distanceToSegment = function(point, segment) {
430     var x0 = point.x;
431     var y0 = point.y;
432     var x1 = segment.x1;
433     var y1 = segment.y1;
434     var x2 = segment.x2;
435     var y2 = segment.y2;
436     var dx = x2 - x1;
437     var dy = y2 - y1;
438     var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
439                 (Math.pow(dx, 2) + Math.pow(dy, 2));
440     var x, y;
441     if(along <= 0.0) {
442         x = x1;
443         y = y1;
444     } else if(along >= 1.0) {
445         x = x2;
446         y = y2;
447     } else {
448         x = x1 + along * dx;
449         y = y1 + along * dy;
450     }
451     return {
452         distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),
453         x: x, y: y
454     };
455 };