diff --git a/examples/intersects.html b/examples/intersects.html new file mode 100644 index 0000000000..6f5624094a --- /dev/null +++ b/examples/intersects.html @@ -0,0 +1,178 @@ + + + Geometry Intersections + + + + + + +
+

OpenLayers Geometry Intersection Example

+
+
+ + + +
+
+
+ Features +
+ + Intersections +
+ +
+ + diff --git a/lib/OpenLayers/Geometry.js b/lib/OpenLayers/Geometry.js index 32810863eb..3fd6ae6b7d 100644 --- a/lib/OpenLayers/Geometry.js +++ b/lib/OpenLayers/Geometry.js @@ -200,3 +200,67 @@ OpenLayers.Geometry = OpenLayers.Class({ CLASS_NAME: "OpenLayers.Geometry" }); + + +/** + * 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. + * 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. + * + * 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, point) { + 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); + } + } + } + return intersection; +}; diff --git a/lib/OpenLayers/Geometry/Collection.js b/lib/OpenLayers/Geometry/Collection.js index ccd061ab69..863ff5d83d 100644 --- a/lib/OpenLayers/Geometry/Collection.js +++ b/lib/OpenLayers/Geometry/Collection.js @@ -306,5 +306,27 @@ OpenLayers.Geometry.Collection = OpenLayers.Class(OpenLayers.Geometry, { return equivalent; }, + /** + * APIMethod: intersects + * Determine if the input geometry intersects this one. + * + * Parameters: + * geometry - {} Any type of geometry. + * + * Returns: + * {Boolean} The input geometry intersects this one. + */ + intersects: function(geometry) { + var intersect = false; + for(var i=0; i} + * + * 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; i 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); + for(var i=0; i} + * + * Returns: + * {Boolean | Number} The point is inside the linear ring. Returns 1 if + * the point is coincident with an edge. Returns boolean otherwise. + */ + containsPoint: function(point) { + var px = point.x; + var py = point.y; + function getX(y, x1, y1, x2, y2) { + return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2); + } + var numSeg = this.components.length - 1; + var start, end, x1, y1, x2, y2, cx, cy; + var crosses = 0; + for(var i=0; i= x1 && px <= x2) || // right or vert + x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert + // point on edge + crosses = -1; + break; + } + } + // ignore other horizontal edges + continue; + } + cx = getX(py, x1, y1, x2, y2); + if(cx == px) { + // point on line + if(y1 < y2 && (py >= y1 && py <= y2) || // upward + y1 > y2 && (py <= y1 && py >= y2)) { // downward + // point on edge + crosses = -1; + break; + } + } + if(cx <= px) { + // no crossing to the right + continue; + } + if(cx < Math.min(x1, x2) || cx > Math.max(x1, x2)) { + // no crossing + continue; + } + if(y1 < y2 && (py >= y1 && py < y2) || // upward + y1 > y2 && (py < y1 && py >= y2)) { // downward + ++crosses; + } + } + var contained = (crosses == -1) ? + // on edge + 1 : + // even (out) or odd (in) + !!(crosses & 1); + + return contained; + }, + + /** + * APIMethod: intersects + * Determine if the input geometry intersects this one. + * + * Parameters: + * geometry - {} Any type of geometry. + * + * Returns: + * {Boolean} The input geometry intersects this one. + */ + intersects: function(geometry) { + var intersect = false; + if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + intersect = this.containsPoint(geometry); + } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { + intersect = geometry.intersects(this); + } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { + intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply( + this, [geometry] + ); + } else { + // check for component intersections + for(var i=0; i} Any type of geometry. + * + * Returns: + * {Boolean} The input geometry intersects this one. + */ + intersects: function(geometry) { + var intersect = false; + if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + intersect = this.equals(geometry); + } else { + intersect = geometry.intersects(this); + } + return intersect; + }, CLASS_NAME: "OpenLayers.Geometry.Point" }); diff --git a/lib/OpenLayers/Geometry/Polygon.js b/lib/OpenLayers/Geometry/Polygon.js index 2b7c21ef9d..5b26823686 100644 --- a/lib/OpenLayers/Geometry/Polygon.js +++ b/lib/OpenLayers/Geometry/Polygon.js @@ -57,6 +57,102 @@ OpenLayers.Geometry.Polygon = OpenLayers.Class( return area; }, + /** + * Method: containsPoint + * Test if a point is inside a polygon. Points on a polygon edge are + * considered inside. + * + * Parameters: + * point - {} + * + * Returns: + * {Boolean | Number} The point is inside the polygon. Returns 1 if the + * point is on an edge. Returns boolean otherwise. + */ + containsPoint: function(point) { + var numRings = this.components.length; + var contained = false; + if(numRings > 0) { + // check exterior ring - 1 means on edge, boolean otherwise + contained = this.components[0].containsPoint(point); + if(contained !== 1) { + if(contained && numRings > 1) { + // check interior rings + var hole; + for(var i=1; i} Any type of geometry. + * + * Returns: + * {Boolean} The input geometry intersects this one. + */ + intersects: function(geometry) { + var intersect = false; + var i; + if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { + intersect = this.containsPoint(geometry); + } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || + geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { + // check if rings/linestrings intersect + for(i=0; i +