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