Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support WKT point conversion to geo_point type #44107

Merged
merged 10 commits into from
Jul 12, 2019
13 changes: 10 additions & 3 deletions docs/reference/mapping/types/geo-point.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Fields of type `geo_point` accept latitude-longitude pairs, which can be used:
* to integrate distance into a document's <<query-dsl-function-score-query,relevance score>>.
* to <<geo-sorting,sort>> documents by distance.

There are four ways that a geo-point may be specified, as demonstrated below:
There are five ways that a geo-point may be specified, as demonstrated below:

[source,js]
--------------------------------------------------
Expand Down Expand Up @@ -53,10 +53,16 @@ PUT my_index/_doc/4
"location": [ -71.34, 41.12 ] <4>
}

PUT my_index/_doc/5
{
"text": "Geo-point as a WKT POINT primitive",
"location" : "POINT (-77.03653 38.897676)" <5>
Hohol marked this conversation as resolved.
Show resolved Hide resolved
}

GET my_index/_search
{
"query": {
"geo_bounding_box": { <5>
"geo_bounding_box": { <6>
"location": {
"top_left": {
"lat": 42,
Expand All @@ -76,7 +82,8 @@ GET my_index/_search
<2> Geo-point expressed as a string with the format: `"lat,lon"`.
<3> Geo-point expressed as a geohash.
<4> Geo-point expressed as an array with the format: [ `lon`, `lat`]
<5> A geo-bounding box query which finds all geo-points that fall inside the box.
<5> Geo-point expressed as a Well-known text POINT with the format: `"POINT(lon lat)"`
Hohol marked this conversation as resolved.
Show resolved Hide resolved
<6> A geo-bounding box query which finds all geo-points that fall inside the box.

[IMPORTANT]
.Geo-points expressed as an array or string
Expand Down
12 changes: 12 additions & 0 deletions server/src/main/java/org/elasticsearch/common/geo/GeoPoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.util.BitUtil;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.geo.builders.PointBuilder;
import org.elasticsearch.common.geo.parsers.GeoWKTParser;
import org.elasticsearch.common.xcontent.ToXContentFragment;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.ElasticsearchParseException;
Expand Down Expand Up @@ -85,6 +87,8 @@ public GeoPoint resetFromString(String value) {
public GeoPoint resetFromString(String value, final boolean ignoreZValue) {
if (value.contains(",")) {
return resetFromCoordinates(value, ignoreZValue);
} else if (value.contains("POINT")) {
Hohol marked this conversation as resolved.
Show resolved Hide resolved
return resetFromWKT(value, ignoreZValue);
}
return resetFromGeoHash(value);
}
Expand Down Expand Up @@ -114,6 +118,14 @@ public GeoPoint resetFromCoordinates(String value, final boolean ignoreZValue) {
return reset(lat, lon);
}

private GeoPoint resetFromWKT(String value, boolean ignoreZValue) {
try {
PointBuilder point = (PointBuilder)GeoWKTParser.parseExpectedType(value, GeoShapeType.POINT, ignoreZValue, false);
Hohol marked this conversation as resolved.
Show resolved Hide resolved
return reset(point.latitude(), point.longitude());
} catch (IOException e) {
throw new ElasticsearchParseException("Invalid WKT format", e);
}
}

public GeoPoint resetFromIndexHash(long hash) {
lon = Geohash.decodeLongitude(hash);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,10 @@ public static GeoPoint parseGeoPoint(XContentParser parser, GeoPoint point, fina
lon = subParser.doubleValue();
} else if (element == 2) {
lat = subParser.doubleValue();
} else {
} else if (element == 3) {
GeoPoint.assertZValue(ignoreZValue, subParser.doubleValue());
} else {
throw new ElasticsearchParseException("[geo_point] field type does not accept > 3 dimensions");
}
} else {
throw new ElasticsearchParseException("numeric value expected");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package org.elasticsearch.common.geo.parsers;

import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Explicit;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoShapeType;
import org.elasticsearch.common.geo.builders.CoordinatesBuilder;
Expand Down Expand Up @@ -77,10 +76,15 @@ public static ShapeBuilder parseExpectedType(XContentParser parser, final GeoSha
public static ShapeBuilder parseExpectedType(XContentParser parser, final GeoShapeType shapeType,
final BaseGeoShapeFieldMapper shapeMapper)
throws IOException, ElasticsearchParseException {
try (StringReader reader = new StringReader(parser.text())) {
Explicit<Boolean> ignoreZValue = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE :
shapeMapper.ignoreZValue();
Explicit<Boolean> coerce = (shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce();
String text = parser.text();
Hohol marked this conversation as resolved.
Show resolved Hide resolved
boolean ignoreZValue = ((shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.IGNORE_Z_VALUE :
shapeMapper.ignoreZValue()).value();
boolean coerce = ((shapeMapper == null) ? BaseGeoShapeFieldMapper.Defaults.COERCE : shapeMapper.coerce()).value();
return parseExpectedType(text, shapeType, ignoreZValue, coerce);
}

public static ShapeBuilder parseExpectedType(String text, GeoShapeType shapeType, boolean ignoreZValue, boolean coerce) throws IOException {
try (StringReader reader = new StringReader(text)) {
// setup the tokenizer; configured to read words w/o numbers
StreamTokenizer tokenizer = new StreamTokenizer(reader);
tokenizer.resetSyntax();
Expand All @@ -93,7 +97,7 @@ public static ShapeBuilder parseExpectedType(XContentParser parser, final GeoSha
tokenizer.wordChars('.', '.');
tokenizer.whitespaceChars(0, ' ');
tokenizer.commentChar('#');
ShapeBuilder builder = parseGeometry(tokenizer, shapeType, ignoreZValue.value(), coerce.value());
ShapeBuilder builder = parseGeometry(tokenizer, shapeType, ignoreZValue, coerce);
checkEOF(tokenizer);
return builder;
}
Expand All @@ -111,7 +115,7 @@ private static ShapeBuilder parseGeometry(StreamTokenizer stream, GeoShapeType s
}
switch (type) {
case POINT:
return parsePoint(stream, ignoreZValue, coerce);
return parsePoint(stream, ignoreZValue);
case MULTIPOINT:
return parseMultiPoint(stream, ignoreZValue, coerce);
case LINESTRING:
Expand Down Expand Up @@ -146,7 +150,7 @@ private static EnvelopeBuilder parseBBox(StreamTokenizer stream) throws IOExcept
return new EnvelopeBuilder(new Coordinate(minLon, maxLat), new Coordinate(maxLon, minLat));
}

private static PointBuilder parsePoint(StreamTokenizer stream, final boolean ignoreZValue, final boolean coerce)
public static PointBuilder parsePoint(StreamTokenizer stream, final boolean ignoreZValue)
throws IOException, ElasticsearchParseException {
if (nextEmptyOrOpen(stream).equals(EMPTY)) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -301,34 +301,25 @@ public void parse(ParseContext context) throws IOException {
XContentParser.Token token = context.parser().currentToken();
if (token == XContentParser.Token.START_ARRAY) {
token = context.parser().nextToken();
if (token == XContentParser.Token.START_ARRAY) {
Hohol marked this conversation as resolved.
Show resolved Hide resolved
// its an array of array of lon/lat [ [1.2, 1.3], [1.4, 1.5] ]
while (token != XContentParser.Token.END_ARRAY) {
parseGeoPointIgnoringMalformed(context, sparse);
token = context.parser().nextToken();
if (token == XContentParser.Token.VALUE_NUMBER) {
double lon = context.parser().doubleValue();
context.parser().nextToken();
double lat = context.parser().doubleValue();
token = context.parser().nextToken();
if (token == XContentParser.Token.VALUE_NUMBER) {
GeoPoint.assertZValue(ignoreZValue.value(), context.parser().doubleValue());
} else if (token != XContentParser.Token.END_ARRAY) {
throw new ElasticsearchParseException("[{}] field type does not accept > 3 dimensions", CONTENT_TYPE);
}
parse(context, sparse.reset(lat, lon));
} else {
// its an array of other possible values
if (token == XContentParser.Token.VALUE_NUMBER) {
double lon = context.parser().doubleValue();
context.parser().nextToken();
double lat = context.parser().doubleValue();
token = context.parser().nextToken();
if (token == XContentParser.Token.VALUE_NUMBER) {
GeoPoint.assertZValue(ignoreZValue.value(), context.parser().doubleValue());
} else if (token != XContentParser.Token.END_ARRAY) {
throw new ElasticsearchParseException("[{}] field type does not accept > 3 dimensions", CONTENT_TYPE);
}
parse(context, sparse.reset(lat, lon));
} else {
while (token != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
parseGeoPointStringIgnoringMalformed(context, sparse);
} else {
parseGeoPointIgnoringMalformed(context, sparse);
}
token = context.parser().nextToken();
while (token != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.VALUE_STRING) {
parseGeoPointStringIgnoringMalformed(context, sparse);
} else {
parseGeoPointIgnoringMalformed(context, sparse);
}
token = context.parser().nextToken();
}
}
} else if (token == XContentParser.Token.VALUE_STRING) {
Expand All @@ -353,7 +344,7 @@ public void parse(ParseContext context) throws IOException {
*/
private void parseGeoPointIgnoringMalformed(ParseContext context, GeoPoint sparse) throws IOException {
try {
parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse));
parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse, ignoreZValue.value()));
} catch (ElasticsearchParseException e) {
if (ignoreMalformed.value() == false) {
throw e;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public void testGeoPointReset() throws IOException {
assertPointsEqual(point.reset(0, 0), point2.reset(0, 0));
assertPointsEqual(point.resetFromString(Double.toString(lat) + ", " + Double.toHexString(lon)), point2.reset(lat, lon));
assertPointsEqual(point.reset(0, 0), point2.reset(0, 0));
assertPointsEqual(point.resetFromString("POINT(" + lon + " " + lat + ")"), point2.reset(lat, lon));
Hohol marked this conversation as resolved.
Show resolved Hide resolved
}

public void testEqualsHashCodeContract() {
Expand Down