From a23e4aefaa90f5d634099ed398bd3df0601a668c Mon Sep 17 00:00:00 2001 From: Colin Goodheart-Smithe Date: Thu, 22 May 2014 14:35:41 +0100 Subject: [PATCH] Geo: Issue with polygons near date line If a polygon is constructed which overlaps the date line but has a hole which lies entirely one to one side of the date line, JTS error saying that the hole is not within the bounds of the polygon because the code which splits the polygon either side of the date line does not add the hole to the correct component of the final set of polygons. The fix ensures this selection happens correctly. Closes #6179 --- .../geo/builders/BasePolygonBuilder.java | 19 ++++----- .../common/geo/ShapeBuilderTests.java | 41 +++++++++++++++++++ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/elasticsearch/common/geo/builders/BasePolygonBuilder.java b/src/main/java/org/elasticsearch/common/geo/builders/BasePolygonBuilder.java index e2b7cceccdd..4de36b60e05 100644 --- a/src/main/java/org/elasticsearch/common/geo/builders/BasePolygonBuilder.java +++ b/src/main/java/org/elasticsearch/common/geo/builders/BasePolygonBuilder.java @@ -19,21 +19,15 @@ package org.elasticsearch.common.geo.builders; +import com.spatial4j.core.shape.Shape; +import com.vividsolutions.jts.geom.*; +import org.elasticsearch.common.xcontent.XContentBuilder; + import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; -import org.elasticsearch.common.xcontent.XContentBuilder; - -import com.spatial4j.core.shape.Shape; -import com.vividsolutions.jts.geom.Coordinate; -import com.vividsolutions.jts.geom.Geometry; -import com.vividsolutions.jts.geom.GeometryFactory; -import com.vividsolutions.jts.geom.LinearRing; -import com.vividsolutions.jts.geom.MultiPolygon; -import com.vividsolutions.jts.geom.Polygon; - /** * The {@link BasePolygonBuilder} implements the groundwork to create polygons. This contains * Methods to wrap polygons at the dateline and building shapes from the data held by the @@ -358,7 +352,10 @@ public abstract class BasePolygonBuilder> extend LOGGER.debug("Holes: " + Arrays.toString(holes)); } for (int i = 0; i < numHoles; i++) { - final Edge current = holes[i]; + final Edge current = new Edge(holes[i].coordinate, holes[i].next); + // the edge intersects with itself at its own coordinate. We need intersect to be set this way so the binary search + // will get the correct position in the edge list and therefore the correct component to add the hole + current.intersect = current.coordinate; final int intersections = intersections(current.coordinate.x, edges); final int pos = Arrays.binarySearch(edges, 0, intersections, current, INTERSECTION_ORDER); assert pos < 0 : "illegal state: two edges cross the datum at the same position"; diff --git a/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java b/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java index 9bb1505db40..f2c0802fee6 100644 --- a/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java +++ b/src/test/java/org/elasticsearch/common/geo/ShapeBuilderTests.java @@ -25,6 +25,7 @@ import com.spatial4j.core.shape.Shape; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Polygon; +import org.elasticsearch.common.geo.builders.PolygonBuilder; import org.elasticsearch.common.geo.builders.ShapeBuilder; import org.elasticsearch.test.ElasticsearchTestCase; import org.junit.Test; @@ -192,5 +193,45 @@ public class ShapeBuilderTests extends ElasticsearchTestCase { assertMultiLineString(shape); } + @Test + public void testDateline() { + // view shape at https://gist.github.com/anonymous/7f1bb6d7e9cd72f5977c + // expect 3 polygons, 1 with a hole + // a giant c shape + PolygonBuilder builder = ShapeBuilder.newPolygon() + .point(-186,0) + .point(-176,0) + .point(-176,3) + .point(-183,3) + .point(-183,5) + .point(-176,5) + .point(-176,8) + .point(-186,8) + .point(-186,0); + + // 3/4 of an embedded 'c', crossing dateline once + builder.hole() + .point(-185,1) + .point(-181,1) + .point(-181,2) + .point(-184,2) + .point(-184,6) + .point(-178,6) + .point(-178,7) + .point(-185,7) + .point(-185,1); + + // embedded hole right of the dateline + builder.hole() + .point(-179,1) + .point(-177,1) + .point(-177,2) + .point(-179,2) + .point(-179,1); + + Shape shape = builder.close().build(); + + assertMultiPolygon(shape); + } }