|
@@ -21,7 +21,6 @@ package org.elasticsearch.index.query;
|
|
|
|
|
|
import org.apache.lucene.document.LatLonDocValuesField;
|
|
|
import org.apache.lucene.document.LatLonPoint;
|
|
|
-//import org.apache.lucene.geo.Rectangle;
|
|
|
import org.apache.lucene.search.IndexOrDocValuesQuery;
|
|
|
import org.apache.lucene.search.MatchNoDocsQuery;
|
|
|
import org.apache.lucene.search.Query;
|
|
@@ -29,11 +28,9 @@ import org.elasticsearch.ElasticsearchParseException;
|
|
|
import org.elasticsearch.common.Numbers;
|
|
|
import org.elasticsearch.common.ParseField;
|
|
|
import org.elasticsearch.common.ParsingException;
|
|
|
+import org.elasticsearch.common.geo.GeoBoundingBox;
|
|
|
import org.elasticsearch.common.geo.GeoPoint;
|
|
|
-import org.elasticsearch.common.geo.GeoShapeType;
|
|
|
import org.elasticsearch.common.geo.GeoUtils;
|
|
|
-import org.elasticsearch.common.geo.builders.EnvelopeBuilder;
|
|
|
-import org.elasticsearch.common.geo.parsers.GeoWKTParser;
|
|
|
import org.elasticsearch.common.io.stream.StreamInput;
|
|
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
@@ -66,24 +63,12 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
|
|
|
private static final ParseField TYPE_FIELD = new ParseField("type");
|
|
|
private static final ParseField VALIDATION_METHOD_FIELD = new ParseField("validation_method");
|
|
|
- private static final ParseField TOP_FIELD = new ParseField("top");
|
|
|
- private static final ParseField BOTTOM_FIELD = new ParseField("bottom");
|
|
|
- private static final ParseField LEFT_FIELD = new ParseField("left");
|
|
|
- private static final ParseField RIGHT_FIELD = new ParseField("right");
|
|
|
- private static final ParseField TOP_LEFT_FIELD = new ParseField("top_left");
|
|
|
- private static final ParseField BOTTOM_RIGHT_FIELD = new ParseField("bottom_right");
|
|
|
- private static final ParseField TOP_RIGHT_FIELD = new ParseField("top_right");
|
|
|
- private static final ParseField BOTTOM_LEFT_FIELD = new ParseField("bottom_left");
|
|
|
private static final ParseField IGNORE_UNMAPPED_FIELD = new ParseField("ignore_unmapped");
|
|
|
- private static final ParseField WKT_FIELD = new ParseField("wkt");
|
|
|
|
|
|
|
|
|
/** Name of field holding geo coordinates to compute the bounding box on.*/
|
|
|
private final String fieldName;
|
|
|
- /** Top left corner coordinates of bounding box. */
|
|
|
- private GeoPoint topLeft = new GeoPoint(Double.NaN, Double.NaN);
|
|
|
- /** Bottom right corner coordinates of bounding box.*/
|
|
|
- private GeoPoint bottomRight = new GeoPoint(Double.NaN, Double.NaN);
|
|
|
+ private GeoBoundingBox geoBoundingBox = new GeoBoundingBox(new GeoPoint(Double.NaN, Double.NaN), new GeoPoint(Double.NaN, Double.NaN));
|
|
|
/** How to deal with incorrect coordinates.*/
|
|
|
private GeoValidationMethod validationMethod = GeoValidationMethod.DEFAULT;
|
|
|
/** How the query should be run. */
|
|
@@ -108,8 +93,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
public GeoBoundingBoxQueryBuilder(StreamInput in) throws IOException {
|
|
|
super(in);
|
|
|
fieldName = in.readString();
|
|
|
- topLeft = in.readGeoPoint();
|
|
|
- bottomRight = in.readGeoPoint();
|
|
|
+ geoBoundingBox = new GeoBoundingBox(in);
|
|
|
type = GeoExecType.readFromStream(in);
|
|
|
validationMethod = GeoValidationMethod.readFromStream(in);
|
|
|
ignoreUnmapped = in.readBoolean();
|
|
@@ -118,8 +102,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
@Override
|
|
|
protected void doWriteTo(StreamOutput out) throws IOException {
|
|
|
out.writeString(fieldName);
|
|
|
- out.writeGeoPoint(topLeft);
|
|
|
- out.writeGeoPoint(bottomRight);
|
|
|
+ geoBoundingBox.writeTo(out);
|
|
|
type.writeTo(out);
|
|
|
validationMethod.writeTo(out);
|
|
|
out.writeBoolean(ignoreUnmapped);
|
|
@@ -162,8 +145,8 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
// we do not check longitudes as the query generation code can deal with flipped left/right values
|
|
|
}
|
|
|
|
|
|
- topLeft.reset(top, left);
|
|
|
- bottomRight.reset(bottom, right);
|
|
|
+ geoBoundingBox.topLeft().reset(top, left);
|
|
|
+ geoBoundingBox.bottomRight().reset(bottom, right);
|
|
|
return this;
|
|
|
}
|
|
|
|
|
@@ -197,12 +180,12 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
|
|
|
/** Returns the top left corner of the bounding box. */
|
|
|
public GeoPoint topLeft() {
|
|
|
- return topLeft;
|
|
|
+ return geoBoundingBox.topLeft();
|
|
|
}
|
|
|
|
|
|
/** Returns the bottom right corner of the bounding box. */
|
|
|
public GeoPoint bottomRight() {
|
|
|
- return bottomRight;
|
|
|
+ return geoBoundingBox.bottomRight();
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -295,6 +278,9 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ GeoPoint topLeft = geoBoundingBox.topLeft();
|
|
|
+ GeoPoint bottomRight = geoBoundingBox.bottomRight();
|
|
|
+
|
|
|
QueryValidationException validationException = null;
|
|
|
// For everything post 2.0 validate latitude and longitude unless validation was explicitly turned off
|
|
|
if (GeoUtils.isValidLatitude(topLeft.getLat()) == false) {
|
|
@@ -335,8 +321,8 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
throw new QueryShardException(context, "couldn't validate latitude/ longitude values", exception);
|
|
|
}
|
|
|
|
|
|
- GeoPoint luceneTopLeft = new GeoPoint(topLeft);
|
|
|
- GeoPoint luceneBottomRight = new GeoPoint(bottomRight);
|
|
|
+ GeoPoint luceneTopLeft = new GeoPoint(geoBoundingBox.topLeft());
|
|
|
+ GeoPoint luceneBottomRight = new GeoPoint(geoBoundingBox.bottomRight());
|
|
|
if (GeoValidationMethod.isCoerce(validationMethod)) {
|
|
|
// Special case: if the difference between the left and right is 360 and the right is greater than the left, we are asking for
|
|
|
// the complete longitude range so need to set longitude to the complete longitude range
|
|
@@ -368,8 +354,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
builder.startObject(NAME);
|
|
|
|
|
|
builder.startObject(fieldName);
|
|
|
- builder.array(TOP_LEFT_FIELD.getPreferredName(), topLeft.getLon(), topLeft.getLat());
|
|
|
- builder.array(BOTTOM_RIGHT_FIELD.getPreferredName(), bottomRight.getLon(), bottomRight.getLat());
|
|
|
+ geoBoundingBox.toXContentFragment(builder, false);
|
|
|
builder.endObject();
|
|
|
builder.field(VALIDATION_METHOD_FIELD.getPreferredName(), validationMethod);
|
|
|
builder.field(TYPE_FIELD.getPreferredName(), type);
|
|
@@ -391,7 +376,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
boolean ignoreUnmapped = DEFAULT_IGNORE_UNMAPPED;
|
|
|
|
|
|
// bottom (minLat), top (maxLat), left (minLon), right (maxLon)
|
|
|
- double[] bbox = null;
|
|
|
+ GeoBoundingBox bbox = null;
|
|
|
String type = "memory";
|
|
|
|
|
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
|
@@ -399,7 +384,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
currentFieldName = parser.currentName();
|
|
|
} else if (token == XContentParser.Token.START_OBJECT) {
|
|
|
try {
|
|
|
- bbox = parseBoundingBox(parser);
|
|
|
+ bbox = GeoBoundingBox.parseBoundingBox(parser);
|
|
|
fieldName = currentFieldName;
|
|
|
} catch (Exception e) {
|
|
|
throw new ElasticsearchParseException("failed to parse [{}] query. [{}]", NAME, e.getMessage());
|
|
@@ -426,11 +411,8 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
throw new ElasticsearchParseException("failed to parse [{}] query. bounding box not provided", NAME);
|
|
|
}
|
|
|
|
|
|
- final GeoPoint topLeft = new GeoPoint(bbox[1], bbox[2]);
|
|
|
- final GeoPoint bottomRight = new GeoPoint(bbox[0], bbox[3]);
|
|
|
-
|
|
|
GeoBoundingBoxQueryBuilder builder = new GeoBoundingBoxQueryBuilder(fieldName);
|
|
|
- builder.setCorners(topLeft, bottomRight);
|
|
|
+ builder.setCorners(bbox.topLeft(), bbox.bottomRight());
|
|
|
builder.queryName(queryName);
|
|
|
builder.boost(boost);
|
|
|
builder.type(GeoExecType.fromString(type));
|
|
@@ -444,8 +426,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
|
|
|
@Override
|
|
|
protected boolean doEquals(GeoBoundingBoxQueryBuilder other) {
|
|
|
- return Objects.equals(topLeft, other.topLeft) &&
|
|
|
- Objects.equals(bottomRight, other.bottomRight) &&
|
|
|
+ return Objects.equals(geoBoundingBox, other.geoBoundingBox) &&
|
|
|
Objects.equals(type, other.type) &&
|
|
|
Objects.equals(validationMethod, other.validationMethod) &&
|
|
|
Objects.equals(fieldName, other.fieldName) &&
|
|
@@ -454,7 +435,7 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
|
|
|
@Override
|
|
|
protected int doHashCode() {
|
|
|
- return Objects.hash(topLeft, bottomRight, type, validationMethod, fieldName, ignoreUnmapped);
|
|
|
+ return Objects.hash(geoBoundingBox, type, validationMethod, fieldName, ignoreUnmapped);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -462,72 +443,4 @@ public class GeoBoundingBoxQueryBuilder extends AbstractQueryBuilder<GeoBounding
|
|
|
return NAME;
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * Parses the bounding box and returns bottom, top, left, right coordinates
|
|
|
- */
|
|
|
- public static double[] parseBoundingBox(XContentParser parser) throws IOException, ElasticsearchParseException {
|
|
|
- XContentParser.Token token = parser.currentToken();
|
|
|
- if (token != XContentParser.Token.START_OBJECT) {
|
|
|
- throw new ElasticsearchParseException("failed to parse bounding box. Expected start object but found [{}]", token);
|
|
|
- }
|
|
|
-
|
|
|
- double top = Double.NaN;
|
|
|
- double bottom = Double.NaN;
|
|
|
- double left = Double.NaN;
|
|
|
- double right = Double.NaN;
|
|
|
-
|
|
|
- String currentFieldName;
|
|
|
- GeoPoint sparse = new GeoPoint();
|
|
|
- EnvelopeBuilder envelope = null;
|
|
|
-
|
|
|
- while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
|
|
- if (token == XContentParser.Token.FIELD_NAME) {
|
|
|
- currentFieldName = parser.currentName();
|
|
|
- token = parser.nextToken();
|
|
|
- if (WKT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- envelope = (EnvelopeBuilder)(GeoWKTParser.parseExpectedType(parser, GeoShapeType.ENVELOPE));
|
|
|
- } else if (TOP_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- top = parser.doubleValue();
|
|
|
- } else if (BOTTOM_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- bottom = parser.doubleValue();
|
|
|
- } else if (LEFT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- left = parser.doubleValue();
|
|
|
- } else if (RIGHT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- right = parser.doubleValue();
|
|
|
- } else {
|
|
|
- if (TOP_LEFT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- GeoUtils.parseGeoPoint(parser, sparse, false, GeoUtils.EffectivePoint.TOP_LEFT);
|
|
|
- top = sparse.getLat();
|
|
|
- left = sparse.getLon();
|
|
|
- } else if (BOTTOM_RIGHT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- GeoUtils.parseGeoPoint(parser, sparse, false, GeoUtils.EffectivePoint.BOTTOM_RIGHT);
|
|
|
- bottom = sparse.getLat();
|
|
|
- right = sparse.getLon();
|
|
|
- } else if (TOP_RIGHT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- GeoUtils.parseGeoPoint(parser, sparse, false, GeoUtils.EffectivePoint.TOP_RIGHT);
|
|
|
- top = sparse.getLat();
|
|
|
- right = sparse.getLon();
|
|
|
- } else if (BOTTOM_LEFT_FIELD.match(currentFieldName, parser.getDeprecationHandler())) {
|
|
|
- GeoUtils.parseGeoPoint(parser, sparse, false, GeoUtils.EffectivePoint.BOTTOM_LEFT);
|
|
|
- bottom = sparse.getLat();
|
|
|
- left = sparse.getLon();
|
|
|
- } else {
|
|
|
- throw new ElasticsearchParseException("failed to parse bounding box. unexpected field [{}]", currentFieldName);
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- throw new ElasticsearchParseException("failed to parse bounding box. field name expected but [{}] found", token);
|
|
|
- }
|
|
|
- }
|
|
|
- if (envelope != null) {
|
|
|
- if (Double.isNaN(top) == false || Double.isNaN(bottom) == false || Double.isNaN(left) == false ||
|
|
|
- Double.isNaN(right) == false) {
|
|
|
- throw new ElasticsearchParseException("failed to parse bounding box. Conflicting definition found "
|
|
|
- + "using well-known text and explicit corners.");
|
|
|
- }
|
|
|
- org.locationtech.spatial4j.shape.Rectangle r = envelope.buildS4J();
|
|
|
- return new double[]{r.getMinY(), r.getMaxY(), r.getMinX(), r.getMaxX()};
|
|
|
- }
|
|
|
- return new double[]{bottom, top, left, right};
|
|
|
- }
|
|
|
}
|