Skip to content

Commit

Permalink
Add exceptions to GeoPointFieldMapper when parsing geo_point object
Browse files Browse the repository at this point in the history
* moved `geo_point` parsing to GeoUtils
* cleaned up `gzipped.json` for bulktest
* merged `GeoPointFieldMapper` and `GeoPoint` parsing methods

Closes #5390
  • Loading branch information
chilling committed Mar 19, 2014
1 parent 8055277 commit 743de0b
Show file tree
Hide file tree
Showing 15 changed files with 348 additions and 194 deletions.
104 changes: 0 additions & 104 deletions src/main/java/org/elasticsearch/common/geo/GeoPoint.java
Expand Up @@ -19,22 +19,12 @@

package org.elasticsearch.common.geo;

import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token;
import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper;

import java.io.IOException;

/**
*
*/
public final class GeoPoint {

public static final String LATITUDE = GeoPointFieldMapper.Names.LAT;
public static final String LONGITUDE = GeoPointFieldMapper.Names.LON;
public static final String GEOHASH = GeoPointFieldMapper.Names.GEOHASH;

private double lat;
private double lon;

Expand Down Expand Up @@ -150,98 +140,4 @@ public static GeoPoint parseFromLatLon(String latLon) {
point.resetFromString(latLon);
return point;
}

/**
* Parse a {@link GeoPoint} with a {@link XContentParser}:
*
* @param parser {@link XContentParser} to parse the value from
* @return new {@link GeoPoint} parsed from the parse
*
* @throws IOException
* @throws org.elasticsearch.ElasticsearchParseException
*/
public static GeoPoint parse(XContentParser parser) throws IOException, ElasticsearchParseException {
return parse(parser, new GeoPoint());
}

/**
* 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>
* </ul>
*
* @param parser {@link XContentParser} to parse the value from
* @param point A {@link GeoPoint} that will be reset by the values parsed
* @return new {@link GeoPoint} parsed from the parse
*
* @throws IOException
* @throws org.elasticsearch.ElasticsearchParseException
*/
public static GeoPoint parse(XContentParser parser, GeoPoint point) throws IOException, ElasticsearchParseException {
if(parser.currentToken() == Token.START_OBJECT) {
while(parser.nextToken() != Token.END_OBJECT) {
if(parser.currentToken() == Token.FIELD_NAME) {
String field = parser.text();
if(LATITUDE.equals(field)) {
if(parser.nextToken() == Token.VALUE_NUMBER) {
point.resetLat(parser.doubleValue());
} else {
throw new ElasticsearchParseException("latitude must be a number");
}
} else if (LONGITUDE.equals(field)) {
if(parser.nextToken() == Token.VALUE_NUMBER) {
point.resetLon(parser.doubleValue());
} else {
throw new ElasticsearchParseException("latitude must be a number");
}
} else if (GEOHASH.equals(field)) {
if(parser.nextToken() == Token.VALUE_STRING) {
point.resetFromGeoHash(parser.text());
} else {
throw new ElasticsearchParseException("geohash must be a string");
}
} else {
throw new ElasticsearchParseException("field must be either '" + LATITUDE + "', '" + LONGITUDE + "' or '" + GEOHASH + "'");
}
} else {
throw new ElasticsearchParseException("Token '"+parser.currentToken()+"' not allowed");
}
}
return point;
} else if(parser.currentToken() == Token.START_ARRAY) {
int element = 0;
while(parser.nextToken() != Token.END_ARRAY) {
if(parser.currentToken() == Token.VALUE_NUMBER) {
element++;
if(element == 1) {
point.resetLon(parser.doubleValue());
} else if(element == 2) {
point.resetLat(parser.doubleValue());
} else {
throw new ElasticsearchParseException("only two values allowed");
}
} else {
throw new ElasticsearchParseException("Numeric value expected");
}
}
return point;
} else if(parser.currentToken() == Token.VALUE_STRING) {
String data = parser.text();
int comma = data.indexOf(',');
if(comma > 0) {
double lat = Double.parseDouble(data.substring(0, comma).trim());
double lon = Double.parseDouble(data.substring(comma + 1).trim());
return point.reset(lat, lon);
} else {
point.resetFromGeoHash(data);
return point;
}
} else {
throw new ElasticsearchParseException("geo_point expected");
}
}
}
119 changes: 119 additions & 0 deletions src/main/java/org/elasticsearch/common/geo/GeoUtils.java
Expand Up @@ -22,12 +22,22 @@
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.apache.lucene.util.SloppyMath;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token;
import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper;

