add geometry.intersects method for all geometry types (closes #1072)

git-svn-id: http://svn.openlayers.org/trunk/openlayers@5458 dc9f47b5-9b13-0410-9fdd-eb0c1a62fdaf
This commit is contained in:
Tim Schaub
2007-12-17 06:05:35 +00:00
parent 189b12d020
commit 5667311cba
9 changed files with 1135 additions and 1 deletions
+64
View File
@@ -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 | <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, 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;
};
+22
View File
@@ -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 - {<OpenLayers.Geometry>} Any type of geometry.
*
* Returns:
* {Boolean} The input geometry intersects this one.
*/
intersects: function(geometry) {
var intersect = false;
for(var i=0; i<this.components.length; ++ i) {
intersect = geometry.intersects(this.components[i]);
if(intersect) {
break;
}
}
return intersect;
},
/** @final @type String */
CLASS_NAME: "OpenLayers.Geometry.Collection"
});
+110
View File
@@ -41,6 +41,116 @@ OpenLayers.Geometry.LineString = OpenLayers.Class(OpenLayers.Geometry.Curve, {
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; i<segs1.length; ++i) {
seg1 = segs1[i];
seg1x1 = seg1.x1;
seg1x2 = seg1.x2;
seg1y1 = seg1.y1;
seg1y2 = seg1.y2;
inner: for(var j=0; j<segs2.length; ++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);
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);
},
CLASS_NAME: "OpenLayers.Geometry.LineString"
});
+119
View File
@@ -174,6 +174,125 @@ OpenLayers.Geometry.LinearRing = OpenLayers.Class(
}
return area;
},
/**
* Method: containsPoint
* Test if a point is inside a linear ring. For the case where a point
* is coincident with a linear ring edge, returns 1. Otherwise,
* returns boolean.
*
* Parameters:
* point - {<OpenLayers.Geometry.Point>}
*
* 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<numSeg; ++i) {
start = this.components[i];
x1 = start.x;
y1 = start.y;
end = this.components[i + 1];
x2 = end.x;
y2 = end.y;
/**
* The following conditions enforce five edge-crossing rules:
* 1. points coincident with edges are considered contained;
* 2. an upward edge includes its starting endpoint, and
* excludes its final endpoint;
* 3. a downward edge excludes its starting endpoint, and
* includes its final endpoint;
* 4. horizontal edges are excluded; and
* 5. the edge-ray intersection point must be strictly right
* of the point P.
*/
if(y1 == y2) {
// horizontal edge
if(py == y1) {
// point on horizontal line
if(x1 <= x2 && (px >= 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 - {<OpenLayers.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<geometry.components.length; ++ i) {
intersect = geometry.components[i].intersects(this);
if(intersect) {
break;
}
}
}
return intersect;
},
CLASS_NAME: "OpenLayers.Geometry.LinearRing"
});
+20
View File
@@ -168,6 +168,26 @@ OpenLayers.Geometry.Point = OpenLayers.Class(OpenLayers.Geometry, {
this.y = origin.y + (scale * (this.y - origin.y));
this.clearBounds();
},
/**
* APIMethod: intersects
* Determine if the input geometry intersects this one.
*
* Parameters:
* geometry - {<OpenLayers.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.equals(geometry);
} else {
intersect = geometry.intersects(this);
}
return intersect;
},
CLASS_NAME: "OpenLayers.Geometry.Point"
});
+96
View File
@@ -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 - {<OpenLayers.Geometry.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<numRings; ++i) {
hole = this.components[i].containsPoint(point);
if(hole) {
if(hole === 1) {
// on edge
contained = 1;
} else {
// in hole
contained = false;
}
break;
}
}
}
}
}
return contained;
},
/**
* APIMethod: intersects
* Determine if the input geometry intersects this one.
*
* Parameters:
* geometry - {<OpenLayers.Geometry>} 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<this.components.length; ++i) {
intersect = geometry.intersects(this.components[i]);
if(intersect) {
break;
}
}
if(!intersect) {
// check if this poly contains points of the ring/linestring
for(i=0; i<geometry.components.length; ++i) {
intersect = this.containsPoint(geometry.components[i]);
if(intersect) {
break;
}
}
}
} else {
for(i=0; i<geometry.components.length; ++ i) {
intersect = this.intersects(geometry.components[i]);
if(intersect) {
break;
}
}
}
// check case where this poly is wholly contained by another
if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") {
// exterior ring points will be contained in the other geometry
var ring = this.components[0];
for(i=0; i<ring.components.length; ++i) {
intersect = geometry.containsPoint(ring.components[i]);
if(intersect) {
break;
}
}
}
return intersect;
},
CLASS_NAME: "OpenLayers.Geometry.Polygon"
});