More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / Geometry / LineString.js
blob:a/labs/openlayers/lib/OpenLayers/Geometry/LineString.js -> blob:b/labs/openlayers/lib/OpenLayers/Geometry/LineString.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/Geometry/Curve.js
  */
   
  /**
  * Class: OpenLayers.Geometry.LineString
  * A LineString is a Curve which, once two points have been added to it, can
  * never be less than two points long.
  *
  * Inherits from:
  * - <OpenLayers.Geometry.Curve>
  */
  OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
   
  /**
  * Constructor: OpenLayers.Geometry.LineString
  * Create a new LineString geometry
  *
  * Parameters:
  * points - {Array(<OpenLayers.Geometry.Point>)} An array of points used to
  * generate the linestring
  *
  */
  initialize: function(points) {
  OpenLayers.Geometry.Curve.prototype.initialize.apply(this, arguments);
  },
   
  /**
  * APIMethod: removeComponent
  * Only allows removal of a point if there are three or more points in
  * the linestring. (otherwise the result would be just a single point)
  *
  * Parameters:
  * point - {<OpenLayers.Geometry.Point>} The point to be removed
  */
  removeComponent: function(point) {
  if ( this.components && (this.components.length > 2)) {
  OpenLayers.Geometry.Collection.prototype.removeComponent.apply(this,
  arguments);
  }
  },
   
  /**
  * APIMethod: intersects
  * Test for instersection between two geometries. This is a cheapo
  * implementation of the Bently-Ottmann algorigithm. It doesn't
  * really keep track of a sweep line data structure. It is closer
  * to the brute force method, except that segments are sorted and
  * potential intersections are only calculated when bounding boxes
  * intersect.
  *
  * Parameters:
  * geometry - {<OpenLayers.Geometry>}
  *
  * Returns:
  * {Boolean} The input geometry intersects this geometry.
  */
  intersects: function(geometry) {
  var intersect = false;
  var type = geometry.CLASS_NAME;
  if(type == "OpenLayers.Geometry.LineString" ||
  type == "OpenLayers.Geometry.LinearRing" ||
  type == "OpenLayers.Geometry.Point") {
  var segs1 = this.getSortedSegments();
  var segs2;
  if(type == "OpenLayers.Geometry.Point") {
  segs2 = [{
  x1: geometry.x, y1: geometry.y,
  x2: geometry.x, y2: geometry.y
  }];
  } else {
  segs2 = geometry.getSortedSegments();
  }
  var seg1, seg1x1, seg1x2, seg1y1, seg1y2,
  seg2, seg2y1, seg2y2;
  // sweep right
  outer: for(var i=0, len=segs1.length; i<len; ++i) {
  seg1 = segs1[i];
  seg1x1 = seg1.x1;
  seg1x2 = seg1.x2;
  seg1y1 = seg1.y1;
  seg1y2 = seg1.y2;
  inner: for(var j=0, jlen=segs2.length; j<jlen; ++j) {
  seg2 = segs2[j];
  if(seg2.x1 > seg1x2) {
  // seg1 still left of seg2
  break;
  }
  if(seg2.x2 < seg1x1) {
  // seg2 still left of seg1
  continue;
  }
  seg2y1 = seg2.y1;
  seg2y2 = seg2.y2;
  if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) {
  // seg2 above seg1
  continue;
  }
  if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) {
  // seg2 below seg1
  continue;
  }
  if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) {
  intersect = true;
  break outer;
  }
  }
  }
  } else {
  intersect = geometry.intersects(this);
  }
  return intersect;
  },
   
  /**
  * Method: getSortedSegments
  *
  * Returns:
  * {Array} An array of segment objects. Segment objects have 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.
  */
  getSortedSegments: function() {
  var numSeg = this.components.length - 1;
  var segments = new Array(numSeg), point1, point2;
  for(var i=0; i<numSeg; ++i) {
  point1 = this.components[i];
  point2 = this.components[i + 1];
  if(point1.x < point2.x) {
  segments[i] = {
  x1: point1.x,
  y1: point1.y,
  x2: point2.x,
  y2: point2.y
  };
  } else {
  segments[i] = {
  x1: point2.x,
  y1: point2.y,
  x2: point1.x,
  y2: point1.y
  };
  }
  }
  // more efficient to define this somewhere static
  function byX1(seg1, seg2) {
  return seg1.x1 - seg2.x1;
  }
  return segments.sort(byX1);
  },
   
  /**
  * Method: splitWithSegment
  * Split this geometry with the given segment.
  *
  * Parameters:
  * seg - {Object} An object with x1, y1, x2, and y2 properties referencing
  * segment endpoint coordinates.
  * options - {Object} Properties of this object will be used to determine
  * how the split is conducted.
  *
  * Valid options:
  * edge - {Boolean} Allow splitting when only edges intersect. Default is
  * true. If false, a vertex on the source segment must be within the
  * tolerance distance of the intersection to be considered a split.
  * tolerance - {Number} If a non-null value is provided, intersections
  * within the tolerance distance of one of the source segment's
  * endpoints will be assumed to occur at the endpoint.
  *
  * Returns:
  * {Object} An object with *lines* and *points* properties. If the given
  * segment intersects this linestring, the lines array will reference
  * geometries that result from the split. The points array will contain
  * all intersection points. Intersection points are sorted along the
  * segment (in order from x1,y1 to x2,y2).
  */
  splitWithSegment: function(seg, options) {
  var edge = !(options && options.edge === false);
  var tolerance = options && options.tolerance;
  var lines = [];
  var verts = this.getVertices();
  var points = [];
  var intersections = [];
  var split = false;
  var vert1, vert2, point;
  var node, vertex, target;
  var interOptions = {point: true, tolerance: tolerance};
  var result = null;
  for(var i=0, stop=verts.length-2; i<=stop; ++i) {
  vert1 = verts[i];
  points.push(vert1.clone());
  vert2 = verts[i+1];
  target = {x1: vert1.x, y1: vert1.y, x2: vert2.x, y2: vert2.y};
  point = OpenLayers.Geometry.segmentsIntersect(
  seg, target, interOptions
  );
  if(point instanceof OpenLayers.Geometry.Point) {
  if((point.x === seg.x1 && point.y === seg.y1) ||
  (point.x === seg.x2 && point.y === seg.y2) ||
  point.equals(vert1) || point.equals(vert2)) {
  vertex = true;
  } else {
  vertex = false;
  }
  if(vertex || edge) {
  // push intersections different than the previous
  if(!point.equals(intersections[intersections.length-1])) {
  intersections.push(point.clone());
  }
  if(i === 0) {
  if(point.equals(vert1)) {
  continue;
  }
  }
  if(point.equals(vert2)) {
  continue;
  }
  split = true;
  if(!point.equals(vert1)) {
  points.push(point);
  }
  lines.push(new OpenLayers.Geometry.LineString(points));
  points = [point.clone()];
  }
  }
  }
  if(split) {
  points.push(vert2.clone());
  lines.push(new OpenLayers.Geometry.LineString(points));
  }
  if(intersections.length > 0) {
  // sort intersections along segment
  var xDir = seg.x1 < seg.x2 ? 1 : -1;
  var yDir = seg.y1 < seg.y2 ? 1 : -1;
  result = {
  lines: lines,
  points: intersections.sort(function(p1, p2) {
  return (xDir * p1.x - xDir * p2.x) || (yDir * p1.y - yDir * p2.y);
  })
  };
  }
  return result;
  },
   
  /**
  * Method: split
  * Use this geometry (the source) to attempt to split a target geometry.
  *
  * Parameters:
  * target - {<OpenLayers.Geometry>} The target geometry.
  * options - {Object} Properties of this object will be used to determine
  * how the split is conducted.
  *
  * Valid options:
  * mutual - {Boolean} Split the source geometry in addition to the target
  * geometry. Default is false.
  * edge - {Boolean} Allow splitting when only edges intersect. Default is
  * true. If false, a vertex on the source must be within the tolerance
  * distance of the intersection to be considered a split.
  * tolerance - {Number} If a non-null value is provided, intersections
  * within the tolerance distance of an existing vertex on the source
  * will be assumed to occur at the vertex.
  *
  * Returns:
  * {Array} A list of geometries (of this same type as the target) that
  * result from splitting the target with the source geometry. The
  * source and target geometry will remain unmodified. If no split
  * results, null will be returned. If mutual is true and a split
  * results, return will be an array of two arrays - the first will be
  * all geometries that result from splitting the source geometry and
  * the second will be all geometries that result from splitting the
  * target geometry.
  */
  split: function(target, options) {
  var results = null;
  var mutual = options && options.mutual;
  var sourceSplit, targetSplit, sourceParts, targetParts;
  if(target instanceof OpenLayers.Geometry.LineString) {
  var verts = this.getVertices();
  var vert1, vert2, seg, splits, lines, point;
  var points = [];
  sourceParts = [];
  for(var i=0, stop=verts.length-2; i<=stop; ++i) {
  vert1 = verts[i];
  vert2 = verts[i+1];
  seg = {
  x1: vert1.x, y1: vert1.y,
  x2: vert2.x, y2: vert2.y
  };
  targetParts = targetParts || [target];
  if(mutual) {
  points.push(vert1.clone());
  }
  for(var j=0; j<targetParts.length; ++j) {
  splits = targetParts[j].splitWithSegment(seg, options);
  if(splits) {
  // splice in new features
  lines = splits.lines;
  if(lines.length > 0) {
  lines.unshift(j, 1);
  Array.prototype.splice.apply(targetParts, lines);
  j += lines.length - 2;
  }
  if(mutual) {
  for(var k=0, len=splits.points.length; k<len; ++k) {
  point = splits.points[k];
  if(!point.equals(vert1)) {
  points.push(point);
  sourceParts.push(new OpenLayers.Geometry.LineString(points));
  if(point.equals(vert2)) {
  points = [];
  } else {
  points = [point.clone()];
  }
  }
  }
  }
  }
  }
  }
  if(mutual && sourceParts.length > 0 && points.length > 0) {
  points.push(vert2.clone());
  sourceParts.push(new OpenLayers.Geometry.LineString(points));
  }
  } else {
  results = target.splitWith(this, options);
  }
  if(targetParts && targetParts.length > 1) {
  targetSplit = true;
  } else {
  targetParts = [];
  }
  if(sourceParts && sourceParts.length > 1) {
  sourceSplit = true;
  } else {
  sourceParts = [];
  }
  if(targetSplit || sourceSplit) {
  if(mutual) {
  results = [sourceParts, targetParts];
  } else {
  results = targetParts;
  }
  }
  return results;
  },
   
  /**
  * Method: splitWith
  * Split this geometry (the target) with the given geometry (the source).
  *
  * Parameters:
  * geometry - {<OpenLayers.Geometry>} A geometry used to split this
  * geometry (the source).
  * options - {Object} Properties of this object will be used to determine