Skip to content

Commit bfa83b8

Browse files
committed
More robust polygons.
Previously, we only tested the first point in a hole to determine whether a clockwise polygon ring contains a counterclockwise polygon hole. However, with topologically invalid inputs—such as when the first point in the hole is coincident with a point in the exterior ring—this code assumed that the ring did not contain the hole. Now we use a more robust check: we test for containment using the first point in the hole that is not coincident with the ring.
1 parent 2b8efaf commit bfa83b8

File tree

1 file changed

+27
-4
lines changed

1 file changed

+27
-4
lines changed

shp/polygon.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ export default function(record) {
1010
});
1111

1212
holes.forEach(function(hole) {
13-
var point = hole[0];
1413
polygons.some(function(polygon) {
15-
if (ringContains(polygon[0], point)) {
14+
if (ringContainsSome(polygon[0], hole)) {
1615
polygon.push(hole);
1716
return true;
1817
}
@@ -31,12 +30,36 @@ function ringClockwise(ring) {
3130
return area >= 0;
3231
}
3332

33+
function ringContainsSome(ring, hole) {
34+
var i = -1, n = hole.length, c;
35+
while (++i < n) {
36+
if (c = ringContains(ring, hole[i])) {
37+
return c > 0;
38+
}
39+
}
40+
return false;
41+
}
42+
3443
function ringContains(ring, point) {
35-
var x = point[0], y = point[1], contains = false;
44+
var x = point[0], y = point[1], contains = -1;
3645
for (var i = 0, n = ring.length, j = n - 1; i < n; j = i++) {
3746
var pi = ring[i], xi = pi[0], yi = pi[1],
3847
pj = ring[j], xj = pj[0], yj = pj[1];
39-
if (((yi > y) ^ (yj > y)) && (x < (xj - xi) * (y - yi) / (yj - yi) + xi)) contains = !contains;
48+
if (segmentContains(pi, pj, point)) {
49+
return 0;
50+
}
51+
if (((yi > y) !== (yj > y)) && ((x < (xj - xi) * (y - yi) / (yj - yi) + xi))) {
52+
contains = -contains;
53+
}
4054
}
4155
return contains;
4256
}
57+
58+
function segmentContains(p0, p1, p2) {
59+
var x20 = p2[0] - p0[0], y20 = p2[1] - p0[1];
60+
if (x20 === 0 && y20 === 0) return true;
61+
var x10 = p1[0] - p0[0], y10 = p1[1] - p0[1];
62+
if (x10 === 0 && y10 === 0) return false;
63+
var t = (x20 * x10 + y20 * y10) / (x10 * x10 + y10 * y10);
64+
return t < 0 || t > 1 ? false : t === 0 || t === 1 ? true : t * x10 === x20 && t * y10 === y20;
65+
}

0 commit comments

Comments
 (0)