[GEO] GIS envelope validation

ShapeBuilder expected coordinates for Envelope types in strict Top-Left, Bottom-Right order. Given that GeoJSON does not enforce coordinate order (as seen in #8672) clients could specify envelope bounds in any order and be compliant with the GeoJSON spec but not the ES ShapeBuilder logic. This change loosens the ShapeBuilder requirements on envelope coordinate order, reordering where necessary.

closes #2544
closes #9067
closes #9079
closes #9080
This commit is contained in:
Nicholas Knize 2014-12-29 14:01:25 -06:00
parent 31652a8b3d
commit 0e24f34b0c
2 changed files with 47 additions and 2 deletions

View File

@ -787,8 +787,20 @@ public abstract class ShapeBuilder implements ToXContent {
}
protected static EnvelopeBuilder parseEnvelope(CoordinateNode coordinates, Orientation orientation) {
return newEnvelope(orientation).
topLeft(coordinates.children.get(0).coordinate).bottomRight(coordinates.children.get(1).coordinate);
// validate the coordinate array for envelope type
if (coordinates.children.size() != 2) {
throw new ElasticsearchParseException("Invalid number of points (" + coordinates.children.size() + ") provided for " +
"geo_shape ('envelope') when expecting an array of 2 coordinates");
}
// 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 newEnvelope(orientation).topLeft(uL).bottomRight(lR);
}
protected static void validateMultiPointNode(CoordinateNode coordinates) {

View File

@ -121,6 +121,7 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
@Test
public void testParse_envelope() throws IOException {
// test #1: envelope with expected coordinate order (TopLeft, BottomRight)
String multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
.startArray("coordinates")
.startArray().value(-50).value(30).endArray()
@ -130,6 +131,38 @@ public class GeoJSONShapeParserTests extends ElasticsearchTestCase {
Rectangle expected = SPATIAL_CONTEXT.makeRectangle(-50, 50, -30, 30);
assertGeometryEquals(expected, multilinesGeoJson);
// test #2: envelope with agnostic coordinate order (TopRight, BottomLeft)
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
.startArray("coordinates")
.startArray().value(50).value(30).endArray()
.startArray().value(-50).value(-30).endArray()
.endArray()
.endObject().string();
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)
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
.startArray("coordinates")
.startArray().value(50).value(30).endArray()
.startArray().value(-50).value(-30).endArray()
.startArray().value(50).value(-39).endArray()
.endArray()
.endObject().string();
XContentParser parser = JsonXContent.jsonXContent.createParser(multilinesGeoJson);
parser.nextToken();
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
// test #4: "envelope" with empty coordinates
multilinesGeoJson = XContentFactory.jsonBuilder().startObject().field("type", "envelope")
.startArray("coordinates")
.endArray()
.endObject().string();
parser = JsonXContent.jsonXContent.createParser(multilinesGeoJson);
parser.nextToken();
ElasticsearchGeoAssertions.assertValidException(parser, ElasticsearchParseException.class);
}
@Test