|
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