import java.io.IOException;

/**
*/
public class GeoUtils {

public static final String LATITUDE = GeoPointFieldMapper.Names.LAT;
public static final String LONGITUDE = GeoPointFieldMapper.Names.LON;
public static final String GEOHASH = GeoPointFieldMapper.Names.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 Down Expand Up @@ -293,5 +303,114 @@ private static double centeredModulus(double dividend, double divisor) {
}
return rtn;
}
/**
* Parse a {@link GeoPoint} with a {@link XContentParser}:
*
* @param parser {@link XContentParser} to parse the value from
* @return new {@link GeoPoint} parsed from the parse
*
* @throws IOException
* @throws org.elasticsearch.ElasticsearchParseException
*/
public static GeoPoint parseGeoPoint(XContentParser parser) throws IOException, ElasticsearchParseException {
return parseGeoPoint(parser, new GeoPoint());
}

/**
* 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>
* </ul>
*
* @param parser {@link XContentParser} to parse the value from
* @param point A {@link GeoPoint} that will be reset by the values parsed
* @return new {@link GeoPoint} parsed from the parse
*
* @throws IOException
* @throws org.elasticsearch.ElasticsearchParseException
*/
public static GeoPoint parseGeoPoint(XContentParser parser, GeoPoint point) throws IOException, ElasticsearchParseException {
double lat = Double.NaN;
double lon = Double.NaN;
String geohash = null;

if(parser.currentToken() == Token.START_OBJECT) {
while(parser.nextToken() != Token.END_OBJECT) {
if(parser.currentToken() == Token.FIELD_NAME) {
String field = parser.text();
if(LATITUDE.equals(field)) {
if(parser.nextToken() == Token.VALUE_NUMBER) {
lat = parser.doubleValue();
} else {
throw new ElasticsearchParseException("latitude must be a number");
}
} else if (LONGITUDE.equals(field)) {
if(parser.nextToken() == Token.VALUE_NUMBER) {
lon = parser.doubleValue();
} else {
throw new ElasticsearchParseException("latitude must be a number");
}
} else if (GEOHASH.equals(field)) {
if(parser.nextToken() == Token.VALUE_STRING) {
geohash = parser.text();
} else {
throw new ElasticsearchParseException("geohash must be a string");
}
} else {
throw new ElasticsearchParseException("field must be either '" + LATITUDE + "', '" + LONGITUDE + "' or '" + GEOHASH + "'");
}
} else {
throw new ElasticsearchParseException("Token '"+parser.currentToken()+"' not allowed");
}
}

if (geohash != null) {
if(!Double.isNaN(lat) || !Double.isNaN(lon)) {
throw new ElasticsearchParseException("field must be either lat/lon or geohash");
} else {
return point.resetFromGeoHash(geohash);
}
} else if (Double.isNaN(lat)) {
throw new ElasticsearchParseException("field [" + LATITUDE + "] missing");
} else if (Double.isNaN(lon)) {
throw new ElasticsearchParseException("field [" + LONGITUDE + "] missing");
} else {
return point.reset(lat, lon);
}

} else if(parser.currentToken() == Token.START_ARRAY) {
int element = 0;
while(parser.nextToken() != Token.END_ARRAY) {
if(parser.currentToken() == Token.VALUE_NUMBER) {
element++;
if(element == 1) {
lon = parser.doubleValue();
} else if(element == 2) {
lat = parser.doubleValue();
} else {
throw new ElasticsearchParseException("only two values allowed");
}
} else {
throw new ElasticsearchParseException("Numeric value expected");
}
}
return point.reset(lat, lon);
} else if(parser.currentToken() == Token.VALUE_STRING) {
String data = parser.text();
int comma = data.indexOf(',');
if(comma > 0) {
lat = Double.parseDouble(data.substring(0, comma).trim());
lon = Double.parseDouble(data.substring(comma + 1).trim());
return point.reset(lat, lon);
} else {
return point.resetFromGeoHash(data);
}
} else {
throw new ElasticsearchParseException("geo_point expected");
}
}
}
Expand Up @@ -57,9 +57,7 @@
import java.util.Map;

import static org.elasticsearch.index.mapper.MapperBuilders.*;
import static org.elasticsearch.index.mapper.core.TypeParsers.parseField;
import static org.elasticsearch.index.mapper.core.TypeParsers.parseMultiField;
import static org.elasticsearch.index.mapper.core.TypeParsers.parsePathType;
import static org.elasticsearch.index.mapper.core.TypeParsers.*;

