/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for * full list of contributors). Published under the Clear BSD license. * See http://svn.openlayers.org/trunk/openlayers/license.txt for the * full text of the license. */ /** * @requires OpenLayers/Format/WKT.js * @requires OpenLayers/Feature/Vector.js */ /** * Class: OpenLayers.Geometry * A Geometry is a description of a geographic object. Create an instance of * this class with the constructor. This is a base class, * typical geometry types are described by subclasses of this class. */ OpenLayers.Geometry = OpenLayers.Class({ /** * Property: id * {String} A unique identifier for this geometry. */ id: null, /** * Property: parent * {}This is set when a Geometry is added as component * of another geometry */ parent: null, /** * Property: bounds * {} The bounds of this geometry */ bounds: null, /** * Constructor: OpenLayers.Geometry * Creates a geometry object. */ initialize: function() { this.id = OpenLayers.Util.createUniqueID(this.CLASS_NAME+ "_"); }, /** * Method: destroy * Destroy this geometry. */ destroy: function() { this.id = null; this.bounds = null; }, /** * APIMethod: clone * Create a clone of this geometry. Does not set any non-standard * properties of the cloned geometry. * * Returns: * {} An exact clone of this geometry. */ clone: function() { return new OpenLayers.Geometry(); }, /** * Set the bounds for this Geometry. * * Parameters: * object - {} */ setBounds: function(bounds) { if (bounds) { this.bounds = bounds.clone(); } }, /** * Method: clearBounds * Nullify this components bounds and that of its parent as well. */ clearBounds: function() { this.bounds = null; if (this.parent) { this.parent.clearBounds(); } }, /** * Method: extendBounds * Extend the existing bounds to include the new bounds. * If geometry's bounds is not yet set, then set a new Bounds. * * Parameters: * newBounds - {} */ extendBounds: function(newBounds){ var bounds = this.getBounds(); if (!bounds) { this.setBounds(newBounds); } else { this.bounds.extend(newBounds); } }, /** * APIMethod: getBounds * Get the bounds for this Geometry. If bounds is not set, it * is calculated again, this makes queries faster. * * Returns: * {} */ getBounds: function() { if (this.bounds == null) { this.calculateBounds(); } return this.bounds; }, /** * APIMethod: calculateBounds * Recalculate the bounds for the geometry. */ calculateBounds: function() { // // This should be overridden by subclasses. // }, /** * APIMethod: distanceTo * Calculate the closest distance between two geometries (on the x-y plane). * * Parameters: * geometry - {} The target geometry. * options - {Object} Optional properties for configuring the distance * calculation. * * Valid options depend on the specific geometry type. * * Returns: * {Number | Object} The distance between this geometry and the target. * If details is true, the return will be an object with distance, * x0, y0, x1, and x2 properties. The x0 and y0 properties represent * the coordinates of the closest point on this geometry. The x1 and y1 * properties represent the coordinates of the closest point on the * target geometry. */ distanceTo: function(geometry, options) { }, /** * APIMethod: getVertices * Return a list of all points in this geometry. * * Parameters: * nodes - {Boolean} For lines, only return vertices that are * endpoints. If false, for lines, only vertices that are not * endpoints will be returned. If not provided, all vertices will * be returned. * * Returns: * {Array} A list of all vertices in the geometry. */ getVertices: function(nodes) { }, /** * Method: atPoint * Note - This is only an approximation based on the bounds of the * geometry. * * Parameters: * lonlat - {} * toleranceLon - {float} Optional tolerance in Geometric Coords * toleranceLat - {float} Optional tolerance in Geographic Coords * * Returns: * {Boolean} Whether or not the geometry is at the specified location */ atPoint: function(lonlat, toleranceLon, toleranceLat) { var atPoint = false; var bounds = this.getBounds(); if ((bounds != null) && (lonlat != null)) { var dX = (toleranceLon != null) ? toleranceLon : 0; var dY = (toleranceLat != null) ? toleranceLat : 0; var toleranceBounds = new OpenLayers.Bounds(this.bounds.left - dX, this.bounds.bottom - dY, this.bounds.right + dX, this.bounds.top + dY); atPoint = toleranceBounds.containsLonLat(lonlat); } return atPoint; }, /** * Method: getLength * Calculate the length of this geometry. This method is defined in * subclasses. * * Returns: * {Float} The length of the collection by summing its parts */ getLength: function() { //to be overridden by geometries that actually have a length // return 0.0; }, /** * Method: getArea * Calculate the area of this geometry. This method is defined in subclasses. * * Returns: * {Float} The area of the collection by summing its parts */ getArea: function() { //to be overridden by geometries that actually have an area // return 0.0; }, /** * APIMethod: getCentroid * Calculate the centroid of this geometry. This method is defined in subclasses. * * Returns: * {} The centroid of the collection */ getCentroid: function() { return null; }, /** * Method: toString * Returns the Well-Known Text representation of a geometry * * Returns: * {String} Well-Known Text */ toString: function() { return OpenLayers.Format.WKT.prototype.write( new OpenLayers.Feature.Vector(this) ); }, CLASS_NAME: "OpenLayers.Geometry" }); /** * Function: OpenLayers.Geometry.fromWKT * Generate a geometry given a Well-Known Text string. * * Parameters: * wkt - {String} A string representing the geometry in Well-Known Text. * * Returns: * {} A geometry of the appropriate class. */ OpenLayers.Geometry.fromWKT = function(wkt) { var format = arguments.callee.format; if(!format) { format = new OpenLayers.Format.WKT(); arguments.callee.format = format; } var geom; var result = format.read(wkt); if(result instanceof OpenLayers.Feature.Vector) { geom = result.geometry; } else if(result instanceof Array) { var len = result.length; var components = new Array(len); for(var i=0; i= seg2.x1 || seg2.x2 >= seg1.x1. In those * obvious cases where there is no intersection, the function should * not be called. * * Parameters: * seg1 - {Object} Object representing a segment with properties x1, y1, x2, * and y2. The start point is represented by x1 and y1. The end point * is represented by x2 and y2. Start and end are ordered so that x1 < x2. * seg2 - {Object} Object representing a segment with properties x1, y1, x2, * and y2. The start point is represented by x1 and y1. The end point * is represented by x2 and y2. Start and end are ordered so that x1 < x2. * options - {Object} Optional properties for calculating the intersection. * * Valid options: * point - {Boolean} Return the intersection point. If false, the actual * intersection point will not be calculated. If true and the segments * intersect, the intersection point will be returned. If true and * the segments do not intersect, false will be returned. If true and * the segments are coincident, true will be returned. * tolerance - {Number} If a non-null value is provided, if the segments are * within the tolerance distance, this will be considered an intersection. * In addition, if the point option is true and the calculated intersection * is within the tolerance distance of an end point, the endpoint will be * returned instead of the calculated intersection. Further, if the * intersection is within the tolerance of endpoints on both segments, or * if two segment endpoints are within the tolerance distance of eachother * (but no intersection is otherwise calculated), an endpoint on the * first segment provided will be returned. * * Returns: * {Boolean | } The two segments intersect. * If the point argument is true, the return will be the intersection * point or false if none exists. If point is true and the segments * are coincident, return will be true (and the instersection is equal * to the shorter segment). */ OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, options) { var point = options && options.point; var tolerance = options && options.tolerance; var intersection = false; var x11_21 = seg1.x1 - seg2.x1; var y11_21 = seg1.y1 - seg2.y1; var x12_11 = seg1.x2 - seg1.x1; var y12_11 = seg1.y2 - seg1.y1; var y22_21 = seg2.y2 - seg2.y1; var x22_21 = seg2.x2 - seg2.x1; var d = (y22_21 * x12_11) - (x22_21 * y12_11); var n1 = (x22_21 * y11_21) - (y22_21 * x11_21); var n2 = (x12_11 * y11_21) - (y12_11 * x11_21); if(d == 0) { // parallel if(n1 == 0 && n2 == 0) { // coincident intersection = true; } } else { var along1 = n1 / d; var along2 = n2 / d; if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) { // intersect if(!point) { intersection = true; } else { // calculate the intersection point var x = seg1.x1 + (along1 * x12_11); var y = seg1.y1 + (along1 * y12_11); intersection = new OpenLayers.Geometry.Point(x, y); } } } if(tolerance) { var dist; if(intersection) { if(point) { var segs = [seg1, seg2]; var seg, x, y; // check segment endpoints for proximity to intersection // set intersection to first endpoint within the tolerance outer: for(var i=0; i<2; ++i) { seg = segs[i]; for(var j=1; j<3; ++j) { x = seg["x" + j]; y = seg["y" + j]; dist = Math.sqrt( Math.pow(x - intersection.x, 2) + Math.pow(y - intersection.y, 2) ); if(dist < tolerance) { intersection.x = x; intersection.y = y; break outer; } } } } } else { // no calculated intersection, but segments could be within // the tolerance of one another var segs = [seg1, seg2]; var source, target, x, y, p, result; // check segment endpoints for proximity to intersection // set intersection to first endpoint within the tolerance outer: for(var i=0; i<2; ++i) { source = segs[i]; target = segs[(i+1)%2]; for(var j=1; j<3; ++j) { p = {x: source["x"+j], y: source["y"+j]}; result = OpenLayers.Geometry.distanceToSegment(p, target); if(result.distance < tolerance) { if(point) { intersection = new OpenLayers.Geometry.Point(p.x, p.y); } else { intersection = true; } break outer; } } } } } return intersection; }; /** * Function: OpenLayers.Geometry.distanceToSegment * * Parameters: * point - {Object} An object with x and y properties representing the * point coordinates. * segment - {Object} An object with x1, y1, x2, and y2 properties * representing endpoint coordinates. * * Returns: * {Object} An object with distance, x, and y properties. The distance * will be the shortest distance between the input point and segment. * The x and y properties represent the coordinates along the segment * where the shortest distance meets the segment. */ OpenLayers.Geometry.distanceToSegment = function(point, segment) { var x0 = point.x; var y0 = point.y; var x1 = segment.x1; var y1 = segment.y1; var x2 = segment.x2; var y2 = segment.y2; var dx = x2 - x1; var dy = y2 - y1; var along = ((dx * (x0 - x1)) + (dy * (y0 - y1))) / (Math.pow(dx, 2) + Math.pow(dy, 2)); var x, y; if(along <= 0.0) { x = x1; y = y1; } else if(along >= 1.0) { x = x2; y = y2; } else { x = x1 + along * dx; y = y1 + along * dy; } return { distance: Math.sqrt(Math.pow(x - x0, 2) + Math.pow(y - y0, 2)), x: x, y: y }; };