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. */
6 * @requires OpenLayers/Format/WKT.js
7 * @requires OpenLayers/Feature/Vector.js
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.
16 OpenLayers.Geometry = OpenLayers.Class({
20 * {String} A unique identifier for this geometry.
26 * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
33 * {<OpenLayers.Bounds>} The bounds of this geometry
38 * Constructor: OpenLayers.Geometry
39 * Creates a geometry object.
41 initialize: function() {
42 this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_");
47 * Destroy this geometry.
56 * Create a clone of this geometry. Does not set any non-standard
57 * properties of the cloned geometry.
60 * {<OpenLayers.Geometry>} An exact clone of this geometry.
63 return new OpenLayers.Geometry();
67 * Set the bounds for this Geometry.
70 * object - {<OpenLayers.Bounds>}
72 setBounds: function(bounds) {
74 this.bounds = bounds.clone();
80 * Nullify this components bounds and that of its parent as well.
82 clearBounds: function() {
85 this.parent.clearBounds();
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.
95 * newBounds - {<OpenLayers.Bounds>}
97 extendBounds: function(newBounds){
98 var bounds = this.getBounds();
100 this.setBounds(newBounds);
102 this.bounds.extend(newBounds);
107 * APIMethod: getBounds
108 * Get the bounds for this Geometry. If bounds is not set, it
109 * is calculated again, this makes queries faster.
112 * {<OpenLayers.Bounds>}
114 getBounds: function() {
115 if (this.bounds == null) {
116 this.calculateBounds();
122 * APIMethod: calculateBounds
123 * Recalculate the bounds for the geometry.
125 calculateBounds: function() {
127 // This should be overridden by subclasses.
132 * APIMethod: distanceTo
133 * Calculate the closest distance between two geometries (on the x-y plane).
136 * geometry - {<OpenLayers.Geometry>} The target geometry.
137 * options - {Object} Optional properties for configuring the distance
140 * Valid options depend on the specific geometry type.
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
150 distanceTo: function(geometry, options) {
154 * APIMethod: getVertices
155 * Return a list of all points in this geometry.
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
164 * {Array} A list of all vertices in the geometry.
166 getVertices: function(nodes) {
171 * Note - This is only an approximation based on the bounds of the
175 * lonlat - {<OpenLayers.LonLat>}
176 * toleranceLon - {float} Optional tolerance in Geometric Coords
177 * toleranceLat - {float} Optional tolerance in Geographic Coords
180 * {Boolean} Whether or not the geometry is at the specified location
182 atPoint: function(lonlat, toleranceLon, toleranceLat) {
184 var bounds = this.getBounds();
185 if ((bounds != null) && (lonlat != null)) {
187 var dX = (toleranceLon != null) ? toleranceLon : 0;
188 var dY = (toleranceLat != null) ? toleranceLat : 0;
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);
196 atPoint = toleranceBounds.containsLonLat(lonlat);
203 * Calculate the length of this geometry. This method is defined in
207 * {Float} The length of the collection by summing its parts
209 getLength: function() {
210 //to be overridden by geometries that actually have a length
217 * Calculate the area of this geometry. This method is defined in subclasses.
220 * {Float} The area of the collection by summing its parts
222 getArea: function() {
223 //to be overridden by geometries that actually have an area
229 * APIMethod: getCentroid
230 * Calculate the centroid of this geometry. This method is defined in subclasses.
233 * {<OpenLayers.Geometry.Point>} The centroid of the collection
235 getCentroid: function() {
241 * Returns the Well-Known Text representation of a geometry
244 * {String} Well-Known Text
246 toString: function() {
247 return OpenLayers.Format.WKT.prototype.write(
248 new OpenLayers.Feature.Vector(this)
252 CLASS_NAME: "OpenLayers.Geometry"
256 * Function: OpenLayers.Geometry.fromWKT
257 * Generate a geometry given a Well-Known Text string.
260 * wkt - {String} A string representing the geometry in Well-Known Text.
263 * {<OpenLayers.Geometry>} A geometry of the appropriate class.
265 OpenLayers.Geometry.fromWKT = function(wkt) {
266 var format = arguments.callee.format;
268 format = new OpenLayers.Format.WKT();
269 arguments.callee.format = format;
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;
281 geom = new OpenLayers.Geometry.Collection(components);
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
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.
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.
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).
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);
341 if(n1 == 0 && n2 == 0) {
348 if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
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);
364 var segs = [seg1, seg2];
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) {
370 for(var j=1; j<3; ++j) {
374 Math.pow(x - intersection.x, 2) +
375 Math.pow(y - intersection.y, 2)
377 if(dist < tolerance) {
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) {
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) {
401 intersection = new OpenLayers.Geometry.Point(p.x, p.y);
415 * Function: OpenLayers.Geometry.distanceToSegment
418 * point - {Object} An object with x and y properties representing the
420 * segment - {Object} An object with x1, y1, x2, and y2 properties
421 * representing endpoint coordinates.
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.
429 OpenLayers.Geometry.distanceToSegment = function(point, segment) {
438 var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) /
439 (Math.pow(dx, 2) + Math.pow(dy, 2));
444 } else if(along >= 1.0) {
452 distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)),