More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Geometry.js
blob:a/labs/openlayers/lib/OpenLayers/Geometry.js -> blob:b/labs/openlayers/lib/OpenLayers/Geometry.js
  /* 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 <OpenLayers.Geometry> 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
  * {<OpenLayers.Geometry>}This is set when a Geometry is added as component
  * of another geometry
  */
  parent: null,
   
  /**
  * Property: bounds
  * {<OpenLayers.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:
  * {<OpenLayers.Geometry>} An exact clone of this geometry.
  */
  clone: function() {
  return new OpenLayers.Geometry();
  },
   
  /**
  * Set the bounds for this Geometry.
  *
  * Parameters:
  * object - {<OpenLayers.Bounds>}
  */
  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 - {<OpenLayers.Bounds>}
  */
  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:
  * {<OpenLayers.Bounds>}
  */
  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 - {<OpenLayers.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 - {<OpenLayers.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:
  * {<OpenLayers.Geometry.Point>} 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:
  * {<OpenLayers.Geometry>} 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<len; ++i) {
  components[i] = result[i].geometry;
  }
  geom = new OpenLayers.Geometry.Collection(components);
  }
  return geom;
  };
   
  /**
  * Method: OpenLayers.Geometry.segmentsIntersect
  * Determine whether two line segments intersect. Optionally calculates
  * and returns the intersection point. This function is optimized for
  * cases where seg1.x2 >= 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 | <OpenLayers.Geometry.Point>} 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;