[76] | 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 | }; |
---|