mirror of https://github.com/apache/lucene.git
don't test LatLonPoint's quantization with epsilons: demand exact answers
This commit is contained in:
parent
a893aaaf4d
commit
370f2d3488
|
@ -46,8 +46,9 @@ import org.apache.lucene.geo.Polygon;
|
||||||
* <li>{@link #newPolygonQuery newPolygonQuery()} for matching points within an arbitrary polygon.
|
* <li>{@link #newPolygonQuery newPolygonQuery()} for matching points within an arbitrary polygon.
|
||||||
* </ul>
|
* </ul>
|
||||||
* <p>
|
* <p>
|
||||||
* <b>WARNING</b>: Values are indexed with some loss of precision, incurring up to 1E-7 error from the
|
* <b>WARNING</b>: Values are indexed with some loss of precision from the
|
||||||
* original {@code double} values.
|
* original {@code double} values (4.190951585769653E-8 for the latitude component
|
||||||
|
* and 8.381903171539307E-8 for longitude).
|
||||||
* @see PointValues
|
* @see PointValues
|
||||||
*/
|
*/
|
||||||
// TODO ^^^ that is very sandy and hurts the API, usage, and tests tremendously, because what the user passes
|
// TODO ^^^ that is very sandy and hurts the API, usage, and tests tremendously, because what the user passes
|
||||||
|
@ -98,9 +99,9 @@ public class LatLonPoint extends Field {
|
||||||
|
|
||||||
private static final int BITS = 32;
|
private static final int BITS = 32;
|
||||||
private static final double LONGITUDE_MUL = (0x1L<<BITS)/360.0D;
|
private static final double LONGITUDE_MUL = (0x1L<<BITS)/360.0D;
|
||||||
private static final double LONGITUDE_DECODE = 1/LONGITUDE_MUL;
|
static final double LONGITUDE_DECODE = 1/LONGITUDE_MUL;
|
||||||
private static final double LATITUDE_MUL = (0x1L<<BITS)/180.0D;
|
private static final double LATITUDE_MUL = (0x1L<<BITS)/180.0D;
|
||||||
private static final double LATITUDE_DECODE = 1/LATITUDE_MUL;
|
static final double LATITUDE_DECODE = 1/LATITUDE_MUL;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|
|
@ -16,8 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.apache.lucene.document;
|
package org.apache.lucene.document;
|
||||||
|
|
||||||
import org.apache.lucene.geo.GeoTestUtil;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.apache.lucene.util.LuceneTestCase;
|
import org.apache.lucene.util.LuceneTestCase;
|
||||||
|
import org.apache.lucene.util.NumericUtils;
|
||||||
|
import org.apache.lucene.util.TestUtil;
|
||||||
|
|
||||||
/** Simple tests for {@link LatLonPoint} */
|
/** Simple tests for {@link LatLonPoint} */
|
||||||
public class TestLatLonPoint extends LuceneTestCase {
|
public class TestLatLonPoint extends LuceneTestCase {
|
||||||
|
@ -35,179 +38,113 @@ public class TestLatLonPoint extends LuceneTestCase {
|
||||||
// sort field
|
// sort field
|
||||||
assertEquals("<distance:\"field\" latitude=18.0 longitude=19.0>", LatLonPoint.newDistanceSort("field", 18.0, 19.0).toString());
|
assertEquals("<distance:\"field\" latitude=18.0 longitude=19.0>", LatLonPoint.newDistanceSort("field", 18.0, 19.0).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEncodeDecode() throws Exception {
|
|
||||||
// just for testing quantization error
|
|
||||||
final double ENCODING_TOLERANCE = 1e-7;
|
|
||||||
|
|
||||||
int iters = atLeast(10000);
|
/**
|
||||||
for(int iter=0;iter<iters;iter++) {
|
* step through some integers, ensuring they decode to their expected double values.
|
||||||
double lat = GeoTestUtil.nextLatitude();
|
* double values start at -90 and increase by LATITUDE_DECODE for each integer.
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(lat));
|
* check edge cases within the double range and random doubles within the range too.
|
||||||
assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, ENCODING_TOLERANCE);
|
*/
|
||||||
|
public void testLatitudeQuantization() throws Exception {
|
||||||
|
Random random = random();
|
||||||
|
for (int i = 0; i < 10000; i++) {
|
||||||
|
int encoded = random.nextInt();
|
||||||
|
double min = -90.0 + (encoded - (long)Integer.MIN_VALUE) * LatLonPoint.LATITUDE_DECODE;
|
||||||
|
double decoded = LatLonPoint.decodeLatitude(encoded);
|
||||||
|
// should exactly equal expected value
|
||||||
|
assertEquals(min, decoded, 0.0D);
|
||||||
|
// should round-trip
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLatitude(decoded));
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLatitudeCeil(decoded));
|
||||||
|
// test within the range
|
||||||
|
if (i != Integer.MAX_VALUE) {
|
||||||
|
// this is the next representable value
|
||||||
|
// all double values between [min .. max) should encode to the current integer
|
||||||
|
// all double values between (min .. max] should encodeCeil to the next integer.
|
||||||
|
double max = min + LatLonPoint.LATITUDE_DECODE;
|
||||||
|
assertEquals(max, LatLonPoint.decodeLatitude(encoded+1), 0.0D);
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLatitude(max));
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLatitudeCeil(max));
|
||||||
|
|
||||||
double lon = GeoTestUtil.nextLongitude();
|
// first and last doubles in range that will be quantized
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lon));
|
double minEdge = Math.nextUp(min);
|
||||||
assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, ENCODING_TOLERANCE);
|
double maxEdge = Math.nextDown(max);
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLatitude(minEdge));
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLatitudeCeil(minEdge));
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLatitude(maxEdge));
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLatitudeCeil(maxEdge));
|
||||||
|
|
||||||
|
// check random values within the double range
|
||||||
|
long minBits = NumericUtils.doubleToSortableLong(minEdge);
|
||||||
|
long maxBits = NumericUtils.doubleToSortableLong(maxEdge);
|
||||||
|
for (int j = 0; j < 100; j++) {
|
||||||
|
double value = NumericUtils.sortableLongToDouble(TestUtil.nextLong(random, minBits, maxBits));
|
||||||
|
// round down
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLatitude(value));
|
||||||
|
// round up
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLatitudeCeil(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check edge/interesting cases explicitly
|
|
||||||
assertEquals(0.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(0.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(90.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(90.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(-90.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(-90.0)), ENCODING_TOLERANCE);
|
|
||||||
|
|
||||||
assertEquals(0.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(0.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(180.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(180.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(-180.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(-180.0)), ENCODING_TOLERANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEncodeDecodeCeil() throws Exception {
|
|
||||||
// just for testing quantization error
|
|
||||||
final double ENCODING_TOLERANCE = 1e-7;
|
|
||||||
|
|
||||||
int iters = atLeast(10000);
|
|
||||||
for(int iter=0;iter<iters;iter++) {
|
|
||||||
double lat = GeoTestUtil.nextLatitude();
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(lat));
|
|
||||||
assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, ENCODING_TOLERANCE);
|
|
||||||
|
|
||||||
double lon = GeoTestUtil.nextLongitude();
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(lon));
|
|
||||||
assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, ENCODING_TOLERANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check edge/interesting cases explicitly
|
|
||||||
assertEquals(0.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(0.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(90.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(90.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(-90.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(-90.0)), ENCODING_TOLERANCE);
|
|
||||||
|
|
||||||
assertEquals(0.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(0.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(180.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(180.0)), ENCODING_TOLERANCE);
|
|
||||||
assertEquals(-180.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(-180.0)), ENCODING_TOLERANCE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEncodeDecodeExtremeValues() throws Exception {
|
/**
|
||||||
|
* step through some integers, ensuring they decode to their expected double values.
|
||||||
|
* double values start at -180 and increase by LONGITUDE_DECODE for each integer.
|
||||||
|
* check edge cases within the double range and a random doubles within the range too.
|
||||||
|
*/
|
||||||
|
public void testLongitudeQuantization() throws Exception {
|
||||||
|
Random random = random();
|
||||||
|
for (int i = 0; i < 10000; i++) {
|
||||||
|
int encoded = random.nextInt();
|
||||||
|
double min = -180.0 + (encoded - (long)Integer.MIN_VALUE) * LatLonPoint.LONGITUDE_DECODE;
|
||||||
|
double decoded = LatLonPoint.decodeLongitude(encoded);
|
||||||
|
// should exactly equal expected value
|
||||||
|
assertEquals(min, decoded, 0.0D);
|
||||||
|
// should round-trip
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLongitude(decoded));
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLongitudeCeil(decoded));
|
||||||
|
// test within the range
|
||||||
|
if (i != Integer.MAX_VALUE) {
|
||||||
|
// this is the next representable value
|
||||||
|
// all double values between [min .. max) should encode to the current integer
|
||||||
|
// all double values between (min .. max] should encodeCeil to the next integer.
|
||||||
|
double max = min + LatLonPoint.LONGITUDE_DECODE;
|
||||||
|
assertEquals(max, LatLonPoint.decodeLongitude(encoded+1), 0.0D);
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLongitude(max));
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLongitudeCeil(max));
|
||||||
|
|
||||||
|
// first and last doubles in range that will be quantized
|
||||||
|
double minEdge = Math.nextUp(min);
|
||||||
|
double maxEdge = Math.nextDown(max);
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLongitude(minEdge));
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLongitudeCeil(minEdge));
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLongitude(maxEdge));
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLongitudeCeil(maxEdge));
|
||||||
|
|
||||||
|
// check random values within the double range
|
||||||
|
long minBits = NumericUtils.doubleToSortableLong(minEdge);
|
||||||
|
long maxBits = NumericUtils.doubleToSortableLong(maxEdge);
|
||||||
|
for (int j = 0; j < 100; j++) {
|
||||||
|
double value = NumericUtils.sortableLongToDouble(TestUtil.nextLong(random, minBits, maxBits));
|
||||||
|
// round down
|
||||||
|
assertEquals(encoded, LatLonPoint.encodeLongitude(value));
|
||||||
|
// round up
|
||||||
|
assertEquals(encoded+1, LatLonPoint.encodeLongitudeCeil(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check edge/interesting cases explicitly
|
||||||
|
public void testEncodeEdgeCases() {
|
||||||
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLatitude(-90.0));
|
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLatitude(-90.0));
|
||||||
assertEquals(0, LatLonPoint.encodeLatitude(0.0));
|
|
||||||
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLatitude(90.0));
|
|
||||||
|
|
||||||
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLongitude(-180.0));
|
|
||||||
assertEquals(0, LatLonPoint.encodeLatitude(0.0));
|
|
||||||
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLongitude(180.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEncodeDecodeExtremeValuesCeil() throws Exception {
|
|
||||||
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLatitudeCeil(-90.0));
|
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLatitudeCeil(-90.0));
|
||||||
assertEquals(0, LatLonPoint.encodeLatitudeCeil(0.0));
|
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLatitude(90.0));
|
||||||
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLatitudeCeil(90.0));
|
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLatitudeCeil(90.0));
|
||||||
|
|
||||||
|
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLongitude(-180.0));
|
||||||
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLongitudeCeil(-180.0));
|
assertEquals(Integer.MIN_VALUE, LatLonPoint.encodeLongitudeCeil(-180.0));
|
||||||
assertEquals(0, LatLonPoint.encodeLatitudeCeil(0.0));
|
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLongitude(180.0));
|
||||||
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLongitudeCeil(180.0));
|
assertEquals(Integer.MAX_VALUE, LatLonPoint.encodeLongitudeCeil(180.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testEncodeDecodeIsStable() throws Exception {
|
|
||||||
int iters = atLeast(1000);
|
|
||||||
for(int iter=0;iter<iters;iter++) {
|
|
||||||
double lat = GeoTestUtil.nextLatitude();
|
|
||||||
double lon = GeoTestUtil.nextLongitude();
|
|
||||||
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(lat));
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lon));
|
|
||||||
|
|
||||||
double latEnc2 = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(latEnc));
|
|
||||||
double lonEnc2 = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lonEnc));
|
|
||||||
assertEquals(latEnc, latEnc2, 0.0);
|
|
||||||
assertEquals(lonEnc, lonEnc2, 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEncodeDecodeCeilIsStable() throws Exception {
|
|
||||||
int iters = atLeast(1000);
|
|
||||||
for(int iter=0;iter<iters;iter++) {
|
|
||||||
double lat = GeoTestUtil.nextLatitude();
|
|
||||||
double lon = GeoTestUtil.nextLongitude();
|
|
||||||
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(lat));
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(lon));
|
|
||||||
|
|
||||||
double latEnc2 = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(latEnc));
|
|
||||||
double lonEnc2 = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(lonEnc));
|
|
||||||
assertEquals(latEnc, latEnc2, 0.0);
|
|
||||||
assertEquals(lonEnc, lonEnc2, 0.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** make sure values always go down: this is important for edge case consistency */
|
|
||||||
public void testEncodeDecodeRoundsDown() throws Exception {
|
|
||||||
int iters = atLeast(10000);
|
|
||||||
for(int iter=0;iter<iters;iter++) {
|
|
||||||
final double latBase = GeoTestUtil.nextLatitude();
|
|
||||||
final double lonBase = GeoTestUtil.nextLongitude();
|
|
||||||
|
|
||||||
// test above the value
|
|
||||||
double lat = latBase;
|
|
||||||
double lon = lonBase;
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
lat = Math.min(90, Math.nextUp(lat));
|
|
||||||
lon = Math.min(180, Math.nextUp(lon));
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(lat));
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lon));
|
|
||||||
assertTrue(latEnc <= lat);
|
|
||||||
assertTrue(lonEnc <= lon);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test below the value
|
|
||||||
lat = latBase;
|
|
||||||
lon = lonBase;
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
lat = Math.max(-90, Math.nextDown(lat));
|
|
||||||
lon = Math.max(-180, Math.nextDown(lon));
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(lat));
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lon));
|
|
||||||
assertTrue(latEnc <= lat);
|
|
||||||
assertTrue(lonEnc <= lon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** bug in previous encoding! */
|
|
||||||
public void testSpecialBuggyValue() throws Exception {
|
|
||||||
double special = 124.40717171877621;
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(special));
|
|
||||||
assertTrue(lonEnc <= special);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** make sure values can go up if we need */
|
|
||||||
public void testEncodeDecodeCeilRoundsUp() throws Exception {
|
|
||||||
int iters = atLeast(10000);
|
|
||||||
for(int iter=0;iter<iters;iter++) {
|
|
||||||
final double latBase = GeoTestUtil.nextLatitude();
|
|
||||||
final double lonBase = GeoTestUtil.nextLongitude();
|
|
||||||
|
|
||||||
// test above the value
|
|
||||||
double lat = latBase;
|
|
||||||
double lon = lonBase;
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
lat = Math.min(90, Math.nextUp(lat));
|
|
||||||
lon = Math.min(180, Math.nextUp(lon));
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(lat));
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(lon));
|
|
||||||
assertTrue(latEnc >= lat);
|
|
||||||
assertTrue(lonEnc >= lon);
|
|
||||||
}
|
|
||||||
|
|
||||||
// test below the value
|
|
||||||
lat = latBase;
|
|
||||||
lon = lonBase;
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
lat = Math.max(-90, Math.nextDown(lat));
|
|
||||||
lon = Math.max(-180, Math.nextDown(lon));
|
|
||||||
double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitudeCeil(lat));
|
|
||||||
double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitudeCeil(lon));
|
|
||||||
assertTrue(latEnc >= lat);
|
|
||||||
assertTrue(lonEnc >= lon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue