Skip to content

Commit b6987ef

Browse files
committed
Geo: Don't flip longitude of envelopes crossing dateline (#34535)
When a envelope that crosses the dateline is specified as a part of geo_shape query is parsed it shouldn't have its left and right points flipped. Fixes #34418
1 parent 4030318 commit b6987ef

File tree

5 files changed

+79
-8
lines changed

5 files changed

+79
-8
lines changed

docs/reference/mapping/types/geo-shape.asciidoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ POST /example/doc
567567

568568
Elasticsearch supports an `envelope` type, which consists of coordinates
569569
for upper left and lower right points of the shape to represent a
570-
bounding rectangle:
570+
bounding rectangle in the format [[minLon, maxLat],[maxLon, minLat]]:
571571

572572
[source,js]
573573
--------------------------------------------------

docs/reference/migration/migrate_6_0/search.asciidoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ been removed. Also the deprecated `min_word_len` (a synonym for `min_word_length
5151

5252
* The `optimize_bbox` parameter has been removed from `geo_distance` queries.
5353

54+
* An `envelope` crossing the dateline in a `geo_shape `query is now processed
55+
correctly when specified using REST API instead of having its left and
56+
right corners flipped.
57+
5458
* The `ignore_malformed` and `coerce` parameters have been removed from
5559
`geo_bounding_box`, `geo_polygon`, and `geo_distance` queries.
5660

server/src/main/java/org/elasticsearch/common/geo/GeoShapeType.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,6 @@ public ShapeBuilder getBuilder(CoordinateNode coordinates, DistanceUnit.Distance
223223
// verify coordinate bounds, correct if necessary
224224
Coordinate uL = coordinates.children.get(0).coordinate;
225225
Coordinate lR = coordinates.children.get(1).coordinate;
226-
if (((lR.x < uL.x) || (uL.y < lR.y))) {
227-
Coordinate uLtmp = uL;
228-
uL = new Coordinate(Math.min(uL.x, lR.x), Math.max(uL.y, lR.y));
229-
lR = new Coordinate(Math.max(uLtmp.x, lR.x), Math.min(uLtmp.y, lR.y));
230-
}
231226
return new EnvelopeBuilder(uL, lR);
232227
}
233228

server/src/test/java/org/elasticsearch/common/geo/GeoJsonShapeParserTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,15 @@ public void testParseEnvelope() throws IOException {
176176
Rectangle expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30);
177177
assertGeometryEquals(expected, multilinesGeoJson);
178178

179-
// test #2: envelope with agnostic coordinate order (TopRight, BottomLeft)
179+
// test #2: envelope that spans dateline
180180
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
181181
.startArray("coordinates")
182182
.startArray().value(50).value(30).endArray()
183183
.startArray().value(-50).value(-30).endArray()
184184
.endArray()
185185
.endObject();
186186

187-
expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30);
187+
expected = SPATIAL_CONTEXT.makeRectangle(50, -50, -30, 30);
188188
assertGeometryEquals(expected, multilinesGeoJson);
189189

190190
// test #3: "envelope" (actually a triangle) with invalid number of coordinates (TopRight, BottomLeft, BottomRight)

server/src/test/java/org/elasticsearch/search/geo/GeoShapeQueryTests.java

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
package org.elasticsearch.search.geo;
2121

2222
import org.elasticsearch.action.get.GetResponse;
23+
import org.elasticsearch.action.index.IndexRequest;
2324
import org.elasticsearch.action.search.SearchResponse;
25+
import org.elasticsearch.common.CheckedSupplier;
2426
import org.elasticsearch.common.Strings;
2527
import org.elasticsearch.common.geo.ShapeRelation;
2628
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
@@ -32,6 +34,7 @@
3234
import org.elasticsearch.common.settings.Settings;
3335
import org.elasticsearch.common.xcontent.XContentBuilder;
3436
import org.elasticsearch.common.xcontent.XContentFactory;
37+
import org.elasticsearch.common.xcontent.XContentParser;
3538
import org.elasticsearch.common.xcontent.XContentType;
3639
import org.elasticsearch.index.mapper.MapperParsingException;
3740
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
@@ -531,4 +534,73 @@ public void testFieldAlias() throws IOException {
531534
.execute().actionGet();
532535
assertEquals(1, response.getHits().getTotalHits());
533536
}
537+
538+
// Test for issue #34418
539+
public void testEnvelopeSpanningDateline() throws IOException {
540+
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
541+
.startObject("doc")
542+
.startObject("properties")
543+
.startObject("geo").field("type", "geo_shape").endObject()
544+
.endObject()
545+
.endObject()
546+
.endObject();
547+
548+
createIndex("test", Settings.builder().put("index.number_of_shards", 1).build(), "doc", mapping);
549+
550+
String doc1 = "{\"geo\": {\r\n" + "\"coordinates\": [\r\n" + "-33.918711,\r\n" + "18.847685\r\n" + "],\r\n" +
551+
"\"type\": \"Point\"\r\n" + "}}";
552+
client().index(new IndexRequest("test", "doc", "1").source(doc1, XContentType.JSON).setRefreshPolicy(IMMEDIATE)).actionGet();
553+
554+
String doc2 = "{\"geo\": {\r\n" + "\"coordinates\": [\r\n" + "-49.0,\r\n" + "18.847685\r\n" + "],\r\n" +
555+
"\"type\": \"Point\"\r\n" + "}}";
556+
client().index(new IndexRequest("test", "doc", "2").source(doc2, XContentType.JSON).setRefreshPolicy(IMMEDIATE)).actionGet();
557+
558+
String doc3 = "{\"geo\": {\r\n" + "\"coordinates\": [\r\n" + "49.0,\r\n" + "18.847685\r\n" + "],\r\n" +
559+
"\"type\": \"Point\"\r\n" + "}}";
560+
client().index(new IndexRequest("test", "doc", "3").source(doc3, XContentType.JSON).setRefreshPolicy(IMMEDIATE)).actionGet();
561+
562+
@SuppressWarnings("unchecked") CheckedSupplier<GeoShapeQueryBuilder, IOException> querySupplier = randomFrom(
563+
() -> QueryBuilders.geoShapeQuery(
564+
"geo",
565+
new EnvelopeBuilder(new Coordinate(-21, 44), new Coordinate(-39, 9))
566+
).relation(ShapeRelation.WITHIN),
567+
() -> {
568+
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
569+
.startObject("geo")
570+
.startObject("shape")
571+
.field("type", "envelope")
572+
.startArray("coordinates")
573+
.startArray().value(-21).value(44).endArray()
574+
.startArray().value(-39).value(9).endArray()
575+
.endArray()
576+
.endObject()
577+
.field("relation", "within")
578+
.endObject()
579+
.endObject();
580+
try (XContentParser parser = createParser(builder)){
581+
parser.nextToken();
582+
return GeoShapeQueryBuilder.fromXContent(parser);
583+
}
584+
},
585+
() -> {
586+
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
587+
.startObject("geo")
588+
.field("shape", "BBOX (-21, -39, 44, 9)")
589+
.field("relation", "within")
590+
.endObject()
591+
.endObject();
592+
try (XContentParser parser = createParser(builder)){
593+
parser.nextToken();
594+
return GeoShapeQueryBuilder.fromXContent(parser);
595+
}
596+
}
597+
);
598+
599+
SearchResponse response = client().prepareSearch("test")
600+
.setQuery(querySupplier.get())
601+
.execute().actionGet();
602+
assertEquals(2, response.getHits().getTotalHits());
603+
assertNotEquals("1", response.getHits().getAt(0).getId());
604+
assertNotEquals("1", response.getHits().getAt(1).getId());
605+
}
534606
}

0 commit comments

Comments
 (0)