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:
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user