/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.index.search.geo;
import org.apache.lucene.spatial.prefix.tree.Cell;
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
import org.apache.lucene.spatial.prefix.tree.QuadPrefixTree;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.geo.GeoHashUtils;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentParser.Token;
import org.elasticsearch.test.ESTestCase;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.distance.DistanceUtils;
import java.io.IOException;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.hamcrest.Matchers.not;
public class GeoUtilsTests extends ESTestCase {
private static final char[] BASE_32 = {'0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n',
'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
private static final double MAX_ACCEPTABLE_ERROR = 0.000000001;
public void testGeohashCellWidth() {
double equatorialDistance = 2 * Math.PI * 6378137.0;
assertThat(GeoUtils.geoHashCellWidth(0), equalTo(equatorialDistance));
assertThat(GeoUtils.geoHashCellWidth(1), equalTo(equatorialDistance / 8));
assertThat(GeoUtils.geoHashCellWidth(2), equalTo(equatorialDistance / 32));
assertThat(GeoUtils.geoHashCellWidth(3), equalTo(equatorialDistance / 256));
assertThat(GeoUtils.geoHashCellWidth(4), equalTo(equatorialDistance / 1024));
assertThat(GeoUtils.geoHashCellWidth(5), equalTo(equatorialDistance / 8192));
assertThat(GeoUtils.geoHashCellWidth(6), equalTo(equatorialDistance / 32768));
assertThat(GeoUtils.geoHashCellWidth(7), equalTo(equatorialDistance / 262144));
assertThat(GeoUtils.geoHashCellWidth(8), equalTo(equatorialDistance / 1048576));
assertThat(GeoUtils.geoHashCellWidth(9), equalTo(equatorialDistance / 8388608));
assertThat(GeoUtils.geoHashCellWidth(10), equalTo(equatorialDistance / 33554432));
assertThat(GeoUtils.geoHashCellWidth(11), equalTo(equatorialDistance / 268435456));
assertThat(GeoUtils.geoHashCellWidth(12), equalTo(equatorialDistance / 1073741824));
}
public void testGeohashCellHeight() {
double polarDistance = Math.PI * 6356752.314245;
assertThat(GeoUtils.geoHashCellHeight(0), equalTo(polarDistance));
assertThat(GeoUtils.geoHashCellHeight(1), equalTo(polarDistance / 4));
assertThat(GeoUtils.geoHashCellHeight(2), equalTo(polarDistance / 32));
assertThat(GeoUtils.geoHashCellHeight(3), equalTo(polarDistance / 128));
assertThat(GeoUtils.geoHashCellHeight(4), equalTo(polarDistance / 1024));
assertThat(GeoUtils.geoHashCellHeight(5), equalTo(polarDistance / 4096));
assertThat(GeoUtils.geoHashCellHeight(6), equalTo(polarDistance / 32768));
assertThat(GeoUtils.geoHashCellHeight(7), equalTo(polarDistance / 131072));
assertThat(GeoUtils.geoHashCellHeight(8), equalTo(polarDistance / 1048576));
assertThat(GeoUtils.geoHashCellHeight(9), equalTo(polarDistance / 4194304));
assertThat(GeoUtils.geoHashCellHeight(10), equalTo(polarDistance / 33554432));
assertThat(GeoUtils.geoHashCellHeight(11), equalTo(polarDistance / 134217728));
assertThat(GeoUtils.geoHashCellHeight(12), equalTo(polarDistance / 1073741824));
}
public void testGeohashCellSize() {
double equatorialDistance = 2 * Math.PI * 6378137.0;
double polarDistance = Math.PI * 6356752.314245;
assertThat(GeoUtils.geoHashCellSize(0), equalTo(Math.sqrt(Math.pow(polarDistance, 2) + Math.pow(equatorialDistance, 2))));
assertThat(GeoUtils.geoHashCellSize(1), equalTo(Math.sqrt(Math.pow(polarDistance / 4, 2) + Math.pow(equatorialDistance / 8, 2))));
assertThat(GeoUtils.geoHashCellSize(2), equalTo(Math.sqrt(Math.pow(polarDistance / 32, 2) + Math.pow(equatorialDistance / 32, 2))));
assertThat(GeoUtils.geoHashCellSize(3),
equalTo(Math.sqrt(Math.pow(polarDistance / 128, 2) + Math.pow(equatorialDistance / 256, 2))));
assertThat(GeoUtils.geoHashCellSize(4),
equalTo(Math.sqrt(Math.pow(polarDistance / 1024, 2) + Math.pow(equatorialDistance / 1024, 2))));
assertThat(GeoUtils.geoHashCellSize(5),
equalTo(Math.sqrt(Math.pow(polarDistance / 4096, 2) + Math.pow(equatorialDistance / 8192, 2))));
assertThat(GeoUtils.geoHashCellSize(6),
equalTo(Math.sqrt(Math.pow(polarDistance / 32768, 2) + Math.pow(equatorialDistance / 32768, 2))));
assertThat(GeoUtils.geoHashCellSize(7),
equalTo(Math.sqrt(Math.pow(polarDistance / 131072, 2) + Math.pow(equatorialDistance / 262144, 2))));
assertThat(GeoUtils.geoHashCellSize(8),
equalTo(Math.sqrt(Math.pow(polarDistance / 1048576, 2) + Math.pow(equatorialDistance / 1048576, 2))));
assertThat(GeoUtils.geoHashCellSize(9),
equalTo(Math.sqrt(Math.pow(polarDistance / 4194304, 2) + Math.pow(equatorialDistance / 8388608, 2))));
assertThat(GeoUtils.geoHashCellSize(10),
equalTo(Math.sqrt(Math.pow(polarDistance / 33554432, 2) + Math.pow(equatorialDistance / 33554432, 2))));
assertThat(GeoUtils.geoHashCellSize(11),
equalTo(Math.sqrt(Math.pow(polarDistance / 134217728, 2) + Math.pow(equatorialDistance / 268435456, 2))));
assertThat(GeoUtils.geoHashCellSize(12),
equalTo(Math.sqrt(Math.pow(polarDistance / 1073741824, 2) + Math.pow(equatorialDistance / 1073741824, 2))));
}
public void testGeoHashLevelsForPrecision() {
for (int i = 0; i < 100; i++) {
double precision = randomDouble() * 100;
int level = GeoUtils.geoHashLevelsForPrecision(precision);
assertThat(GeoUtils.geoHashCellSize(level), lessThanOrEqualTo(precision));
}
}
public void testGeoHashLevelsForPrecision_String() {
for (int i = 0; i < 100; i++) {
double precision = randomDouble() * 100;
String precisionString = precision + "m";
int level = GeoUtils.geoHashLevelsForPrecision(precisionString);
assertThat(GeoUtils.geoHashCellSize(level), lessThanOrEqualTo(precision));
}
}
public void testQuadTreeCellWidth() {
double equatorialDistance = 2 * Math.PI * 6378137.0;
assertThat(GeoUtils.quadTreeCellWidth(0), equalTo(equatorialDistance));
assertThat(GeoUtils.quadTreeCellWidth(1), equalTo(equatorialDistance / 2));
assertThat(GeoUtils.quadTreeCellWidth(2), equalTo(equatorialDistance / 4));
assertThat(GeoUtils.quadTreeCellWidth(3), equalTo(equatorialDistance / 8));
assertThat(GeoUtils.quadTreeCellWidth(4), equalTo(equatorialDistance / 16));
assertThat(GeoUtils.quadTreeCellWidth(5), equalTo(equatorialDistance / 32));
assertThat(GeoUtils.quadTreeCellWidth(6), equalTo(equatorialDistance / 64));
assertThat(GeoUtils.quadTreeCellWidth(7), equalTo(equatorialDistance / 128));
assertThat(GeoUtils.quadTreeCellWidth(8), equalTo(equatorialDistance / 256));
assertThat(GeoUtils.quadTreeCellWidth(9), equalTo(equatorialDistance / 512));
assertThat(GeoUtils.quadTreeCellWidth(10), equalTo(equatorialDistance / 1024));
assertThat(GeoUtils.quadTreeCellWidth(11), equalTo(equatorialDistance / 2048));
assertThat(GeoUtils.quadTreeCellWidth(12), equalTo(equatorialDistance / 4096));
}
public void testQuadTreeCellHeight() {
double polarDistance = Math.PI * 6356752.314245;
assertThat(GeoUtils.quadTreeCellHeight(0), equalTo(polarDistance));
assertThat(GeoUtils.quadTreeCellHeight(1), equalTo(polarDistance / 2));
assertThat(GeoUtils.quadTreeCellHeight(2), equalTo(polarDistance / 4));
assertThat(GeoUtils.quadTreeCellHeight(3), equalTo(polarDistance / 8));
assertThat(GeoUtils.quadTreeCellHeight(4), equalTo(polarDistance / 16));
assertThat(GeoUtils.quadTreeCellHeight(5), equalTo(polarDistance / 32));
assertThat(GeoUtils.quadTreeCellHeight(6), equalTo(polarDistance / 64));
assertThat(GeoUtils.quadTreeCellHeight(7), equalTo(polarDistance / 128));
assertThat(GeoUtils.quadTreeCellHeight(8), equalTo(polarDistance / 256));
assertThat(GeoUtils.quadTreeCellHeight(9), equalTo(polarDistance / 512));
assertThat(GeoUtils.quadTreeCellHeight(10), equalTo(polarDistance / 1024));
assertThat(GeoUtils.quadTreeCellHeight(11), equalTo(polarDistance / 2048));
assertThat(GeoUtils.quadTreeCellHeight(12), equalTo(polarDistance / 4096));
}
public void testQuadTreeCellSize() {
double equatorialDistance = 2 * Math.PI * 6378137.0;
double polarDistance = Math.PI * 6356752.314245;
assertThat(GeoUtils.quadTreeCellSize(0), equalTo(Math.sqrt(Math.pow(polarDistance, 2) + Math.pow(equatorialDistance, 2))));
assertThat(GeoUtils.quadTreeCellSize(1), equalTo(Math.sqrt(Math.pow(polarDistance / 2, 2) + Math.pow(equatorialDistance / 2, 2))));
assertThat(GeoUtils.quadTreeCellSize(2), equalTo(Math.sqrt(Math.pow(polarDistance / 4, 2) + Math.pow(equatorialDistance / 4, 2))));
assertThat(GeoUtils.quadTreeCellSize(3), equalTo(Math.sqrt(Math.pow(polarDistance / 8, 2) + Math.pow(equatorialDistance / 8, 2))));
assertThat(GeoUtils.quadTreeCellSize(4), equalTo(Math.sqrt(Math.pow(polarDistance / 16, 2) + Math.pow(equatorialDistance / 16, 2))));
assertThat(GeoUtils.quadTreeCellSize(5), equalTo(Math.sqrt(Math.pow(polarDistance / 32, 2) + Math.pow(equatorialDistance / 32, 2))));
assertThat(GeoUtils.quadTreeCellSize(6), equalTo(Math.sqrt(Math.pow(polarDistance / 64, 2) + Math.pow(equatorialDistance / 64, 2))));
assertThat(GeoUtils.quadTreeCellSize(7),
equalTo(Math.sqrt(Math.pow(polarDistance / 128, 2) + Math.pow(equatorialDistance / 128, 2))));
assertThat(GeoUtils.quadTreeCellSize(8),
equalTo(Math.sqrt(Math.pow(polarDistance / 256, 2) + Math.pow(equatorialDistance / 256, 2))));
assertThat(GeoUtils.quadTreeCellSize(9),
equalTo(Math.sqrt(Math.pow(polarDistance / 512, 2) + Math.pow(equatorialDistance / 512, 2))));
assertThat(GeoUtils.quadTreeCellSize(10),
equalTo(Math.sqrt(Math.pow(polarDistance / 1024, 2) + Math.pow(equatorialDistance / 1024, 2))));
assertThat(GeoUtils.quadTreeCellSize(11),
equalTo(Math.sqrt(Math.pow(polarDistance / 2048, 2) + Math.pow(equatorialDistance / 2048, 2))));
assertThat(GeoUtils.quadTreeCellSize(12),
equalTo(Math.sqrt(Math.pow(polarDistance / 4096, 2) + Math.pow(equatorialDistance / 4096, 2))));
}
public void testQuadTreeLevelsForPrecision() {
for (int i = 0; i < 100; i++) {
double precision = randomDouble() * 100;
int level = GeoUtils.quadTreeLevelsForPrecision(precision);
assertThat(GeoUtils.quadTreeCellSize(level), lessThanOrEqualTo(precision));
}
}
public void testQuadTreeLevelsForPrecisionString() {
for (int i = 0; i < 100; i++) {
double precision = randomDouble() * 100;
String precisionString = precision + "m";
int level = GeoUtils.quadTreeLevelsForPrecision(precisionString);
assertThat(GeoUtils.quadTreeCellSize(level), lessThanOrEqualTo(precision));
}
}
public void testNormalizeLatInNormalRange() {
for (int i = 0; i < 100; i++) {
double testValue = (randomDouble() * 180.0) - 90.0;
assertThat(GeoUtils.normalizeLat(testValue), closeTo(testValue, MAX_ACCEPTABLE_ERROR));
}
}
public void testNormalizeLatOutsideNormalRange() {
for (int i = 0; i < 100; i++) {
double normalisedValue = (randomDouble() * 180.0) - 90.0;
int shift = (randomBoolean() ? 1 : -1) * randomIntBetween(1, 10000);
double testValue = normalisedValue + (180.0 * shift);
double expectedValue = normalisedValue * (shift % 2 == 0 ? 1 : -1);
assertThat(GeoUtils.normalizeLat(testValue), closeTo(expectedValue, MAX_ACCEPTABLE_ERROR));
}
}
public void testNormalizeLatHuge() {
assertThat(GeoUtils.normalizeLat(-18000000000091.0), equalTo(GeoUtils.normalizeLat(-091.0)));
assertThat(GeoUtils.normalizeLat(-18000000000090.0), equalTo(GeoUtils.normalizeLat(-090.0)));
assertThat(GeoUtils.normalizeLat(-18000000000089.0), equalTo(GeoUtils.normalizeLat(-089.0)));
assertThat(GeoUtils.normalizeLat(-18000000000088.0), equalTo(GeoUtils.normalizeLat(-088.0)));
assertThat(GeoUtils.normalizeLat(-18000000000001.0), equalTo(GeoUtils.normalizeLat(-001.0)));
assertThat(GeoUtils.normalizeLat(+18000000000000.0), equalTo(GeoUtils.normalizeLat(+000.0)));
assertThat(GeoUtils.normalizeLat(+18000000000001.0), equalTo(GeoUtils.normalizeLat(+001.0)));
assertThat(GeoUtils.normalizeLat(+18000000000002.0), equalTo(GeoUtils.normalizeLat(+002.0)));
assertThat(GeoUtils.normalizeLat(+18000000000088.0), equalTo(GeoUtils.normalizeLat(+088.0)));
assertThat(GeoUtils.normalizeLat(+18000000000089.0), equalTo(GeoUtils.normalizeLat(+089.0)));
assertThat(GeoUtils.normalizeLat(+18000000000090.0), equalTo(GeoUtils.normalizeLat(+090.0)));
assertThat(GeoUtils.normalizeLat(+18000000000091.0), equalTo(GeoUtils.normalizeLat(+091.0)));
}
public void testNormalizeLatEdgeCases() {
assertThat(GeoUtils.normalizeLat(Double.POSITIVE_INFINITY), equalTo(Double.NaN));
assertThat(GeoUtils.normalizeLat(Double.NEGATIVE_INFINITY), equalTo(Double.NaN));
assertThat(GeoUtils.normalizeLat(Double.NaN), equalTo(Double.NaN));
assertThat(0.0, not(equalTo(-0.0)));
assertThat(GeoUtils.normalizeLat(-0.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLat(0.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLat(-180.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLat(180.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLat(-90.0), equalTo(-90.0));
assertThat(GeoUtils.normalizeLat(90.0), equalTo(90.0));
}
public void testNormalizeLonInNormalRange() {
for (int i = 0; i < 100; i++) {
double testValue = (randomDouble() * 360.0) - 180.0;
assertThat(GeoUtils.normalizeLon(testValue), closeTo(testValue, MAX_ACCEPTABLE_ERROR));
}
}
public void testNormalizeLonOutsideNormalRange() {
for (int i = 0; i < 100; i++) {
double normalisedValue = (randomDouble() * 360.0) - 180.0;
double testValue = normalisedValue + ((randomBoolean() ? 1 : -1) * 360.0 * randomIntBetween(1, 10000));
assertThat(GeoUtils.normalizeLon(testValue), closeTo(normalisedValue, MAX_ACCEPTABLE_ERROR));
}
}
public void testNormalizeLonHuge() {
assertThat(GeoUtils.normalizeLon(-36000000000181.0), equalTo(GeoUtils.normalizeLon(-181.0)));
assertThat(GeoUtils.normalizeLon(-36000000000180.0), equalTo(GeoUtils.normalizeLon(-180.0)));
assertThat(GeoUtils.normalizeLon(-36000000000179.0), equalTo(GeoUtils.normalizeLon(-179.0)));
assertThat(GeoUtils.normalizeLon(-36000000000178.0), equalTo(GeoUtils.normalizeLon(-178.0)));
assertThat(GeoUtils.normalizeLon(-36000000000001.0), equalTo(GeoUtils.normalizeLon(-001.0)));
assertThat(GeoUtils.normalizeLon(+36000000000000.0), equalTo(GeoUtils.normalizeLon(+000.0)));
assertThat(GeoUtils.normalizeLon(+36000000000001.0), equalTo(GeoUtils.normalizeLon(+001.0)));
assertThat(GeoUtils.normalizeLon(+36000000000002.0), equalTo(GeoUtils.normalizeLon(+002.0)));
assertThat(GeoUtils.normalizeLon(+36000000000178.0), equalTo(GeoUtils.normalizeLon(+178.0)));
assertThat(GeoUtils.normalizeLon(+36000000000179.0), equalTo(GeoUtils.normalizeLon(+179.0)));
assertThat(GeoUtils.normalizeLon(+36000000000180.0), equalTo(GeoUtils.normalizeLon(+180.0)));
assertThat(GeoUtils.normalizeLon(+36000000000181.0), equalTo(GeoUtils.normalizeLon(+181.0)));
}
public void testNormalizeLonEdgeCases() {
assertThat(GeoUtils.normalizeLon(Double.POSITIVE_INFINITY), equalTo(Double.NaN));
assertThat(GeoUtils.normalizeLon(Double.NEGATIVE_INFINITY), equalTo(Double.NaN));
assertThat(GeoUtils.normalizeLon(Double.NaN), equalTo(Double.NaN));
assertThat(0.0, not(equalTo(-0.0)));
assertThat(GeoUtils.normalizeLon(-0.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLon(0.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLon(-360.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLon(360.0), equalTo(0.0));
assertThat(GeoUtils.normalizeLon(-180.0), equalTo(180.0));
assertThat(GeoUtils.normalizeLon(180.0), equalTo(180.0));
}
public void testNormalizePointInNormalRange() {
for (int i = 0; i < 100; i++) {
double testLat = (randomDouble() * 180.0) - 90.0;
double testLon = (randomDouble() * 360.0) - 180.0;
GeoPoint testPoint = new GeoPoint(testLat, testLon);
assertNormalizedPoint(testPoint, testPoint);
}
}
public void testNormalizePointOutsideNormalRange() {
for (int i = 0; i < 100; i++) {
double normalisedLat = (randomDouble() * 180.0) - 90.0;
double normalisedLon = (randomDouble() * 360.0) - 180.0;
int shiftLat = (randomBoolean() ? 1 : -1) * randomIntBetween(1, 10000);
int shiftLon = (randomBoolean() ? 1 : -1) * randomIntBetween(1, 10000);
double testLat = normalisedLat + (180.0 * shiftLat);
double testLon = normalisedLon + (360.0 * shiftLon);
double expectedLat = normalisedLat * (shiftLat % 2 == 0 ? 1 : -1);
double expectedLon = normalisedLon + (shiftLat % 2 == 0 ? 0 : 180);
if (expectedLon > 180.0) {
expectedLon -= 360;
}
GeoPoint testPoint = new GeoPoint(testLat, testLon);
GeoPoint expectedPoint = new GeoPoint(expectedLat, expectedLon);
assertNormalizedPoint(testPoint, expectedPoint);
}
}
public void testNormalizePointOutsideNormalRange_withOptions() {
for (int i = 0; i < 100; i++) {
boolean normalize = randomBoolean();
double normalisedLat = (randomDouble() * 180.0) - 90.0;
double normalisedLon = (randomDouble() * 360.0) - 180.0;
int shift = randomIntBetween(1, 10000);
double testLat = normalisedLat + (180.0 * shift);
double testLon = normalisedLon + (360.0 * shift);
double expectedLat;
double expectedLon;
if (normalize) {
expectedLat = normalisedLat * (shift % 2 == 0 ? 1 : -1);
expectedLon = normalisedLon + ((shift % 2 == 1) ? 180 : 0);
if (expectedLon > 180.0) {
expectedLon -= 360;
}
} else {
expectedLat = testLat;
expectedLon = testLon;
}
GeoPoint testPoint = new GeoPoint(testLat, testLon);
GeoPoint expectedPoint = new GeoPoint(expectedLat, expectedLon);
GeoUtils.normalizePoint(testPoint, normalize, normalize);
assertThat("Unexpected Latitude", testPoint.lat(), closeTo(expectedPoint.lat(), MAX_ACCEPTABLE_ERROR));
assertThat("Unexpected Longitude", testPoint.lon(), closeTo(expectedPoint.lon(), MAX_ACCEPTABLE_ERROR));
}
}
public void testNormalizePointHuge() {
assertNormalizedPoint(new GeoPoint(-18000000000091.0, -36000000000181.0), new GeoPoint(-089.0, -001.0));
assertNormalizedPoint(new GeoPoint(-18000000000090.0, -36000000000180.0), new GeoPoint(-090.0, +180.0));
assertNormalizedPoint(new GeoPoint(-18000000000089.0, -36000000000179.0), new GeoPoint(-089.0, -179.0));
assertNormalizedPoint(new GeoPoint(-18000000000088.0, -36000000000178.0), new GeoPoint(-088.0, -178.0));
assertNormalizedPoint(new GeoPoint(-18000000000001.0, -36000000000001.0), new GeoPoint(-001.0, -001.0));
assertNormalizedPoint(new GeoPoint(+18000000000000.0, +18000000000000.0), new GeoPoint(+000.0, +000.0));
assertNormalizedPoint(new GeoPoint(+18000000000001.0, +36000000000001.0), new GeoPoint(+001.0, +001.0));
assertNormalizedPoint(new GeoPoint(+18000000000002.0, +36000000000002.0), new GeoPoint(+002.0, +002.0));
assertNormalizedPoint(new GeoPoint(+18000000000088.0, +36000000000178.0), new GeoPoint(+088.0, +178.0));
assertNormalizedPoint(new GeoPoint(+18000000000089.0, +36000000000179.0), new GeoPoint(+089.0, +179.0));
assertNormalizedPoint(new GeoPoint(+18000000000090.0, +36000000000180.0), new GeoPoint(+090.0, +180.0));
assertNormalizedPoint(new GeoPoint(+18000000000091.0, +36000000000181.0), new GeoPoint(+089.0, +001.0));
}
public void testNormalizePointEdgeCases() {
assertNormalizedPoint(new GeoPoint(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY), new GeoPoint(Double.NaN, Double.NaN));
assertNormalizedPoint(new GeoPoint(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY), new GeoPoint(Double.NaN, Double.NaN));
assertNormalizedPoint(new GeoPoint(Double.NaN, Double.NaN), new GeoPoint(Double.NaN, Double.NaN));
assertThat(0.0, not(equalTo(-0.0)));
assertNormalizedPoint(new GeoPoint(-0.0, -0.0), new GeoPoint(0.0, 0.0));
assertNormalizedPoint(new GeoPoint(0.0, 0.0), new GeoPoint(0.0, 0.0));
assertNormalizedPoint(new GeoPoint(-180.0, -360.0), new GeoPoint(0.0, 180.0));
assertNormalizedPoint(new GeoPoint(180.0, 360.0), new GeoPoint(0.0, 180.0));
assertNormalizedPoint(new GeoPoint(-90.0, -180.0), new GeoPoint(-90.0, -180.0));
assertNormalizedPoint(new GeoPoint(90.0, 180.0), new GeoPoint(90.0, 180.0));
}
public void testParseGeoPoint() throws IOException {
for (int i = 0; i < 100; i++) {
double lat = randomDouble() * 180 - 90 + randomIntBetween(-1000, 1000) * 180;
double lon = randomDouble() * 360 - 180 + randomIntBetween(-1000, 1000) * 360;
XContentBuilder json = jsonBuilder().startObject().field("lat", lat).field("lon", lon).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
GeoPoint point = GeoUtils.parseGeoPoint(parser);
assertThat(point, equalTo(new GeoPoint(lat, lon)));
json = jsonBuilder().startObject().field("lat", String.valueOf(lat)).field("lon", String.valueOf(lon)).endObject();
parser = createParser(json);
parser.nextToken();
point = GeoUtils.parseGeoPoint(parser);
assertThat(point, equalTo(new GeoPoint(lat, lon)));
json = jsonBuilder().startObject().startArray("foo").value(lon).value(lat).endArray().endObject();
parser = createParser(json);
while (parser.currentToken() != Token.START_ARRAY) {
parser.nextToken();
}
point = GeoUtils.parseGeoPoint(parser);
assertThat(point, equalTo(new GeoPoint(lat, lon)));
json = jsonBuilder().startObject().field("foo", lat + "," + lon).endObject();
parser = createParser(json);
while (parser.currentToken() != Token.VALUE_STRING) {
parser.nextToken();
}
point = GeoUtils.parseGeoPoint(parser);
assertThat(point, equalTo(new GeoPoint(lat, lon)));
}
}
public void testParseGeoPointGeohash() throws IOException {
for (int i = 0; i < 100; i++) {
int geoHashLength = randomIntBetween(1, GeoHashUtils.PRECISION);
StringBuilder geohashBuilder = new StringBuilder(geoHashLength);
for (int j = 0; j < geoHashLength; j++) {
geohashBuilder.append(BASE_32[randomInt(BASE_32.length - 1)]);
}
XContentBuilder json = jsonBuilder().startObject().field("geohash", geohashBuilder.toString()).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
GeoPoint point = GeoUtils.parseGeoPoint(parser);
assertThat(point.lat(), allOf(lessThanOrEqualTo(90.0), greaterThanOrEqualTo(-90.0)));
assertThat(point.lon(), allOf(lessThanOrEqualTo(180.0), greaterThanOrEqualTo(-180.0)));
json = jsonBuilder().startObject().field("geohash", geohashBuilder.toString()).endObject();
parser = createParser(json);
while (parser.currentToken() != Token.VALUE_STRING) {
parser.nextToken();
}
point = GeoUtils.parseGeoPoint(parser);
assertThat(point.lat(), allOf(lessThanOrEqualTo(90.0), greaterThanOrEqualTo(-90.0)));
assertThat(point.lon(), allOf(lessThanOrEqualTo(180.0), greaterThanOrEqualTo(-180.0)));
}
}
public void testParseGeoPointGeohashWrongType() throws IOException {
XContentBuilder json = jsonBuilder().startObject().field("geohash", 1.0).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), containsString("geohash must be a string"));
}
public void testParseGeoPointLatNoLon() throws IOException {
double lat = 0.0;
XContentBuilder json = jsonBuilder().startObject().field("lat", lat).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("field [lon] missing"));
}
public void testParseGeoPointLonNoLat() throws IOException {
double lon = 0.0;
XContentBuilder json = jsonBuilder().startObject().field("lon", lon).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("field [lat] missing"));
}
public void testParseGeoPointLonWrongType() throws IOException {
double lat = 0.0;
XContentBuilder json = jsonBuilder().startObject().field("lat", lat).field("lon", false).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("longitude must be a number"));
}
public void testParseGeoPointLatWrongType() throws IOException {
double lon = 0.0;
XContentBuilder json = jsonBuilder().startObject().field("lat", false).field("lon", lon).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("latitude must be a number"));
}
public void testParseGeoPointExtraField() throws IOException {
double lat = 0.0;
double lon = 0.0;
XContentBuilder json = jsonBuilder().startObject().field("lat", lat).field("lon", lon).field("foo", true).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("field must be either [lat], [lon] or [geohash]"));
}
public void testParseGeoPointLonLatGeoHash() throws IOException {
double lat = 0.0;
double lon = 0.0;
String geohash = "abcd";
XContentBuilder json = jsonBuilder().startObject().field("lat", lat).field("lon", lon).field("geohash", geohash).endObject();
XContentParser parser = createParser(json);
parser.nextToken();
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), containsString("field must be either lat/lon or geohash"));
}
public void testParseGeoPointArrayTooManyValues() throws IOException {
double lat = 0.0;
double lon = 0.0;
double elev = 0.0;
XContentBuilder json = jsonBuilder().startObject().startArray("foo").value(lon).value(lat).value(elev).endArray().endObject();
XContentParser parser = createParser(json);
while (parser.currentToken() != Token.START_ARRAY) {
parser.nextToken();
}
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("only two values allowed"));
}
public void testParseGeoPointArrayWrongType() throws IOException {
double lat = 0.0;
boolean lon = false;
XContentBuilder json = jsonBuilder().startObject().startArray("foo").value(lon).value(lat).endArray().endObject();
XContentParser parser = createParser(json);
while (parser.currentToken() != Token.START_ARRAY) {
parser.nextToken();
}
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("numeric value expected"));
}
public void testParseGeoPointInvalidType() throws IOException {
XContentBuilder json = jsonBuilder().startObject().field("foo", 5).endObject();
XContentParser parser = createParser(json);
while (parser.currentToken() != Token.VALUE_NUMBER) {
parser.nextToken();
}
Exception e = expectThrows(ElasticsearchParseException.class, () -> GeoUtils.parseGeoPoint(parser));
assertThat(e.getMessage(), is("geo_point expected"));
}
public void testPrefixTreeCellSizes() {
assertThat(GeoUtils.EARTH_SEMI_MAJOR_AXIS, equalTo(DistanceUtils.EARTH_EQUATORIAL_RADIUS_KM * 1000));
assertThat(GeoUtils.quadTreeCellWidth(0), lessThanOrEqualTo(GeoUtils.EARTH_EQUATOR));
SpatialContext spatialContext = new SpatialContext(true);
GeohashPrefixTree geohashPrefixTree = new GeohashPrefixTree(spatialContext, GeohashPrefixTree.getMaxLevelsPossible() / 2);
Cell gNode = geohashPrefixTree.getWorldCell();
for (int i = 0; i < geohashPrefixTree.getMaxLevels(); i++) {
double width = GeoUtils.geoHashCellWidth(i);
double height = GeoUtils.geoHashCellHeight(i);
double size = GeoUtils.geoHashCellSize(i);
double degrees = 360.0 * width / GeoUtils.EARTH_EQUATOR;
int level = GeoUtils.quadTreeLevelsForPrecision(size);
assertThat(GeoUtils.quadTreeCellWidth(level), lessThanOrEqualTo(width));
assertThat(GeoUtils.quadTreeCellHeight(level), lessThanOrEqualTo(height));
assertThat(GeoUtils.geoHashLevelsForPrecision(size), equalTo(geohashPrefixTree.getLevelForDistance(degrees)));
assertThat("width at level " + i, gNode.getShape().getBoundingBox().getWidth(), equalTo(360.d * width / GeoUtils.EARTH_EQUATOR));
assertThat("height at level " + i, gNode.getShape().getBoundingBox().getHeight(), equalTo(180.d * height
/ GeoUtils.EARTH_POLAR_DISTANCE));
gNode = gNode.getNextLevelCells(null).next();
}
QuadPrefixTree quadPrefixTree = new QuadPrefixTree(spatialContext);
Cell qNode = quadPrefixTree.getWorldCell();
for (int i = 0; i < quadPrefixTree.getMaxLevels(); i++) {
double degrees = 360.0 / (1L << i);
double width = GeoUtils.quadTreeCellWidth(i);
double height = GeoUtils.quadTreeCellHeight(i);
double size = GeoUtils.quadTreeCellSize(i);
int level = GeoUtils.quadTreeLevelsForPrecision(size);
assertThat(GeoUtils.quadTreeCellWidth(level), lessThanOrEqualTo(width));
assertThat(GeoUtils.quadTreeCellHeight(level), lessThanOrEqualTo(height));
assertThat(GeoUtils.quadTreeLevelsForPrecision(size), equalTo(quadPrefixTree.getLevelForDistance(degrees)));
assertThat("width at level " + i, qNode.getShape().getBoundingBox().getWidth(), equalTo(360.d * width / GeoUtils.EARTH_EQUATOR));
assertThat("height at level " + i, qNode.getShape().getBoundingBox().getHeight(), equalTo(180.d * height
/ GeoUtils.EARTH_POLAR_DISTANCE));
qNode = qNode.getNextLevelCells(null).next();
}
}
private static void assertNormalizedPoint(GeoPoint input, GeoPoint expected) {
GeoUtils.normalizePoint(input);
if (Double.isNaN(expected.lat())) {
assertThat("Unexpected Latitude", input.lat(), equalTo(expected.lat()));
} else {
assertThat("Unexpected Latitude", input.lat(), closeTo(expected.lat(), MAX_ACCEPTABLE_ERROR));
}
if (Double.isNaN(expected.lon())) {
assertThat("Unexpected Longitude", input.lon(), equalTo(expected.lon()));
} else {
assertThat("Unexpected Longitude", input.lon(), closeTo(expected.lon(), MAX_ACCEPTABLE_ERROR));
}
}
}