/** * Normalize the geo {@code Point} for the given coordinates to lie within their respective * normalized ranges. * * <p>You can control which coordinate gets normalized with the two flags. * * <p>Note: A shift of 180° is applied in the longitude if necessary, in order to normalize * properly the latitude. If normalizing latitude but not longitude, it is assumed that the * longitude is in the form x+k*360, with x in ]-180;180], and k is meaningful to the application. * Therefore x will be adjusted while keeping k preserved. * * @param point The point to normalize in-place. * @param normLat Whether to normalize latitude or leave it as is. * @param normLon Whether to normalize longitude. */ public static void normalizePoint(GeoPoint point, boolean normLat, boolean normLon) { double lat = point.lat(); double lon = point.lon(); normLat = normLat && (lat > 90 || lat <= -90); normLon = normLon && (lon > 180 || lon <= -180); if (normLat) { lat = centeredModulus(lat, 360); boolean shift = true; if (lat < -90) { lat = -180 - lat; } else if (lat > 90) { lat = 180 - lat; } else { // No need to shift the longitude, and the latitude is normalized shift = false; } if (shift) { if (normLon) { lon += 180; } else { // Longitude won't be normalized, // keep it in the form x+k*360 (with x in ]-180;180]) // by only changing x, assuming k is meaningful for the user application. lon += normalizeLon(lon) > 0 ? -180 : 180; } } } if (normLon) { lon = centeredModulus(lon, 360); } point.reset(lat, lon); }
private void assertGeoPointQuery(GeoDistanceRangeQueryBuilder queryBuilder, Query query) throws IOException { assertThat(query, instanceOf(XGeoPointDistanceRangeQuery.class)); XGeoPointDistanceRangeQuery geoQuery = (XGeoPointDistanceRangeQuery) query; assertThat(geoQuery.getField(), equalTo(queryBuilder.fieldName())); if (queryBuilder.point() != null) { GeoPoint expectedPoint = new GeoPoint(queryBuilder.point()); GeoUtils.normalizePoint(expectedPoint); assertThat(geoQuery.getCenterLat(), equalTo(expectedPoint.lat())); assertThat(geoQuery.getCenterLon(), equalTo(expectedPoint.lon())); } if (queryBuilder.from() != null && queryBuilder.from() instanceof Number) { double fromValue = ((Number) queryBuilder.from()).doubleValue(); if (queryBuilder.unit() != null) { fromValue = queryBuilder.unit().toMeters(fromValue); } assertThat(geoQuery.getMinRadiusMeters(), closeTo(fromValue, 1E-5)); } if (queryBuilder.to() != null && queryBuilder.to() instanceof Number) { double toValue = ((Number) queryBuilder.to()).doubleValue(); if (queryBuilder.unit() != null) { toValue = queryBuilder.unit().toMeters(toValue); } assertThat(geoQuery.getMaxRadiusMeters(), closeTo(toValue, 1E-5)); } }
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); } }
/** parse a {@link GeoPoint} from a String */ public static GeoPoint parseGeoPoint(String data, GeoPoint point) { 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 { return point.resetFromGeoHash(data); } }
public void testSortModeSumIsRejectedInSetter() { GeoDistanceSortBuilder builder = new GeoDistanceSortBuilder("testname", -1, -1); GeoPoint point = RandomGeoGenerator.randomPoint(random()); builder.point(point.getLat(), point.getLon()); try { builder.sortMode(SortMode.SUM); fail("sort mode sum should not be supported"); } catch (IllegalArgumentException e) { // all good } }
private static boolean contains(GeoPoint point, List<GeoPoint> set, Distance precision) { for (GeoPoint r : set) { final double distance = GeoDistance.PLANE.calculate( point.getLat(), point.getLon(), r.getLat(), r.getLon(), DistanceUnit.METERS); if (new Distance(distance, DistanceUnit.METERS).compareTo(precision) <= 0) { return true; } } return false; }
@Override public BytesRef binaryValue() { final byte[] bytes = new byte[points.size() * 16]; int off = 0; for (Iterator<ObjectCursor<GeoPoint>> it = points.iterator(); it.hasNext(); ) { final GeoPoint point = it.next().value; ByteUtils.writeDoubleLE(point.getLat(), bytes, off); ByteUtils.writeDoubleLE(point.getLon(), bytes, off + 8); off += 16; } return new BytesRef(bytes); }
@Override protected boolean matchDoc(int doc) { values.setDocument(doc); final int length = values.count(); for (int i = 0; i < length; i++) { GeoPoint point = values.valueAt(i); if (pointInPolygon(points, point.lat(), point.lon())) { return true; } } return false; }
public static GeoDistanceSortBuilder randomGeoDistanceSortBuilder() { String fieldName = randomAsciiOfLengthBetween(1, 10); GeoDistanceSortBuilder result = null; int id = randomIntBetween(0, 2); switch (id) { case 0: int count = randomIntBetween(1, 10); String[] geohashes = new String[count]; for (int i = 0; i < count; i++) { geohashes[i] = RandomGeoGenerator.randomPoint(random()).geohash(); } result = new GeoDistanceSortBuilder(fieldName, geohashes); break; case 1: GeoPoint pt = RandomGeoGenerator.randomPoint(random()); result = new GeoDistanceSortBuilder(fieldName, pt.getLat(), pt.getLon()); break; case 2: result = new GeoDistanceSortBuilder(fieldName, points(new GeoPoint[0])); break; default: throw new IllegalStateException("one of three geo initialisation strategies must be used"); } if (randomBoolean()) { result.geoDistance(geoDistance(result.geoDistance())); } if (randomBoolean()) { result.unit(randomValueOtherThan(result.unit(), () -> randomFrom(DistanceUnit.values()))); } if (randomBoolean()) { result.order(randomFrom(SortOrder.values())); } if (randomBoolean()) { result.sortMode(randomValueOtherThan(SortMode.SUM, () -> randomFrom(SortMode.values()))); } if (randomBoolean()) { result.setNestedFilter(randomNestedFilter()); } if (randomBoolean()) { result.setNestedPath( randomValueOtherThan(result.getNestedPath(), () -> randomAsciiOfLengthBetween(1, 10))); } if (randomBoolean()) { result.validation( randomValueOtherThan( result.validation(), () -> randomFrom(GeoValidationMethod.values()))); } return result; }
@Override public Mapper parse(ParseContext context) throws IOException { ContentPath.Type origPathType = context.path().pathType(); context.path().pathType(pathType); context.path().add(simpleName()); 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) { parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null); token = context.parser().nextToken(); } } else { // its an array of other possible values if (token == XContentParser.Token.VALUE_NUMBER) { double lon = context.parser().doubleValue(); token = context.parser().nextToken(); double lat = context.parser().doubleValue(); while ((token = context.parser().nextToken()) != XContentParser.Token.END_ARRAY) ; parse(context, sparse.reset(lat, lon), null); } else { while (token != XContentParser.Token.END_ARRAY) { 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.VALUE_STRING) { parsePointFromString(context, sparse, context.parser().text()); } else if (token != XContentParser.Token.VALUE_NULL) { parse(context, GeoUtils.parseGeoPoint(context.parser(), sparse), null); } } context.path().remove(); context.path().pathType(origPathType); return null; }
private void assertLegacyQuery(GeoDistanceRangeQueryBuilder queryBuilder, Query query) throws IOException { assertThat(query, instanceOf(GeoDistanceRangeQuery.class)); GeoDistanceRangeQuery geoQuery = (GeoDistanceRangeQuery) query; assertThat(geoQuery.fieldName(), equalTo(queryBuilder.fieldName())); if (queryBuilder.point() != null) { GeoPoint expectedPoint = new GeoPoint(queryBuilder.point()); if (GeoValidationMethod.isCoerce(queryBuilder.getValidationMethod())) { GeoUtils.normalizePoint(expectedPoint, true, true); } assertThat(geoQuery.lat(), equalTo(expectedPoint.lat())); assertThat(geoQuery.lon(), equalTo(expectedPoint.lon())); } assertThat(geoQuery.geoDistance(), equalTo(queryBuilder.geoDistance())); if (queryBuilder.from() != null && queryBuilder.from() instanceof Number) { double fromValue = ((Number) queryBuilder.from()).doubleValue(); if (queryBuilder.unit() != null) { fromValue = queryBuilder.unit().toMeters(fromValue); } if (queryBuilder.geoDistance() != null) { fromValue = queryBuilder.geoDistance().normalize(fromValue, DistanceUnit.DEFAULT); } double fromSlop = Math.abs(fromValue) / 1000; if (queryBuilder.includeLower() == false) { fromSlop = NumericUtils.sortableLongToDouble( (NumericUtils.doubleToSortableLong(Math.abs(fromValue)) + 1L)) / 1000.0; } assertThat(geoQuery.minInclusiveDistance(), closeTo(fromValue, fromSlop)); } if (queryBuilder.to() != null && queryBuilder.to() instanceof Number) { double toValue = ((Number) queryBuilder.to()).doubleValue(); if (queryBuilder.unit() != null) { toValue = queryBuilder.unit().toMeters(toValue); } if (queryBuilder.geoDistance() != null) { toValue = queryBuilder.geoDistance().normalize(toValue, DistanceUnit.DEFAULT); } double toSlop = Math.abs(toValue) / 1000; if (queryBuilder.includeUpper() == false) { toSlop = NumericUtils.sortableLongToDouble( (NumericUtils.doubleToSortableLong(Math.abs(toValue)) - 1L)) / 1000.0; } assertThat(geoQuery.maxInclusiveDistance(), closeTo(toValue, toSlop)); } }
@Override public GeoPoint value(Object value) { if (value instanceof GeoPoint) { return (GeoPoint) value; } else { return GeoPoint.parseFromLatLon(value.toString()); } }
@Override protected GeoDistanceSortBuilder mutate(GeoDistanceSortBuilder original) throws IOException { GeoDistanceSortBuilder result = new GeoDistanceSortBuilder(original); int parameter = randomIntBetween(0, 8); switch (parameter) { case 0: while (Arrays.deepEquals(original.points(), result.points())) { GeoPoint pt = RandomGeoGenerator.randomPoint(random()); result.point(pt.getLat(), pt.getLon()); } break; case 1: result.points(points(original.points())); break; case 2: result.geoDistance(geoDistance(original.geoDistance())); break; case 3: result.unit(randomValueOtherThan(result.unit(), () -> randomFrom(DistanceUnit.values()))); break; case 4: result.order(randomValueOtherThan(original.order(), () -> randomFrom(SortOrder.values()))); break; case 5: result.sortMode( randomValueOtherThanMany( Arrays.asList(SortMode.SUM, result.sortMode())::contains, () -> randomFrom(SortMode.values()))); break; case 6: result.setNestedFilter( randomValueOtherThan(original.getNestedFilter(), () -> randomNestedFilter())); break; case 7: result.setNestedPath( randomValueOtherThan(result.getNestedPath(), () -> randomAsciiOfLengthBetween(1, 10))); break; case 8: result.validation( randomValueOtherThan( result.validation(), () -> randomFrom(GeoValidationMethod.values()))); break; } return result; }
private static void duelFieldDataGeoPoint( Random random, AtomicReaderContext context, IndexGeoPointFieldData left, IndexGeoPointFieldData right, Distance precision) throws Exception { AtomicGeoPointFieldData leftData = random.nextBoolean() ? left.load(context) : left.loadDirect(context); AtomicGeoPointFieldData rightData = random.nextBoolean() ? right.load(context) : right.loadDirect(context); int numDocs = context.reader().maxDoc(); MultiGeoPointValues leftValues = leftData.getGeoPointValues(); MultiGeoPointValues rightValues = rightData.getGeoPointValues(); for (int i = 0; i < numDocs; ++i) { leftValues.setDocument(i); final int numValues = leftValues.count(); rightValues.setDocument(i); ; assertEquals(numValues, rightValues.count()); List<GeoPoint> leftPoints = Lists.newArrayList(); List<GeoPoint> rightPoints = Lists.newArrayList(); for (int j = 0; j < numValues; ++j) { GeoPoint l = leftValues.valueAt(j); leftPoints.add(new GeoPoint(l.getLat(), l.getLon())); GeoPoint r = rightValues.valueAt(j); rightPoints.add(new GeoPoint(r.getLat(), r.getLon())); } for (GeoPoint l : leftPoints) { assertTrue( "Couldn't find " + l + " among " + rightPoints, contains(l, rightPoints, precision)); } for (GeoPoint r : rightPoints) { assertTrue( "Couldn't find " + r + " among " + leftPoints, contains(r, leftPoints, precision)); } } }
/** * Creates a query context for a given geo point with a boost of 1 and a precision of {@value * GeoContextMapping#DEFAULT_PRECISION} */ public GeoQueryContext(GeoPoint geoPoint) { this(geoPoint.geohash()); }
/** Decode a geo point from the bits of the encoded latitude and longitudes. */ public GeoPoint decode(long latBits, long lonBits, GeoPoint out) { final double lat = decodeCoordinate(latBits); final double lon = decodeCoordinate(lonBits); return out.reset(lat, lon); }
/** * Decodes the given long-format geohash into a latitude and longitude * * @param geohash long format Geohash to decode * @param ret The Geopoint into which the latitude and longitude will be stored */ public static void decode(long geohash, GeoPoint ret) { double[] interval = decodeCell(geohash); ret.reset((interval[0] + interval[1]) / 2D, (interval[2] + interval[3]) / 2D); }
/** * Decodes the given geohash into a latitude and longitude * * @param geohash Geohash to decocde * @return the given {@link GeoPoint} reseted to the center of cell, given by the geohash */ public static GeoPoint decode(String geohash, GeoPoint ret) { double[] interval = decodeCell(geohash); return ret.reset((interval[0] + interval[1]) / 2D, (interval[2] + interval[3]) / 2D); }
@Override protected GeoDistanceRangeQueryBuilder doCreateTestQueryBuilder() { GeoDistanceRangeQueryBuilder builder; GeoPoint randomPoint = RandomGeoGenerator.randomPointIn(random(), -180.0, -89.9, 180.0, 89.9); if (randomBoolean()) { builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME, randomPoint.geohash()); } else { if (randomBoolean()) { builder = new GeoDistanceRangeQueryBuilder(GEO_POINT_FIELD_NAME, randomPoint); } else { builder = new GeoDistanceRangeQueryBuilder( GEO_POINT_FIELD_NAME, randomPoint.lat(), randomPoint.lon()); } } GeoPoint point = builder.point(); final double maxRadius = GeoUtils.maxRadialDistanceMeters(point.lat(), point.lon()); final int fromValueMeters = randomInt((int) (maxRadius * 0.5)); final int toValueMeters = randomIntBetween(fromValueMeters + 1, (int) maxRadius); DistanceUnit fromToUnits = randomFrom(DistanceUnit.values()); final String fromToUnitsStr = fromToUnits.toString(); final double fromValue = DistanceUnit.convert(fromValueMeters, DistanceUnit.DEFAULT, fromToUnits); final double toValue = DistanceUnit.convert(toValueMeters, DistanceUnit.DEFAULT, fromToUnits); if (randomBoolean()) { int branch = randomInt(2); fromToUnits = DistanceUnit.DEFAULT; switch (branch) { case 0: builder.from(fromValueMeters); break; case 1: builder.to(toValueMeters); break; case 2: builder.from(fromValueMeters); builder.to(toValueMeters); break; } } else { int branch = randomInt(2); switch (branch) { case 0: builder.from(fromValue + fromToUnitsStr); break; case 1: builder.to(toValue + fromToUnitsStr); break; case 2: builder.from(fromValue + fromToUnitsStr); builder.to(toValue + fromToUnitsStr); break; } } if (randomBoolean()) { builder.includeLower(randomBoolean()); } if (randomBoolean()) { builder.includeUpper(randomBoolean()); } if (randomBoolean()) { builder.geoDistance(randomFrom(GeoDistance.values())); } builder.unit(fromToUnits); if (randomBoolean()) { builder.setValidationMethod(randomFrom(GeoValidationMethod.values())); } if (randomBoolean()) { builder.ignoreUnmapped(randomBoolean()); } return builder; }
private void parse(ParseContext context, GeoPoint point, String geohash) throws IOException { if (fieldType().ignoreMalformed == false) { if (point.lat() > 90.0 || point.lat() < -90.0) { throw new IllegalArgumentException( "illegal latitude value [" + point.lat() + "] for " + name()); } if (point.lon() > 180.0 || point.lon() < -180) { throw new IllegalArgumentException( "illegal longitude value [" + point.lon() + "] for " + name()); } } if (fieldType().coerce) { // by setting coerce to false we are assuming all geopoints are already in a valid coordinate // system // thus this extra step can be skipped // LUCENE WATCH: This will be folded back into Lucene's GeoPointField GeoUtils.normalizePoint(point, true, true); } if (fieldType().indexOptions() != IndexOptions.NONE || fieldType().stored()) { Field field = new Field( fieldType().names().indexName(), Double.toString(point.lat()) + ',' + Double.toString(point.lon()), fieldType()); context.doc().add(field); } if (fieldType().isGeohashEnabled()) { if (geohash == null) { geohash = GeoHashUtils.encode(point.lat(), point.lon()); } addGeohashField(context, geohash); } if (fieldType().isLatLonEnabled()) { latMapper.parse(context.createExternalValueContext(point.lat())); lonMapper.parse(context.createExternalValueContext(point.lon())); } if (fieldType().hasDocValues()) { CustomGeoPointDocValuesField field = (CustomGeoPointDocValuesField) context.doc().getByKey(fieldType().names().indexName()); if (field == null) { field = new CustomGeoPointDocValuesField( fieldType().names().indexName(), point.lat(), point.lon()); context.doc().addWithKey(fieldType().names().indexName(), field); } else { field.add(point.lat(), point.lon()); } } multiFields.parse(this, context); }
public Builder(String field, GeoPoint point) { this(field, point == null ? null : point.geohash(), false); }
public Builder point(GeoPoint point) { this.geohash = point.getGeohash(); return this; }
/** * Parse a {@link GeoPoint} with a {@link XContentParser}. A geopoint has one of the following * forms: * * <ul> * <li>Object: * <pre> * {"lat": <i><latitude></i>, "lon": <i><longitude></i>} * </pre> * <li>String: * <pre>"<i><latitude></i>,<i><longitude></i>"</pre> * <li>Geohash: * <pre>"<i><geohash></i>"</pre> * <li>Array: * <pre>[<i><longitude></i>,<i><latitude></i>]</pre> * </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)) { parser.nextToken(); switch (parser.currentToken()) { case VALUE_NUMBER: case VALUE_STRING: lat = parser.doubleValue(true); break; default: throw new ElasticsearchParseException("latitude must be a number"); } } else if (LONGITUDE.equals(field)) { parser.nextToken(); switch (parser.currentToken()) { case VALUE_NUMBER: case VALUE_STRING: lon = parser.doubleValue(true); break; default: throw new ElasticsearchParseException("longitude 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 [{}], [{}] or [{}]", LATITUDE, LONGITUDE, GEOHASH); } } else { throw new ElasticsearchParseException("token [{}] not allowed", parser.currentToken()); } } 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 [{}] missing", LATITUDE); } else if (Double.isNaN(lon)) { throw new ElasticsearchParseException("field [{}] missing", LONGITUDE); } 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(); return parseGeoPoint(data, point); } else { throw new ElasticsearchParseException("geo_point expected"); } }
/** Creates a query context for a given geo point with a provided boost */ public GeoQueryContext(GeoPoint geoPoint, int boost) { this(geoPoint.geohash(), boost); }
public GeoPoint(GeoPoint template) { this(template.getLat(), template.getLon()); }
/** * Creates a query context for a geo point with a provided boost and enables generating neighbours * at specified precisions */ public GeoQueryContext(GeoPoint geoPoint, int boost, int precision, int... neighbours) { this(geoPoint.geohash(), boost, precision, neighbours); }