/**
* Parsing: We handle:
Expand Down Expand Up @@ -488,24 +486,19 @@ public void parse(ParseContext context) throws IOException {
context.path().pathType(pathType);
context.path().add(name());

GeoPoint value = context.parseExternalValue(GeoPoint.class);
if (value != null) {
parseLatLon(context, value.lat(), value.lon());
GeoPoint sparse = context.parseExternalValue(GeoPoint.class);

if (sparse != null) {
parse(context, sparse, null);
} else {
sparse = new GeoPoint();
XContentParser.Token token = context.parser().currentToken();
if (token == XContentParser.Token.START_ARRAY) {
token = context.parser().nextToken();
if (token == XContentParser.Token.START_ARRAY) {
// its an array of array of lon/lat [ [1.2, 1.3], [1.4, 1.5] ]
while (token != XContentParser.Token.END_ARRAY) {
token = context.parser().nextToken();
double lon = context.parser().doubleValue();
token = context.parser().nextToken();
double lat = context.parser().doubleValue();
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) {

}
parseLatLon(context, lat, lon);
parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
token = context.parser().nextToken();
}
} else {
Expand All @@ -517,67 +510,29 @@ public void parse(ParseContext context) throws IOException {
while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) {

}
parseLatLon(context, lat, lon);
parse(context, sparse.reset(lat, lon), null);
} else {
while (token != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.START_OBJECT) {
parseObjectLatLon(context);
} else if (token == XContentParser.Token.VALUE_STRING) {
parseStringLatLon(context);
if (token == XContentParser.Token.VALUE_STRING) {
parsePointFromString(context, sparse, context.parser().text());
} else {
parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
}
token = context.parser().nextToken();
}
}
}
} else if (token == XContentParser.Token.START_OBJECT) {
parseObjectLatLon(context);
} else if (token == XContentParser.Token.VALUE_STRING) {
parseStringLatLon(context);
parsePointFromString(context, sparse, context.parser().text());
} else {
parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null);
}
}

context.path().remove();
context.path().pathType(origPathType);
}

private void parseStringLatLon(ParseContext context) throws IOException {
String value = context.parser().text();
int comma = value.indexOf(',');
if (comma != -1) {
double lat = Double.parseDouble(value.substring(0, comma).trim());
double lon = Double.parseDouble(value.substring(comma + 1).trim());
parseLatLon(context, lat, lon);
} else { // geo hash
parseGeohash(context, value);
}
}

private void parseObjectLatLon(ParseContext context) throws IOException {
XContentParser.Token token;
String currentName = context.parser().currentName();
Double lat = null;
Double lon = null;
String geohash = null;
while ((token = context.parser().nextToken()) != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.FIELD_NAME) {
currentName = context.parser().currentName();
} else if (token.isValue()) {
if (currentName.equals(Names.LAT)) {
lat = context.parser().doubleValue();
} else if (currentName.equals(Names.LON)) {
lon = context.parser().doubleValue();
} else if (currentName.equals(Names.GEOHASH)) {
geohash = context.parser().text();
}
}
}
if (geohash != null) {
parseGeohash(context, geohash);
} else if (lat != null && lon != null) {
parseLatLon(context, lat, lon);
}
}

private void parseGeohashField(ParseContext context, String geohash) throws IOException {
int len = Math.min(geoHashPrecision, geohash.length());
int min = enableGeohashPrefix ? 1 : geohash.length();
Expand All @@ -589,13 +544,12 @@ private void parseGeohashField(ParseContext context, String geohash) throws IOEx
}
}

private void parseLatLon(ParseContext context, double lat, double lon) throws IOException {
parse(context, new GeoPoint(lat, lon), null);
}

private void parseGeohash(ParseContext context, String geohash) throws IOException {
GeoPoint point = GeoHashUtils.decode(geohash);
parse(context, point, geohash);
private void parsePointFromString(ParseContext context, GeoPoint sparse, String point) throws IOException {
if (point.indexOf(',') < 0) {
parse(context, sparse.resetFromGeoHash(point), point);
} else {
parse(context, sparse.resetFromString(point), null);
}
}

private void parse(ParseContext context, GeoPoint point, String geohash) throws IOException {
Expand Down

0 comments on commit 743de0b

Please sign in to comment.