Skip to content

Commit

Permalink
Support of GeoJson Point for GeoPoint field
Browse files Browse the repository at this point in the history
  • Loading branch information
heemin32 committed Sep 26, 2022
1 parent bb47419 commit 70c61a3
Show file tree
Hide file tree
Showing 5 changed files with 520 additions and 121 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public GeoPoint resetFromString(String value, final boolean ignoreZValue, Effect
public GeoPoint resetFromCoordinates(String value, final boolean ignoreZValue) {
String[] vals = value.split(",");
if (vals.length > 3) {
throw new OpenSearchParseException("failed to parse [{}], expected 2 or 3 coordinates " + "but found: [{}]", vals.length);
throw new OpenSearchParseException("failed to parse [{}], expected 2 or 3 coordinates " + "but found: [{}]", vals.length, vals.length);
}
final double lat;
final double lon;
Expand Down
123 changes: 27 additions & 96 deletions server/src/main/java/org/opensearch/common/geo/GeoUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@
import org.opensearch.common.xcontent.LoggingDeprecationHandler;
import org.opensearch.common.xcontent.NamedXContentRegistry;
import org.opensearch.common.xcontent.XContentParser;
import org.opensearch.common.xcontent.XContentParser.Token;
import org.opensearch.common.xcontent.XContentSubParser;
import org.opensearch.common.xcontent.support.MapXContentParser;
import org.opensearch.common.xcontent.support.XContentMapValues;
import org.opensearch.geometry.Point;
import org.opensearch.index.fielddata.FieldData;
import org.opensearch.index.fielddata.GeoPointValues;
import org.opensearch.index.fielddata.MultiGeoPointValues;
Expand Down Expand Up @@ -74,6 +74,7 @@ public class GeoUtils {
public static final String LONGITUDE = "lon";
public static final String GEOHASH = "geohash";


/** Earth ellipsoid major axis defined by WGS 84 in meters */
public static final double EARTH_SEMI_MAJOR_AXIS = 6378137.0; // meters (WGS 84)

Expand All @@ -95,6 +96,12 @@ public class GeoUtils {
/** rounding error for quantized latitude and longitude values */
public static final double TOLERANCE = 1E-6;

public static final PointParser POINT_PARSER;

static {
POINT_PARSER = new PointParser(LONGITUDE, LATITUDE, true);
}

/** Returns true if latitude is actually a valid latitude value.*/
public static boolean isValidLatitude(double latitude) {
if (Double.isNaN(latitude) || Double.isInfinite(latitude) || latitude < GeoUtils.MIN_LAT || latitude > GeoUtils.MAX_LAT) {
Expand Down Expand Up @@ -444,113 +451,37 @@ public static GeoPoint parseGeoPoint(XContentParser parser, GeoPoint point, fina
* Parse a {@link GeoPoint} with a {@link XContentParser}. A geopoint has one of the following forms:
*
* <ul>
* <li>Object: <pre>{&quot;lat&quot;: <i>&lt;latitude&gt;</i>, &quot;lon&quot;: <i>&lt;longitude&gt;</i>}</pre></li>
* <li>String: <pre>&quot;<i>&lt;latitude&gt;</i>,<i>&lt;longitude&gt;</i>&quot;</pre></li>
* <li>Geohash: <pre>&quot;<i>&lt;geohash&gt;</i>&quot;</pre></li>
* <li>Array: <pre>[<i>&lt;longitude&gt;</i>,<i>&lt;latitude&gt;</i>]</pre></li>
* <li>Object: <pre>{@code {"lat": <latitude>, "lon": <longitude}}</pre></li>
* <li>String: <pre>{@code "<latitude>,<longitude>"}</pre></li>
* <li>GeoHash: <pre>{@code "<geohash>"}</pre></li>
* <li>WKT: <pre>{@code "POINT (<longitude> <latitude>)"}</pre></li>
* <li>Array: <pre>{@code [<longitude>, <latitude>]}</pre></li>
* <li>GeoJson: <pre>{@code {"type": "Point", "coordinates": [<longitude>, <latitude>]}}</pre><li>
* </ul>
*
*
* @param parser {@link XContentParser} to parse the value from
* @param point A {@link GeoPoint} that will be reset by the values parsed
* @param ignoreZValue tells to ignore z value or throw exception when there is a z value
* @param effectivePoint tells which point to use for GeoHash form
* @return new {@link GeoPoint} parsed from the parse
*/
public static GeoPoint parseGeoPoint(XContentParser parser, GeoPoint point, final boolean ignoreZValue, EffectivePoint effectivePoint)
throws IOException, OpenSearchParseException {
double lat = Double.NaN;
double lon = Double.NaN;
String geohash = null;
NumberFormatException numberFormatException = null;

if (parser.currentToken() == Token.START_OBJECT) {
try (XContentSubParser subParser = new XContentSubParser(parser)) {
while (subParser.nextToken() != Token.END_OBJECT) {
if (subParser.currentToken() == Token.FIELD_NAME) {
String field = subParser.currentName();
if (LATITUDE.equals(field)) {
subParser.nextToken();
switch (subParser.currentToken()) {
case VALUE_NUMBER:
case VALUE_STRING:
try {
lat = subParser.doubleValue(true);
} catch (NumberFormatException e) {
numberFormatException = e;
}
break;
default:
throw new OpenSearchParseException("latitude must be a number");
}
} else if (LONGITUDE.equals(field)) {
subParser.nextToken();
switch (subParser.currentToken()) {
case VALUE_NUMBER:
case VALUE_STRING:
try {
lon = subParser.doubleValue(true);
} catch (NumberFormatException e) {
numberFormatException = e;
}
break;
default:
throw new OpenSearchParseException("longitude must be a number");
}
} else if (GEOHASH.equals(field)) {
if (subParser.nextToken() == Token.VALUE_STRING) {
geohash = subParser.text();
} else {
throw new OpenSearchParseException("geohash must be a string");
}
} else {
throw new OpenSearchParseException("field must be either [{}], [{}] or [{}]", LATITUDE, LONGITUDE, GEOHASH);
}
} else {
throw new OpenSearchParseException("token [{}] not allowed", subParser.currentToken());
}
}
}
if (geohash != null) {
if (!Double.isNaN(lat) || !Double.isNaN(lon)) {
throw new OpenSearchParseException("field must be either lat/lon or geohash");
} else {
return point.parseGeoHash(geohash, effectivePoint);
}
} else if (numberFormatException != null) {
throw new OpenSearchParseException("[{}] and [{}] must be valid double values", numberFormatException, LATITUDE, LONGITUDE);
} else if (Double.isNaN(lat)) {
throw new OpenSearchParseException("field [{}] missing", LATITUDE);
} else if (Double.isNaN(lon)) {
throw new OpenSearchParseException("field [{}] missing", LONGITUDE);
} else {
return point.reset(lat, lon);
}

} else if (parser.currentToken() == Token.START_ARRAY) {
try (XContentSubParser subParser = new XContentSubParser(parser)) {
int element = 0;
while (subParser.nextToken() != Token.END_ARRAY) {
if (subParser.currentToken() == Token.VALUE_NUMBER) {
element++;
if (element == 1) {
lon = subParser.doubleValue();
} else if (element == 2) {
lat = subParser.doubleValue();
} else if (element == 3) {
GeoPoint.assertZValue(ignoreZValue, subParser.doubleValue());
} else {
throw new OpenSearchParseException("[geo_point] field type does not accept > 3 dimensions");
}
} else {
throw new OpenSearchParseException("numeric value expected");
}
}
}
return point.reset(lat, lon);
} else if (parser.currentToken() == Token.VALUE_STRING) {
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
Point p = POINT_PARSER.parseObject(parser, ignoreZValue, effectivePoint);
point.reset(p.getLat(), p.getLon());
} else if (parser.currentToken() == XContentParser.Token.START_ARRAY ||
parser.currentToken() == XContentParser.Token.VALUE_NUMBER) {
Point p = POINT_PARSER.parseFromArray(parser, ignoreZValue, "geo_point");
point.reset(p.getLat(), p.getLon());
} else if (parser.currentToken() == XContentParser.Token.VALUE_STRING) {
String val = parser.text();
return point.resetFromString(val, ignoreZValue, effectivePoint);
point.resetFromString(val, ignoreZValue, effectivePoint);
} else {
throw new OpenSearchParseException("geo_point expected");
}
return point;
}

/**
Expand Down
Loading

0 comments on commit 70c61a3

Please sign in to comment.