Skip to content

Commit c8c4b9f

Browse files
committed
[GEO] Update ShapeBuilder and GeoPolygonQueryParser to accept unclosed GeoJSON
While the GeoJSON spec does say a polygon is represented as an array of LinearRings (where a LinearRing is defined as a 'closed' array of points), it is a simple change to close the polygon for users. This addresses situations like those integrated with twitter (where GeoJSON polygons are not closed) such that our users do not have to write extra code to close the polygon.
1 parent 3ee9ae6 commit c8c4b9f

File tree

6 files changed

+25
-10
lines changed

6 files changed

+25
-10
lines changed

src/main/java/org/elasticsearch/common/geo/builders/PolygonBuilder.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ public PolygonBuilder(Orientation orientation) {
3636
protected PolygonBuilder(ArrayList<Coordinate> points, Orientation orientation) {
3737
super(orientation);
3838
this.shell = new Ring<>(this, points);
39+
if (!points.isEmpty()) {
40+
this.close();
41+
}
3942
}
4043

4144
@Override

src/main/java/org/elasticsearch/common/geo/builders/ShapeBuilder.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -886,9 +886,6 @@ protected static LineStringBuilder parseLinearRing(CoordinateNode coordinates) {
886886
} else if (coordinates.children.size() < 4) {
887887
throw new ElasticsearchParseException("Invalid number of points in LinearRing (found " +
888888
coordinates.children.size() + " - must be >= 4)");
889-
} else if (!coordinates.children.get(0).coordinate.equals(
890-
coordinates.children.get(coordinates.children.size() - 1).coordinate)) {
891-
throw new ElasticsearchParseException("Invalid LinearRing found (coordinates are not closed)");
892889
}
893890
return parseLineString(coordinates);
894891
}

src/main/java/org/elasticsearch/index/mapper/geo/GeoPointFieldMapper.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -538,9 +538,7 @@ public Mapper parse(ParseContext context) throws IOException {
538538
double lon = context.parser().doubleValue();
539539
token = context.parser().nextToken();
540540
double lat = context.parser().doubleValue();
541-
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) {
542-
543-
}
541+
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY);
544542
parse(context, sparse.reset(lat, lon), null);
545543
} else {
546544
while (token != XContentParser.Token.END_ARRAY) {

src/main/java/org/elasticsearch/index/query/GeoPolygonQueryParser.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ public Query parse(QueryParseContext parseContext) throws IOException, QueryPars
9393
while ((token = parser.nextToken()) != Token.END_ARRAY) {
9494
shell.add(GeoUtils.parseGeoPoint(parser));
9595
}
96+
if (!shell.get(shell.size()-1).equals(shell.get(0))) {
97+
shell.add(shell.get(0));
98+
}
9699
} else {
97100
throw new QueryParsingException(parseContext, "[geo_polygon] query does not support [" + currentFieldName
98101
+ "]");

src/test/java/org/elasticsearch/common/geo/GeoJSONShapeParserTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@ public void testParse_polygonNoHoles() throws IOException {
192192
.startArray().value(101.0).value(1.0).endArray()
193193
.startArray().value(101.0).value(0.0).endArray()
194194
.startArray().value(100.0).value(0.0).endArray()
195-
.startArray().value(100.0).value(1.0).endArray()
196195
.endArray()
197196
.endArray()
198197
.endObject().string();

src/test/java/org/elasticsearch/search/geo/GeoPolygonTests.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@
2727
import org.junit.Test;
2828

2929
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
30+
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
3031
import static org.elasticsearch.index.query.QueryBuilders.geoPolygonQuery;
31-
import static org.elasticsearch.index.query.QueryBuilders.filteredQuery;
32-
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
3332
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
3433
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
3534
import static org.hamcrest.Matchers.anyOf;
@@ -88,7 +87,7 @@ protected void setupSuiteScopeCluster() throws Exception {
8887
public void simplePolygonTest() throws Exception {
8988

9089
SearchResponse searchResponse = client().prepareSearch("test") // from NY
91-
.setQuery(filteredQuery(matchAllQuery(), geoPolygonQuery("location")
90+
.setQuery(boolQuery().must(geoPolygonQuery("location")
9291
.addPoint(40.7, -74.0)
9392
.addPoint(40.7, -74.1)
9493
.addPoint(40.8, -74.1)
@@ -101,4 +100,20 @@ public void simplePolygonTest() throws Exception {
101100
assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5")));
102101
}
103102
}
103+
104+
@Test
105+
public void simpleUnclosedPolygon() throws Exception {
106+
SearchResponse searchResponse = client().prepareSearch("test") // from NY
107+
.setQuery(boolQuery().must(geoPolygonQuery("location")
108+
.addPoint(40.7, -74.0)
109+
.addPoint(40.7, -74.1)
110+
.addPoint(40.8, -74.1)
111+
.addPoint(40.8, -74.0)))
112+
.execute().actionGet();
113+
assertHitCount(searchResponse, 4);
114+
assertThat(searchResponse.getHits().hits().length, equalTo(4));
115+
for (SearchHit hit : searchResponse.getHits()) {
116+
assertThat(hit.id(), anyOf(equalTo("1"), equalTo("3"), equalTo("4"), equalTo("5")));
117+
}
118+
}
104119
}

0 commit comments

Comments
 (0)