Changeset 5458
- Timestamp:
- 12/17/07 01:05:35 (1 year ago)
- Files:
-
- trunk/openlayers/examples/intersects.html (added)
- trunk/openlayers/lib/OpenLayers/Geometry.js (modified) (1 diff)
- trunk/openlayers/lib/OpenLayers/Geometry/Collection.js (modified) (1 diff)
- trunk/openlayers/lib/OpenLayers/Geometry/LineString.js (modified) (1 diff)
- trunk/openlayers/lib/OpenLayers/Geometry/LinearRing.js (modified) (1 diff)
- trunk/openlayers/lib/OpenLayers/Geometry/Point.js (modified) (1 diff)
- trunk/openlayers/lib/OpenLayers/Geometry/Polygon.js (modified) (1 diff)
- trunk/openlayers/tests/data (added)
- trunk/openlayers/tests/data/geos_wkt_intersects.js (added)
- trunk/openlayers/tests/test_Geometry.html (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/openlayers/lib/OpenLayers/Geometry.js
r4985 r5458 201 201 CLASS_NAME: "OpenLayers.Geometry" 202 202 }); 203 204 205 /** 206 * Method: OpenLayers.Geometry.segmentsIntersect 207 * Determine whether two line segments intersect. Optionally calculates 208 * and returns the intersection point. This function is optimized for 209 * cases where seg1.x2 >= seg2.x1 || seg2.x2 >= seg1.x1. In those 210 * obvious cases where there is no intersection, the function should 211 * not be called. 212 * 213 * Parameters: 214 * seg1 - {Object} Object representing a segment with properties x1, y1, x2, 215 * and y2. The start point is represented by x1 and y1. The end point 216 * is represented by x2 and y2. Start and end are ordered so that x1 < x2. 217 * seg2 - {Object} Object representing a segment with properties x1, y1, x2, 218 * and y2. The start point is represented by x1 and y1. The end point 219 * is represented by x2 and y2. Start and end are ordered so that x1 < x2. 220 * point - {Boolean} Return the intersection point. If false, the actual 221 * intersection point will not be calculated. If true and the segments 222 * intersect, the intersection point will be returned. If true and 223 * the segments do not intersect, false will be returned. If true and 224 * the segments are coincident, true will be returned. 225 * 226 * Returns: 227 * {Boolean | <OpenLayers.Geometry.Point>} The two segments intersect. 228 * If the point argument is true, the return will be the intersection 229 * point or false if none exists. If point is true and the segments 230 * are coincident, return will be true (and the instersection is equal 231 * to the shorter segment). 232 */ 233 OpenLayers.Geometry.segmentsIntersect = function(seg1, seg2, point) { 234 var intersection = false; 235 var x11_21 = seg1.x1 - seg2.x1; 236 var y11_21 = seg1.y1 - seg2.y1; 237 var x12_11 = seg1.x2 - seg1.x1; 238 var y12_11 = seg1.y2 - seg1.y1; 239 var y22_21 = seg2.y2 - seg2.y1; 240 var x22_21 = seg2.x2 - seg2.x1; 241 var d = (y22_21 * x12_11) - (x22_21 * y12_11); 242 var n1 = (x22_21 * y11_21) - (y22_21 * x11_21); 243 var n2 = (x12_11 * y11_21) - (y12_11 * x11_21); 244 if(d == 0) { 245 // parallel 246 if(n1 == 0 && n2 == 0) { 247 // coincident 248 intersection = true; 249 } 250 } else { 251 var along1 = n1 / d; 252 var along2 = n2 / d; 253 if(along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) { 254 // intersect 255 if(!point) { 256 intersection = true; 257 } else { 258 // calculate the intersection point 259 var x = seg1.x1 + (along1 * x12_11); 260 var y = seg1.y1 + (along1 * y12_11); 261 intersection = new OpenLayers.Geometry.Point(x, y); 262 } 263 } 264 } 265 return intersection; 266 }; trunk/openlayers/lib/OpenLayers/Geometry/Collection.js
r5347 r5458 307 307 }, 308 308 309 /** 310 * APIMethod: intersects 311 * Determine if the input geometry intersects this one. 312 * 313 * Parameters: 314 * geometry - {<OpenLayers.Geometry>} Any type of geometry. 315 * 316 * Returns: 317 * {Boolean} The input geometry intersects this one. 318 */ 319 intersects: function(geometry) { 320 var intersect = false; 321 for(var i=0; i<this.components.length; ++ i) { 322 intersect = geometry.intersects(this.components[i]); 323 if(intersect) { 324 break; 325 } 326 } 327 return intersect; 328 }, 329 330 /** @final @type String */ 309 331 CLASS_NAME: "OpenLayers.Geometry.Collection" 310 332 }); trunk/openlayers/lib/OpenLayers/Geometry/LineString.js
r4985 r5458 42 42 } 43 43 }, 44 45 /** 46 * APIMethod: intersects 47 * Test for instersection between two geometries. This is a cheapo 48 * implementation of the Bently-Ottmann algorigithm. It doesn't 49 * really keep track of a sweep line data structure. It is closer 50 * to the brute force method, except that segments are sorted and 51 * potential intersections are only calculated when bounding boxes 52 * intersect. 53 * 54 * Parameters: 55 * geometry - {<OpenLayers.Geometry>} 56 * 57 * Returns: 58 * {Boolean} The input geometry intersects this geometry. 59 */ 60 intersects: function(geometry) { 61 var intersect = false; 62 var type = geometry.CLASS_NAME; 63 if(type == "OpenLayers.Geometry.LineString" || 64 type == "OpenLayers.Geometry.LinearRing" || 65 type == "OpenLayers.Geometry.Point") { 66 var segs1 = this.getSortedSegments(); 67 var segs2; 68 if(type == "OpenLayers.Geometry.Point") { 69 segs2 = [{ 70 x1: geometry.x, y1: geometry.y, 71 x2: geometry.x, y2: geometry.y 72 }]; 73 } else { 74 segs2 = geometry.getSortedSegments(); 75 } 76 var seg1, seg1x1, seg1x2, seg1y1, seg1y2, 77 seg2, seg2y1, seg2y2; 78 // sweep right 79 outer: for(var i=0; i<segs1.length; ++i) { 80 seg1 = segs1[i]; 81 seg1x1 = seg1.x1; 82 seg1x2 = seg1.x2; 83 seg1y1 = seg1.y1; 84 seg1y2 = seg1.y2; 85 inner: for(var j=0; j<segs2.length; ++j) { 86 seg2 = segs2[j]; 87 if(seg2.x1 > seg1x2) { 88 // seg1 still left of seg2 89 break; 90 } 91 if(seg2.x2 < seg1x1) { 92 // seg2 still left of seg1 93 continue; 94 } 95 seg2y1 = seg2.y1; 96 seg2y2 = seg2.y2; 97 if(Math.min(seg2y1, seg2y2) > Math.max(seg1y1, seg1y2)) { 98 // seg2 above seg1 99 continue; 100 } 101 if(Math.max(seg2y1, seg2y2) < Math.min(seg1y1, seg1y2)) { 102 // seg2 below seg1 103 continue; 104 } 105 if(OpenLayers.Geometry.segmentsIntersect(seg1, seg2)) { 106 intersect = true; 107 break outer; 108 } 109 } 110 } 111 } else { 112 intersect = geometry.intersects(this); 113 } 114 return intersect; 115 }, 116 117 /** 118 * Method: getSortedSegments 119 * 120 * Returns: 121 * {Array} An array of segment objects. Segment objects have properties 122 * x1, y1, x2, and y2. The start point is represented by x1 and y1. 123 * The end point is represented by x2 and y2. Start and end are 124 * ordered so that x1 < x2. 125 */ 126 getSortedSegments: function() { 127 var numSeg = this.components.length - 1; 128 var segments = new Array(numSeg); 129 for(var i=0; i<numSeg; ++i) { 130 point1 = this.components[i]; 131 point2 = this.components[i + 1]; 132 if(point1.x < point2.x) { 133 segments[i] = { 134 x1: point1.x, 135 y1: point1.y, 136 x2: point2.x, 137 y2: point2.y 138 } 139 } else { 140 segments[i] = { 141 x1: point2.x, 142 y1: point2.y, 143 x2: point1.x, 144 y2: point1.y 145 } 146 } 147 } 148 // more efficient to define this somewhere static 149 function byX1(seg1, seg2) { 150 return seg1.x1 - seg2.x1; 151 } 152 return segments.sort(byX1); 153 }, 44 154 45 155 CLASS_NAME: "OpenLayers.Geometry.LineString" trunk/openlayers/lib/OpenLayers/Geometry/LinearRing.js
r5200 r5458 175 175 return area; 176 176 }, 177 178 /** 179 * Method: containsPoint 180 * Test if a point is inside a linear ring. For the case where a point 181 * is coincident with a linear ring edge, returns 1. Otherwise, 182 * returns boolean. 183 * 184 * Parameters: 185 * point - {<OpenLayers.Geometry.Point>} 186 * 187 * Returns: 188 * {Boolean | Number} The point is inside the linear ring. Returns 1 if 189 * the point is coincident with an edge. Returns boolean otherwise. 190 */ 191 containsPoint: function(point) { 192 var px = point.x; 193 var py = point.y; 194 function getX(y, x1, y1, x2, y2) { 195 return (((x1 - x2) * y) + ((x2 * y1) - (x1 * y2))) / (y1 - y2); 196 } 197 var numSeg = this.components.length - 1; 198 var start, end, x1, y1, x2, y2, cx, cy; 199 var crosses = 0; 200 for(var i=0; i<numSeg; ++i) { 201 start = this.components[i]; 202 x1 = start.x; 203 y1 = start.y; 204 end = this.components[i + 1]; 205 x2 = end.x; 206 y2 = end.y; 207 208 /** 209 * The following conditions enforce five edge-crossing rules: 210 * 1. points coincident with edges are considered contained; 211 * 2. an upward edge includes its starting endpoint, and 212 * excludes its final endpoint; 213 * 3. a downward edge excludes its starting endpoint, and 214 * includes its final endpoint; 215 * 4. horizontal edges are excluded; and 216 * 5. the edge-ray intersection point must be strictly right 217 * of the point P. 218 */ 219 if(y1 == y2) { 220 // horizontal edge 221 if(py == y1) { 222 // point on horizontal line 223 if(x1 <= x2 && (px >= x1 && px <= x2) || // right or vert 224 x1 >= x2 && (px <= x1 && px >= x2)) { // left or vert 225 // point on edge 226 crosses = -1; 227 break; 228 } 229 } 230 // ignore other horizontal edges 231 continue; 232 } 233 cx = getX(py, x1, y1, x2, y2); 234 if(cx == px) { 235 // point on line 236 if(y1 < y2 && (py >= y1 && py <= y2) || // upward 237 y1 > y2 && (py <= y1 && py >= y2)) { // downward 238 // point on edge 239 crosses = -1; 240 break; 241 } 242 } 243 if(cx <= px) { 244 // no crossing to the right 245 continue; 246 } 247 if(cx < Math.min(x1, x2) || cx > Math.max(x1, x2)) { 248 // no crossing 249 continue; 250 } 251 if(y1 < y2 && (py >= y1 && py < y2) || // upward 252 y1 > y2 && (py < y1 && py >= y2)) { // downward 253 ++crosses; 254 } 255 } 256 var contained = (crosses == -1) ? 257 // on edge 258 1 : 259 // even (out) or odd (in) 260 !!(crosses & 1); 261 262 return contained; 263 }, 264 265 /** 266 * APIMethod: intersects 267 * Determine if the input geometry intersects this one. 268 * 269 * Parameters: 270 * geometry - {<OpenLayers.Geometry>} Any type of geometry. 271 * 272 * Returns: 273 * {Boolean} The input geometry intersects this one. 274 */ 275 intersects: function(geometry) { 276 var intersect = false; 277 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { 278 intersect = this.containsPoint(geometry); 279 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString") { 280 intersect = geometry.intersects(this); 281 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { 282 intersect = OpenLayers.Geometry.LineString.prototype.intersects.apply( 283 this, [geometry] 284 ); 285 } else { 286 // check for component intersections 287 for(var i=0; i<geometry.components.length; ++ i) { 288 intersect = geometry.components[i].intersects(this); 289 if(intersect) { 290 break; 291 } 292 } 293 } 294 return intersect; 295 }, 177 296 178 297 CLASS_NAME: "OpenLayers.Geometry.LinearRing" trunk/openlayers/lib/OpenLayers/Geometry/Point.js
r5200 r5458 169 169 this.clearBounds(); 170 170 }, 171 172 /** 173 * APIMethod: intersects 174 * Determine if the input geometry intersects this one. 175 * 176 * Parameters: 177 * geometry - {<OpenLayers.Geometry>} Any type of geometry. 178 * 179 * Returns: 180 * {Boolean} The input geometry intersects this one. 181 */ 182 intersects: function(geometry) { 183 var intersect = false; 184 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { 185 intersect = this.equals(geometry); 186 } else { 187 intersect = geometry.intersects(this); 188 } 189 return intersect; 190 }, 171 191 172 192 CLASS_NAME: "OpenLayers.Geometry.Point" trunk/openlayers/lib/OpenLayers/Geometry/Polygon.js
r5439 r5458 58 58 }, 59 59 60 /** 61 * Method: containsPoint 62 * Test if a point is inside a polygon. Points on a polygon edge are 63 * considered inside. 64 * 65 * Parameters: 66 * point - {<OpenLayers.Geometry.Point>} 67 * 68 * Returns: 69 * {Boolean | Number} The point is inside the polygon. Returns 1 if the 70 * point is on an edge. Returns boolean otherwise. 71 */ 72 containsPoint: function(point) { 73 var numRings = this.components.length; 74 var contained = false; 75 if(numRings > 0) { 76 // check exterior ring - 1 means on edge, boolean otherwise 77 contained = this.components[0].containsPoint(point); 78 if(contained !== 1) { 79 if(contained && numRings > 1) { 80 // check interior rings 81 var hole; 82 for(var i=1; i<numRings; ++i) { 83 hole = this.components[i].containsPoint(point); 84 if(hole) { 85 if(hole === 1) { 86 // on edge 87 contained = 1; 88 } else { 89 // in hole 90 contained = false; 91 } 92 break; 93 } 94 } 95 } 96 } 97 } 98 return contained; 99 }, 100 101 /** 102 * APIMethod: intersects 103 * Determine if the input geometry intersects this one. 104 * 105 * Parameters: 106 * geometry - {<OpenLayers.Geometry>} Any type of geometry. 107 * 108 * Returns: 109 * {Boolean} The input geometry intersects this one. 110 */ 111 intersects: function(geometry) { 112 var intersect = false; 113 var i; 114 if(geometry.CLASS_NAME == "OpenLayers.Geometry.Point") { 115 intersect = this.containsPoint(geometry); 116 } else if(geometry.CLASS_NAME == "OpenLayers.Geometry.LineString" || 117 geometry.CLASS_NAME == "OpenLayers.Geometry.LinearRing") { 118 // check if rings/linestrings intersect 119 for(i=0; i<this.components.length; ++i) { 120 intersect = geometry.intersects(this.components[i]); 121 if(intersect) { 122 break; 123 } 124 } 125 if(!intersect) { 126 // check if this poly contains points of the ring/linestring 127 for(i=0; i<geometry.components.length; ++i) { 128 intersect = this.containsPoint(geometry.components[i]); 129 if(intersect) { 130 break; 131 } 132 } 133 } 134 } else { 135 for(i=0; i<geometry.components.length; ++ i) { 136 intersect = this.intersects(geometry.components[i]); 137 if(intersect) { 138 break; 139 } 140 } 141 } 142 // check case where this poly is wholly contained by another 143 if(!intersect && geometry.CLASS_NAME == "OpenLayers.Geometry.Polygon") { 144 // exterior ring points will be contained in the other geometry 145 var ring = this.components[0]; 146 for(i=0; i<ring.components.length; ++i) { 147 intersect = geometry.containsPoint(ring.components[i]); 148 if(intersect) { 149 break; 150 } 151 } 152 } 153 return intersect; 154 }, 155 60 156 CLASS_NAME: "OpenLayers.Geometry.Polygon" 61 157 }); trunk/openlayers/tests/test_Geometry.html
r4302 r5458 2 2 <head> 3 3 <script src="../lib/OpenLayers.js"></script> 4 <script src="data/geos_wkt_intersects.js"></script> 4 5 <script type="text/javascript"> 5 6 var map; … … 218 219 } 219 220 220 221 function test_Geometry_intersects_geos_wkt(t) { 222 var wkt = new OpenLayers.Format.WKT(); 223 var failures = []; 224 var intersect12, intersect21, msg; 225 for (var i = 0; i < geos_test_data.length; i++) { 226 var testcase = geos_test_data[i]; 227 f1 = wkt.read(testcase['wkt1']); 228 f2 = wkt.read(testcase['wkt2']); 229 intersect12 = f1.geometry.intersects(f2.geometry); 230 intersect21 = f2.geometry.intersects(f1.geometry); 231 if(intersect12 != testcase.result) { 232 msg = "f1 should " + (testcase.result ? "" : "not ") + 233 "intersect f2: f1 = '" + testcase['wkt1'] + "' " + 234 "f2 = '" + testcase['wkt2'] + "'"; 235 failures.push(msg); 236 } 237 if(intersect21 != testcase.result) { 238 msg = "f2 should " + (testcase.result ? "" : "not ") + 239 "intersect f1: f1 = '" + testcase['wkt1'] + "' " + 240 "f2 = '" + testcase['wkt2'] + "'"; 241 failures.push(msg); 242 } 243 } 244 if(failures.length == 0) { 245 t.plan(1); 246 t.ok(true, "all " + geos_test_data.length + " geos tests pass"); 247 } else { 248 t.plan(failures.length); 249 for(var f=0; f<failures.length; ++f) { 250 t.fail(failures[f]); 251 } 252 } 253 } 221 254 222 255 </script>
