Skip to content

Commit 16c770a

Browse files
committed
Shamos-Hoey simple polygon detection bug fix
1 parent 24e7754 commit 16c770a

4 files changed

Lines changed: 184 additions & 114 deletions

File tree

src/main/java/mil/nga/sf/Point.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,42 @@ public boolean isSimple() {
218218
return true;
219219
}
220220

221+
/**
222+
* Indicates if x values are equal
223+
*
224+
* @param point
225+
* point to compare
226+
* @return true if x is equal
227+
* @since 2.2.1
228+
*/
229+
public boolean equalsX(Point point) {
230+
return Double.doubleToLongBits(x) == Double.doubleToLongBits(point.x);
231+
}
232+
233+
/**
234+
* Indicates if y values are equal
235+
*
236+
* @param point
237+
* point to compare
238+
* @return true if y is equal
239+
* @since 2.2.1
240+
*/
241+
public boolean equalsY(Point point) {
242+
return Double.doubleToLongBits(y) == Double.doubleToLongBits(point.y);
243+
}
244+
245+
/**
246+
* Indicates if x and y values are equal
247+
*
248+
* @param point
249+
* point to compare
250+
* @return true if x and y are equal
251+
* @since 2.2.1
252+
*/
253+
public boolean equalsXY(Point point) {
254+
return equalsX(point) && equalsY(point);
255+
}
256+
221257
/**
222258
* {@inheritDoc}
223259
*/
@@ -252,9 +288,7 @@ public boolean equals(Object obj) {
252288
return false;
253289
} else if (!m.equals(other.m))
254290
return false;
255-
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
256-
return false;
257-
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
291+
if (!equalsXY(other))
258292
return false;
259293
if (z == null) {
260294
if (other.z != null)

src/main/java/mil/nga/sf/util/sweep/ShamosHoey.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,21 @@ public static boolean simplePolygon(List<LineString> rings) {
110110
if (ringCopyPoints.size() >= 3) {
111111
Point first = ringCopyPoints.get(0);
112112
Point last = ringCopyPoints.get(ringCopyPoints.size() - 1);
113-
if (first.getX() == last.getX()
114-
&& first.getY() == last.getY()) {
113+
if (first.equalsXY(last)) {
115114
ringCopyPoints.remove(ringCopyPoints.size() - 1);
116115
}
117116
}
118117

118+
// Remove duplicate consecutive points
119+
for (int j = 0; j < ringCopyPoints.size() - 1; j++) {
120+
Point point = ringCopyPoints.get(j);
121+
Point next = ringCopyPoints.get(j + 1);
122+
if (point.equalsXY(next)) {
123+
ringCopyPoints.remove(j + 1);
124+
j--;
125+
}
126+
}
127+
119128
// Verify enough ring points
120129
if (ringCopyPoints.size() < 3) {
121130
simple = false;

src/main/java/mil/nga/sf/util/sweep/SweepLine.java

Lines changed: 42 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -23,50 +23,56 @@ public class SweepLine {
2323
*/
2424
private class SegmentComparator implements Comparator<Segment> {
2525

26-
/**
27-
* Current sweep x value
28-
*/
29-
private double x;
30-
31-
/**
32-
* Set the current sweep x value
33-
*
34-
* @param x
35-
* x value
36-
*/
37-
public void setX(double x) {
38-
this.x = x;
39-
}
40-
4126
/**
4227
* {@inheritDoc}
4328
*/
4429
@Override
4530
public int compare(Segment segment1, Segment segment2) {
4631

47-
double y1 = yValueAtX(segment1, x);
48-
double y2 = yValueAtX(segment2, x);
49-
50-
int compare;
51-
if (y1 < y2) {
52-
compare = -1;
53-
} else if (y2 < y1) {
54-
compare = 1;
55-
} else if (segment1.getRing() < segment2.getRing()) {
56-
compare = -1;
57-
} else if (segment2.getRing() < segment1.getRing()) {
58-
compare = 1;
59-
} else if (segment1.getEdge() < segment2.getEdge()) {
60-
compare = -1;
61-
} else if (segment2.getEdge() < segment1.getEdge()) {
62-
compare = 1;
63-
} else {
64-
compare = 0;
32+
int compare = 0;
33+
34+
if (segment1 != segment2) {
35+
36+
Point lp1 = segment1.getLeftPoint();
37+
Point rp1 = segment1.getRightPoint();
38+
Point lp2 = segment2.getLeftPoint();
39+
Point rp2 = segment2.getRightPoint();
40+
41+
if (lp1.getX() <= lp2.getX()) {
42+
double s = isLeft(segment1, lp2);
43+
if (s != 0) {
44+
compare = comparison(s > 0);
45+
} else {
46+
if (lp1.equalsX(rp1)) { // Vertical line
47+
compare = comparison(lp1.getY() < lp2.getY());
48+
} else {
49+
compare = comparison(isLeft(segment1, rp2) > 0);
50+
}
51+
}
52+
} else {
53+
double s = isLeft(segment2, lp1);
54+
if (s != 0) {
55+
compare = comparison(s < 0);
56+
} else {
57+
compare = comparison(isLeft(segment2, rp1) < 0);
58+
}
59+
}
6560
}
6661

6762
return compare;
6863
}
6964

65+
/**
66+
* Get the comparison value
67+
*
68+
* @param left
69+
* left comparison result
70+
* @return -1 if left, 1 if right
71+
*/
72+
private int comparison(boolean left) {
73+
return left ? -1 : 1;
74+
}
75+
7076
}
7177

7278
/**
@@ -111,23 +117,16 @@ public Segment add(Event event) {
111117
Segment segment = createSegment(event);
112118

113119
// Add to the tree
114-
comparator.setX(event.getPoint().getX());
115120
tree.add(segment);
116121

117122
// Update the above and below pointers
118123
Segment next = tree.higher(segment);
119-
if (next == null) {
120-
next = tree.first();
121-
}
122124
Segment previous = tree.lower(segment);
123-
if (previous == null) {
124-
previous = tree.last();
125-
}
126-
if (next != segment) {
125+
if (next != null) {
127126
segment.setAbove(next);
128127
next.setBelow(segment);
129128
}
130-
if (previous != segment) {
129+
if (previous != null) {
131130
segment.setBelow(previous);
132131
previous.setAbove(segment);
133132
}
@@ -200,7 +199,7 @@ public boolean intersect(Segment segment1, Segment segment2) {
200199

201200
boolean intersect = false;
202201

203-
if (segment1 != null && segment2 != null && segment1 != segment2) {
202+
if (segment1 != null && segment2 != null) {
204203

205204
int ring1 = segment1.getRing();
206205
int ring2 = segment2.getRing();
@@ -244,7 +243,6 @@ public void remove(Segment segment) {
244243

245244
boolean removed = tree.remove(segment);
246245
if (!removed) {
247-
comparator.setX(segment.getLeftPoint().getX());
248246
removed = tree.remove(segment);
249247
}
250248

@@ -264,28 +262,6 @@ public void remove(Segment segment) {
264262

265263
}
266264

267-
/**
268-
* Get the segment y value at the x location by calculating the line slope
269-
*
270-
* @param segment
271-
* segment
272-
* @param x
273-
* current point x value
274-
* @return segment y value
275-
*/
276-
private double yValueAtX(Segment segment, double x) {
277-
278-
Point left = segment.getLeftPoint();
279-
Point right = segment.getRightPoint();
280-
281-
double m = (right.getY() - left.getY()) / (right.getX() - left.getX());
282-
double b = left.getY() - (m * left.getX());
283-
284-
double y = (m * x) + b;
285-
286-
return y;
287-
}
288-
289265
/**
290266
* XY order of two points
291267
*

0 commit comments

Comments
 (0)