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
This commit is contained in:
Igor Motov 2018-10-19 13:53:54 -04:00 committed by GitHub
parent fba5d39bbb
commit 94bde37bcf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 8 deletions

View File

@ -567,7 +567,7 @@ POST /example/doc
Elasticsearch supports an `envelope` type, which consists of coordinates
for upper left and lower right points of the shape to represent a
bounding rectangle:
bounding rectangle in the format [[minLon, maxLat],[maxLon, minLat]]:
[source,js]
--------------------------------------------------

View File

@ -20,6 +20,10 @@
* Attempts to generate multi-term phrase queries against non-text fields
with a custom analyzer will now throw an exception
* An `envelope` crossing the dateline in a `geo_shape `query is now processed
correctly when specified using REST API instead of having its left and
right corners flipped.
[float]
==== Adaptive replica selection enabled by default

View File

@ -223,11 +223,6 @@ public enum GeoShapeType {
// verify coordinate bounds, correct if necessary
Coordinate uL = coordinates.children.get(0).coordinate;
Coordinate lR = coordinates.children.get(1).coordinate;
if (((lR.x < uL.x) || (uL.y < lR.y))) {
Coordinate uLtmp = uL;
uL = new Coordinate(Math.min(uL.x, lR.x), Math.max(uL.y, lR.y));
lR = new Coordinate(Math.max(uLtmp.x, lR.x), Math.min(uLtmp.y, lR.y));
}
return new EnvelopeBuilder(uL, lR);
}

View File

@ -175,7 +175,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
Rectangle expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30);
assertGeometryEquals(expected, multilinesGeoJson);
// test #2: envelope with agnostic coordinate order (TopRight, BottomLeft)
// test #2: envelope that spans dateline
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
.startArray("coordinates")
.startArray().value(50).value(30).endArray()
@ -183,7 +183,7 @@ public class GeoJsonShapeParserTests extends BaseGeoParsingTestCase {
.endArray()
.endObject();
expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30);
expected = SPATIAL_CONTEXT.makeRectangle(50, -50, -30, 30);
assertGeometryEquals(expected, multilinesGeoJson);
// test #3: "envelope" (actually a triangle) with invalid number of coordinates (TopRight, BottomLeft, BottomRight)

View File

@ -20,7 +20,9 @@
package org.elasticsearch.search.geo;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.CheckedSupplier;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.geo.ShapeRelation;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
@ -32,6 +34,7 @@ import org.elasticsearch.common.geo.builders.ShapeBuilder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.MapperParsingException;
import org.elasticsearch.index.query.GeoShapeQueryBuilder;
@ -531,4 +534,73 @@ public class GeoShapeQueryTests extends ESSingleNodeTestCase {
.execute().actionGet();
assertEquals(1, response.getHits().getTotalHits());
}
// Test for issue #34418
public void testEnvelopeSpanningDateline() throws IOException {
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject()
.startObject("doc")
.startObject("properties")
.startObject("geo").field("type", "geo_shape").endObject()
.endObject()
.endObject()
.endObject();
createIndex("test", Settings.builder().put("index.number_of_shards", 1).build(), "doc", mapping);
String doc1 = "{\"geo\": {\r\n" + "\"coordinates\": [\r\n" + "-33.918711,\r\n" + "18.847685\r\n" + "],\r\n" +
"\"type\": \"Point\"\r\n" + "}}";
client().index(new IndexRequest("test", "doc", "1").source(doc1, XContentType.JSON).setRefreshPolicy(IMMEDIATE)).actionGet();
String doc2 = "{\"geo\": {\r\n" + "\"coordinates\": [\r\n" + "-49.0,\r\n" + "18.847685\r\n" + "],\r\n" +
"\"type\": \"Point\"\r\n" + "}}";
client().index(new IndexRequest("test", "doc", "2").source(doc2, XContentType.JSON).setRefreshPolicy(IMMEDIATE)).actionGet();
String doc3 = "{\"geo\": {\r\n" + "\"coordinates\": [\r\n" + "49.0,\r\n" + "18.847685\r\n" + "],\r\n" +
"\"type\": \"Point\"\r\n" + "}}";
client().index(new IndexRequest("test", "doc", "3").source(doc3, XContentType.JSON).setRefreshPolicy(IMMEDIATE)).actionGet();
@SuppressWarnings("unchecked") CheckedSupplier<GeoShapeQueryBuilder, IOException> querySupplier = randomFrom(
() -> QueryBuilders.geoShapeQuery(
"geo",
new EnvelopeBuilder(new Coordinate(-21, 44), new Coordinate(-39, 9))
).relation(ShapeRelation.WITHIN),
() -> {
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
.startObject("geo")
.startObject("shape")
.field("type", "envelope")
.startArray("coordinates")
.startArray().value(-21).value(44).endArray()
.startArray().value(-39).value(9).endArray()
.endArray()
.endObject()
.field("relation", "within")
.endObject()
.endObject();
try (XContentParser parser = createParser(builder)){
parser.nextToken();
return GeoShapeQueryBuilder.fromXContent(parser);
}
},
() -> {
XContentBuilder builder = XContentFactory.jsonBuilder().startObject()
.startObject("geo")
.field("shape", "BBOX (-21, -39, 44, 9)")
.field("relation", "within")
.endObject()
.endObject();
try (XContentParser parser = createParser(builder)){
parser.nextToken();
return GeoShapeQueryBuilder.fromXContent(parser);
}
}
);
SearchResponse response = client().prepareSearch("test")
.setQuery(querySupplier.get())
.execute().actionGet();
assertEquals(2, response.getHits().getTotalHits());
assertNotEquals("1", response.getHits().getAt(0).getId());
assertNotEquals("1", response.getHits().getAt(1).getId());
}
}