Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.locationtech.spatial4j.exception.InvalidShapeException;
import org.locationtech.spatial4j.shape.Shape;
import org.locationtech.spatial4j.shape.jts.JtsGeometry;

import java.io.IOException;
Expand Down Expand Up @@ -634,14 +633,8 @@ private static int createEdges(int component, Orientation orientation, LineStrin
*/
private static Edge[] ring(int component, boolean direction, boolean handedness,
Coordinate[] points, int offset, Edge[] edges, int toffset, int length, final AtomicBoolean translated) {
// calculate the direction of the points:
// find the point a the top of the set and check its
// neighbors orientation. So direction is equivalent
// to clockwise/counterclockwise
final int top = top(points, offset, length);
final int prev = (offset + ((top + length - 1) % length));
final int next = (offset + ((top + 1) % length));
boolean orientation = points[offset + prev].x > points[offset + next].x;

boolean orientation = getOrientation(points, offset, length);

// OGC requires shell as ccw (Right-Handedness) and holes as cw (Left-Handedness)
// since GeoJSON doesn't specify (and doesn't need to) GEO core will assume OGC standards
Expand Down Expand Up @@ -670,6 +663,27 @@ private static Edge[] ring(int component, boolean direction, boolean handedness,
return concat(component, direction ^ orientation, points, offset, edges, toffset, length);
}

/**
* @return whether the points are clockwise (true) or anticlockwise (false)
*/
private static boolean getOrientation(Coordinate[] points, int offset, int length) {
// calculate the direction of the points: find the southernmost point
// and check its neighbors orientation.

final int top = top(points, offset, length);
final int prev = (top + length - 1) % length;
final int next = (top + 1) % length;
final double determinant
= (points[offset + next].x - points[offset + top].x) * (points[offset + prev].y - points[offset + top].y)
- (points[offset + prev].x - points[offset + top].x) * (points[offset + next].y - points[offset + top].y);
assert determinant != 0.0;
return determinant < 0.0;
}

/**
* @return the (offset) index of the point that is furthest west amongst
* those points that are the furthest south in the set.
*/
private static int top(Coordinate[] points, int offset, int length) {
int top = 0; // we start at 1 here since top points to 0
for (int i = 1; i < length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,15 @@ public void testHoleThatIsNorthOfPolygon() {

assertEquals("Hole lies outside shell at or near point (4.0, 3.0, NaN)", e.getMessage());
}

public void testWidePolygonWithConfusingOrientation() {
// A valid polygon that is oriented correctly (anticlockwise) but which
// confounds a naive algorithm for determining its orientation leading
// ES to believe that it crosses the dateline and "fixing" it in a way
// that self-intersects.

PolygonBuilder pb = new PolygonBuilder(new CoordinatesBuilder()
.coordinate(10, -20).coordinate(100, 0).coordinate(-100, 0).coordinate(20, -45).coordinate(40, -60).close());
pb.build(); // Should not throw an exception
}
}