Resync Geopoint hashCode/equals method
Geopoint's equals method was modified to consider two points equal if they are within a threshold. This change was done to accept round-off error introduced from GeoHash encoding methods. This commit removes this trappy leniency from the GeoPoint equals method and instead forces round-off error to be handled at the encoding source.
This commit is contained in:
parent
1ae524bace
commit
6d3dd727c2
|
@ -30,8 +30,7 @@ public final class GeoPoint {
|
||||||
|
|
||||||
private double lat;
|
private double lat;
|
||||||
private double lon;
|
private double lon;
|
||||||
private final static double TOLERANCE = XGeoUtils.TOLERANCE;
|
|
||||||
|
|
||||||
public GeoPoint() {
|
public GeoPoint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,14 +125,10 @@ public final class GeoPoint {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
final GeoPoint geoPoint = (GeoPoint) o;
|
GeoPoint geoPoint = (GeoPoint) o;
|
||||||
final double lonCompare = geoPoint.lon - lon;
|
|
||||||
final double latCompare = geoPoint.lat - lat;
|
|
||||||
|
|
||||||
if ((lonCompare < -TOLERANCE || lonCompare > TOLERANCE)
|
if (Double.compare(geoPoint.lat, lat) != 0) return false;
|
||||||
|| (latCompare < -TOLERANCE || latCompare > TOLERANCE)) {
|
if (Double.compare(geoPoint.lon, lon) != 0) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package org.elasticsearch.index.search.geo;
|
package org.elasticsearch.index.search.geo;
|
||||||
|
|
||||||
|
|
||||||
import org.apache.lucene.util.XGeoHashUtils;
|
import org.apache.lucene.util.XGeoHashUtils;
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
import org.elasticsearch.common.geo.GeoPoint;
|
import org.elasticsearch.common.geo.GeoPoint;
|
||||||
|
@ -28,6 +27,7 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
import org.elasticsearch.common.xcontent.json.JsonXContent;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.elasticsearch.test.geo.RandomGeoGenerator;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -36,9 +36,7 @@ import static org.hamcrest.Matchers.closeTo;
|
||||||
|
|
||||||
|
|
||||||
public class GeoPointParsingTests extends ESTestCase {
|
public class GeoPointParsingTests extends ESTestCase {
|
||||||
|
static double TOLERANCE = 1E-5;
|
||||||
// mind geohash precision and error
|
|
||||||
private static final double ERROR = 0.00001d;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGeoPointReset() throws IOException {
|
public void testGeoPointReset() throws IOException {
|
||||||
|
@ -46,36 +44,66 @@ public class GeoPointParsingTests extends ESTestCase {
|
||||||
double lon = 1 + randomDouble() * 179;
|
double lon = 1 + randomDouble() * 179;
|
||||||
|
|
||||||
GeoPoint point = new GeoPoint(0, 0);
|
GeoPoint point = new GeoPoint(0, 0);
|
||||||
assertCloseTo(point, 0, 0);
|
GeoPoint point2 = new GeoPoint(0, 0);
|
||||||
|
assertPointsEqual(point, point2);
|
||||||
|
|
||||||
assertCloseTo(point.reset(lat, lon), lat, lon);
|
assertPointsEqual(point.reset(lat, lon), point2.reset(lat, lon));
|
||||||
assertCloseTo(point.reset(0, 0), 0, 0);
|
assertPointsEqual(point.reset(0, 0), point2.reset(0, 0));
|
||||||
assertCloseTo(point.resetLat(lat), lat, 0);
|
assertPointsEqual(point.resetLat(lat), point2.reset(lat, 0));
|
||||||
assertCloseTo(point.resetLat(0), 0, 0);
|
assertPointsEqual(point.resetLat(0), point2.reset(0, 0));
|
||||||
assertCloseTo(point.resetLon(lon), 0, lon);
|
assertPointsEqual(point.resetLon(lon), point2.reset(0, lon));
|
||||||
assertCloseTo(point.resetLon(0), 0, 0);
|
assertPointsEqual(point.resetLon(0), point2.reset(0, 0));
|
||||||
assertCloseTo(point.resetFromGeoHash(XGeoHashUtils.stringEncode(lon, lat)), lat, lon);
|
assertCloseTo(point.resetFromGeoHash(XGeoHashUtils.stringEncode(lon, lat)), lat, lon);
|
||||||
assertCloseTo(point.reset(0, 0), 0, 0);
|
assertPointsEqual(point.reset(0, 0), point2.reset(0, 0));
|
||||||
assertCloseTo(point.resetFromString(Double.toString(lat) + ", " + Double.toHexString(lon)), lat, lon);
|
assertPointsEqual(point.resetFromString(Double.toString(lat) + ", " + Double.toHexString(lon)), point2.reset(lat, lon));
|
||||||
assertCloseTo(point.reset(0, 0), 0, 0);
|
assertPointsEqual(point.reset(0, 0), point2.reset(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEqualsHashCodeContract() {
|
||||||
|
// generate a random geopoint
|
||||||
|
final GeoPoint x = RandomGeoGenerator.randomPoint(random());
|
||||||
|
final GeoPoint y = new GeoPoint(x.lat(), x.lon());
|
||||||
|
final GeoPoint z = new GeoPoint(y.lat(), y.lon());
|
||||||
|
// GeoPoint doesn't care about coordinate system bounds, this simply validates inequality
|
||||||
|
final GeoPoint a = new GeoPoint(x.lat() + randomIntBetween(1, 5), x.lon() + randomIntBetween(1, 5));
|
||||||
|
|
||||||
|
/** equality test */
|
||||||
|
// reflexive
|
||||||
|
assertTrue(x.equals(x));
|
||||||
|
// symmetry
|
||||||
|
assertTrue(x.equals(y));
|
||||||
|
// transitivity
|
||||||
|
assertTrue(y.equals(z));
|
||||||
|
assertTrue(x.equals(z));
|
||||||
|
// inequality
|
||||||
|
assertFalse(x.equals(a));
|
||||||
|
|
||||||
|
/** hashCode test */
|
||||||
|
// symmetry
|
||||||
|
assertTrue(x.hashCode() == y.hashCode());
|
||||||
|
// transitivity
|
||||||
|
assertTrue(y.hashCode() == z.hashCode());
|
||||||
|
assertTrue(x.hashCode() == z.hashCode());
|
||||||
|
// inequality
|
||||||
|
assertFalse(x.hashCode() == a.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGeoPointParsing() throws IOException {
|
public void testGeoPointParsing() throws IOException {
|
||||||
double lat = randomDouble() * 180 - 90;
|
GeoPoint randomPt = RandomGeoGenerator.randomPoint(random());
|
||||||
double lon = randomDouble() * 360 - 180;
|
|
||||||
|
|
||||||
GeoPoint point = GeoUtils.parseGeoPoint(objectLatLon(lat, lon));
|
|
||||||
assertCloseTo(point, lat, lon);
|
|
||||||
|
|
||||||
GeoUtils.parseGeoPoint(arrayLatLon(lat, lon), point);
|
|
||||||
assertCloseTo(point, lat, lon);
|
|
||||||
|
|
||||||
GeoUtils.parseGeoPoint(geohash(lat, lon), point);
|
GeoPoint point = GeoUtils.parseGeoPoint(objectLatLon(randomPt.lat(), randomPt.lon()));
|
||||||
assertCloseTo(point, lat, lon);
|
assertPointsEqual(point, randomPt);
|
||||||
|
|
||||||
GeoUtils.parseGeoPoint(stringLatLon(lat, lon), point);
|
GeoUtils.parseGeoPoint(arrayLatLon(randomPt.lat(), randomPt.lon()), point);
|
||||||
assertCloseTo(point, lat, lon);
|
assertPointsEqual(point, randomPt);
|
||||||
|
|
||||||
|
GeoUtils.parseGeoPoint(geohash(randomPt.lat(), randomPt.lon()), point);
|
||||||
|
assertCloseTo(point, randomPt.lat(), randomPt.lon());
|
||||||
|
|
||||||
|
GeoUtils.parseGeoPoint(stringLatLon(randomPt.lat(), randomPt.lon()), point);
|
||||||
|
assertCloseTo(point, randomPt.lat(), randomPt.lon());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Based on issue5390
|
// Based on issue5390
|
||||||
|
@ -98,7 +126,7 @@ public class GeoPointParsingTests extends ESTestCase {
|
||||||
public void testInvalidPointLatHashMix() throws IOException {
|
public void testInvalidPointLatHashMix() throws IOException {
|
||||||
XContentBuilder content = JsonXContent.contentBuilder();
|
XContentBuilder content = JsonXContent.contentBuilder();
|
||||||
content.startObject();
|
content.startObject();
|
||||||
content.field("lat", 0).field("geohash", XGeoHashUtils.stringEncode(0, 0));
|
content.field("lat", 0).field("geohash", XGeoHashUtils.stringEncode(0d, 0d));
|
||||||
content.endObject();
|
content.endObject();
|
||||||
|
|
||||||
XContentParser parser = JsonXContent.jsonXContent.createParser(content.bytes());
|
XContentParser parser = JsonXContent.jsonXContent.createParser(content.bytes());
|
||||||
|
@ -111,7 +139,7 @@ public class GeoPointParsingTests extends ESTestCase {
|
||||||
public void testInvalidPointLonHashMix() throws IOException {
|
public void testInvalidPointLonHashMix() throws IOException {
|
||||||
XContentBuilder content = JsonXContent.contentBuilder();
|
XContentBuilder content = JsonXContent.contentBuilder();
|
||||||
content.startObject();
|
content.startObject();
|
||||||
content.field("lon", 0).field("geohash", XGeoHashUtils.stringEncode(0, 0));
|
content.field("lon", 0).field("geohash", XGeoHashUtils.stringEncode(0d, 0d));
|
||||||
content.endObject();
|
content.endObject();
|
||||||
|
|
||||||
XContentParser parser = JsonXContent.jsonXContent.createParser(content.bytes());
|
XContentParser parser = JsonXContent.jsonXContent.createParser(content.bytes());
|
||||||
|
@ -166,10 +194,15 @@ public class GeoPointParsingTests extends ESTestCase {
|
||||||
parser.nextToken();
|
parser.nextToken();
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void assertCloseTo(GeoPoint point, double lat, double lon) {
|
public static void assertPointsEqual(final GeoPoint point1, final GeoPoint point2) {
|
||||||
assertThat(point.lat(), closeTo(lat, ERROR));
|
assertEquals(point1, point2);
|
||||||
assertThat(point.lon(), closeTo(lon, ERROR));
|
assertEquals(point1.hashCode(), point2.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void assertCloseTo(final GeoPoint point, final double lat, final double lon) {
|
||||||
|
assertEquals(point.lat(), lat, TOLERANCE);
|
||||||
|
assertEquals(point.lon(), lon, TOLERANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import static org.elasticsearch.search.aggregations.AggregationBuilders.stats;
|
||||||
import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
|
import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
|
||||||
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
|
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertSearchResponse;
|
||||||
|
import static org.hamcrest.Matchers.closeTo;
|
||||||
|
|
||||||
@ESIntegTestCase.SuiteScopeTestCase
|
@ESIntegTestCase.SuiteScopeTestCase
|
||||||
public class MissingValueIT extends ESIntegTestCase {
|
public class MissingValueIT extends ESIntegTestCase {
|
||||||
|
@ -198,7 +199,8 @@ public class MissingValueIT extends ESIntegTestCase {
|
||||||
SearchResponse response = client().prepareSearch("idx").addAggregation(geoCentroid("centroid").field("location").missing("2,1")).get();
|
SearchResponse response = client().prepareSearch("idx").addAggregation(geoCentroid("centroid").field("location").missing("2,1")).get();
|
||||||
assertSearchResponse(response);
|
assertSearchResponse(response);
|
||||||
GeoCentroid centroid = response.getAggregations().get("centroid");
|
GeoCentroid centroid = response.getAggregations().get("centroid");
|
||||||
assertEquals(new GeoPoint(1.5, 1.5), centroid.centroid());
|
GeoPoint point = new GeoPoint(1.5, 1.5);
|
||||||
|
assertThat(point.lat(), closeTo(centroid.centroid().lat(), 1E-5));
|
||||||
|
assertThat(point.lon(), closeTo(centroid.centroid().lon(), 1E-5));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,7 @@ public abstract class AbstractGeoTestCase extends ESIntegTestCase {
|
||||||
protected static GeoPoint singleTopLeft, singleBottomRight, multiTopLeft, multiBottomRight, singleCentroid, multiCentroid, unmappedCentroid;
|
protected static GeoPoint singleTopLeft, singleBottomRight, multiTopLeft, multiBottomRight, singleCentroid, multiCentroid, unmappedCentroid;
|
||||||
protected static ObjectIntMap<String> expectedDocCountsForGeoHash = null;
|
protected static ObjectIntMap<String> expectedDocCountsForGeoHash = null;
|
||||||
protected static ObjectObjectMap<String, GeoPoint> expectedCentroidsForGeoHash = null;
|
protected static ObjectObjectMap<String, GeoPoint> expectedCentroidsForGeoHash = null;
|
||||||
|
protected static final double GEOHASH_TOLERANCE = 1E-5D;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setupSuiteScopeCluster() throws Exception {
|
public void setupSuiteScopeCluster() throws Exception {
|
||||||
|
|
|
@ -84,7 +84,8 @@ public class GeoCentroidIT extends AbstractGeoTestCase {
|
||||||
assertThat(geoCentroid, notNullValue());
|
assertThat(geoCentroid, notNullValue());
|
||||||
assertThat(geoCentroid.getName(), equalTo(aggName));
|
assertThat(geoCentroid.getName(), equalTo(aggName));
|
||||||
GeoPoint centroid = geoCentroid.centroid();
|
GeoPoint centroid = geoCentroid.centroid();
|
||||||
assertThat(centroid, equalTo(singleCentroid));
|
assertThat(centroid.lat(), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE));
|
||||||
|
assertThat(centroid.lon(), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -99,7 +100,8 @@ public class GeoCentroidIT extends AbstractGeoTestCase {
|
||||||
assertThat(geoCentroid, notNullValue());
|
assertThat(geoCentroid, notNullValue());
|
||||||
assertThat(geoCentroid.getName(), equalTo(aggName));
|
assertThat(geoCentroid.getName(), equalTo(aggName));
|
||||||
GeoPoint centroid = geoCentroid.centroid();
|
GeoPoint centroid = geoCentroid.centroid();
|
||||||
assertThat(centroid, equalTo(singleCentroid));
|
assertThat(centroid.lat(), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE));
|
||||||
|
assertThat(centroid.lon(), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -122,10 +124,12 @@ public class GeoCentroidIT extends AbstractGeoTestCase {
|
||||||
assertThat(geoCentroid.getName(), equalTo(aggName));
|
assertThat(geoCentroid.getName(), equalTo(aggName));
|
||||||
assertThat((GeoCentroid) global.getProperty(aggName), sameInstance(geoCentroid));
|
assertThat((GeoCentroid) global.getProperty(aggName), sameInstance(geoCentroid));
|
||||||
GeoPoint centroid = geoCentroid.centroid();
|
GeoPoint centroid = geoCentroid.centroid();
|
||||||
assertThat(centroid, equalTo(singleCentroid));
|
assertThat(centroid.lat(), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE));
|
||||||
assertThat((GeoPoint) global.getProperty(aggName + ".value"), equalTo(singleCentroid));
|
assertThat(centroid.lon(), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE));
|
||||||
assertThat((double) global.getProperty(aggName + ".lat"), closeTo(singleCentroid.lat(), 1e-5));
|
assertThat(((GeoPoint) global.getProperty(aggName + ".value")).lat(), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE));
|
||||||
assertThat((double) global.getProperty(aggName + ".lon"), closeTo(singleCentroid.lon(), 1e-5));
|
assertThat(((GeoPoint) global.getProperty(aggName + ".value")).lon(), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE));
|
||||||
|
assertThat((double) global.getProperty(aggName + ".lat"), closeTo(singleCentroid.lat(), GEOHASH_TOLERANCE));
|
||||||
|
assertThat((double) global.getProperty(aggName + ".lon"), closeTo(singleCentroid.lon(), GEOHASH_TOLERANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -140,7 +144,8 @@ public class GeoCentroidIT extends AbstractGeoTestCase {
|
||||||
assertThat(geoCentroid, notNullValue());
|
assertThat(geoCentroid, notNullValue());
|
||||||
assertThat(geoCentroid.getName(), equalTo(aggName));
|
assertThat(geoCentroid.getName(), equalTo(aggName));
|
||||||
GeoPoint centroid = geoCentroid.centroid();
|
GeoPoint centroid = geoCentroid.centroid();
|
||||||
assertThat(centroid, equalTo(multiCentroid));
|
assertThat(centroid.lat(), closeTo(multiCentroid.lat(), GEOHASH_TOLERANCE));
|
||||||
|
assertThat(centroid.lon(), closeTo(multiCentroid.lon(), GEOHASH_TOLERANCE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -160,7 +165,10 @@ public class GeoCentroidIT extends AbstractGeoTestCase {
|
||||||
String geohash = cell.getKeyAsString();
|
String geohash = cell.getKeyAsString();
|
||||||
GeoPoint expectedCentroid = expectedCentroidsForGeoHash.get(geohash);
|
GeoPoint expectedCentroid = expectedCentroidsForGeoHash.get(geohash);
|
||||||
GeoCentroid centroidAgg = cell.getAggregations().get(aggName);
|
GeoCentroid centroidAgg = cell.getAggregations().get(aggName);
|
||||||
assertEquals("Geohash " + geohash + " has wrong centroid ", expectedCentroid, centroidAgg.centroid());
|
assertThat("Geohash " + geohash + " has wrong centroid latitude ", expectedCentroid.lat(),
|
||||||
|
closeTo(centroidAgg.centroid().lat(), GEOHASH_TOLERANCE));
|
||||||
|
assertThat("Geohash " + geohash + " has wrong centroid longitude", expectedCentroid.lon(),
|
||||||
|
closeTo(centroidAgg.centroid().lon(), GEOHASH_TOLERANCE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue