|
46 | 46 | import java.util.Objects;
|
47 | 47 | import java.util.concurrent.atomic.AtomicBoolean;
|
48 | 48 |
|
| 49 | +import static org.apache.lucene.geo.GeoUtils.orient; |
| 50 | + |
49 | 51 | /**
|
50 | 52 | * The {@link PolygonBuilder} implements the groundwork to create polygons. This contains
|
51 | 53 | * Methods to wrap polygons at the dateline and building shapes from the data held by the
|
@@ -642,14 +644,8 @@ private static int createEdges(int component, Orientation orientation, LineStrin
|
642 | 644 | */
|
643 | 645 | private static Edge[] ring(int component, boolean direction, boolean handedness,
|
644 | 646 | Coordinate[] points, int offset, Edge[] edges, int toffset, int length, final AtomicBoolean translated) {
|
645 |
| - // calculate the direction of the points: |
646 |
| - // find the point a the top of the set and check its |
647 |
| - // neighbors orientation. So direction is equivalent |
648 |
| - // to clockwise/counterclockwise |
649 |
| - final int top = top(points, offset, length); |
650 |
| - final int prev = (offset + ((top + length - 1) % length)); |
651 |
| - final int next = (offset + ((top + 1) % length)); |
652 |
| - boolean orientation = points[offset + prev].x > points[offset + next].x; |
| 647 | + |
| 648 | + boolean orientation = getOrientation(points, offset, length); |
653 | 649 |
|
654 | 650 | // OGC requires shell as ccw (Right-Handedness) and holes as cw (Left-Handedness)
|
655 | 651 | // since GeoJSON doesn't specify (and doesn't need to) GEO core will assume OGC standards
|
@@ -678,6 +674,35 @@ private static Edge[] ring(int component, boolean direction, boolean handedness,
|
678 | 674 | return concat(component, direction ^ orientation, points, offset, edges, toffset, length);
|
679 | 675 | }
|
680 | 676 |
|
| 677 | + /** |
| 678 | + * @return whether the points are clockwise (true) or anticlockwise (false) |
| 679 | + */ |
| 680 | + private static boolean getOrientation(Coordinate[] points, int offset, int length) { |
| 681 | + // calculate the direction of the points: find the southernmost point |
| 682 | + // and check its neighbors orientation. |
| 683 | + |
| 684 | + final int top = top(points, offset, length); |
| 685 | + final int prev = (top + length - 1) % length; |
| 686 | + final int next = (top + 1) % length; |
| 687 | + |
| 688 | + final int determinantSign = orient( |
| 689 | + points[offset + prev].x, points[offset + prev].y, |
| 690 | + points[offset + top].x, points[offset + top].y, |
| 691 | + points[offset + next].x, points[offset + next].y); |
| 692 | + |
| 693 | + if (determinantSign == 0) { |
| 694 | + // Points are collinear, but `top` is not in the middle if so, so the edges either side of `top` are intersecting. |
| 695 | + throw new InvalidShapeException("Cannot determine orientation: edges adjacent to (" |
| 696 | + + points[offset + top].x + "," + points[offset +top].y + ") coincide"); |
| 697 | + } |
| 698 | + |
| 699 | + return determinantSign < 0; |
| 700 | + } |
| 701 | + |
| 702 | + /** |
| 703 | + * @return the (offset) index of the point that is furthest west amongst |
| 704 | + * those points that are the furthest south in the set. |
| 705 | + */ |
681 | 706 | private static int top(Coordinate[] points, int offset, int length) {
|
682 | 707 | int top = 0; // we start at 1 here since top points to 0
|
683 | 708 | for (int i = 1; i < length; i++) {
|
|
0 commit comments