Fix north pole overflow error in GeoHashUtils.bbox() (#32891)

Fixes an overflow error in  GeoHashUtils.bbox() calculation of a
bounding box for geohashes with maximum precision located next to the
north pole.
This commit is contained in:
Igor Motov 2018-08-21 14:59:37 -04:00 committed by GitHub
parent fcf8cadd9a
commit 3973bb4028
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 22 additions and 6 deletions

View File

@ -25,6 +25,8 @@ import org.apache.lucene.util.BitUtil;
import java.util.ArrayList;
import java.util.Collection;
import static org.apache.lucene.geo.GeoUtils.MAX_LAT_INCL;
/**
* Utilities for converting to/from the GeoHash standard
*
@ -48,6 +50,8 @@ public class GeoHashUtils {
private static final double LAT_SCALE = (0x1L<<BITS)/180.0D;
private static final double LON_SCALE = (0x1L<<BITS)/360.0D;
private static final short MORTON_OFFSET = (BITS<<1) - (PRECISION*5);
/** Bit encoded representation of the latitude of north pole */
private static final long MAX_LAT_BITS = (0x1L << (PRECISION * 5 / 2)) - 1;
// No instance:
private GeoHashUtils() {
@ -218,12 +222,19 @@ public class GeoHashUtils {
long ghLong = longEncode(geohash, len);
// shift away the level
ghLong >>>= 4;
// deinterleave and add 1 to lat and lon to get topRight
long lat = BitUtil.deinterleave(ghLong >>> 1) + 1;
long lon = BitUtil.deinterleave(ghLong) + 1;
GeoPoint topRight = GeoPoint.fromGeohash(BitUtil.interleave((int)lon, (int)lat) << 4 | len);
return new Rectangle(bottomLeft.lat(), topRight.lat(), bottomLeft.lon(), topRight.lon());
// deinterleave
long lon = BitUtil.deinterleave(ghLong >>> 1);
long lat = BitUtil.deinterleave(ghLong);
if (lat < MAX_LAT_BITS) {
// add 1 to lat and lon to get topRight
GeoPoint topRight = GeoPoint.fromGeohash(BitUtil.interleave((int)(lat + 1), (int)(lon + 1)) << 4 | len);
return new Rectangle(bottomLeft.lat(), topRight.lat(), bottomLeft.lon(), topRight.lon());
} else {
// We cannot go north of north pole, so just using 90 degrees instead of calculating it using
// add 1 to lon to get lon of topRight, we are going to use 90 for lat
GeoPoint topRight = GeoPoint.fromGeohash(BitUtil.interleave((int)lat, (int)(lon + 1)) << 4 | len);
return new Rectangle(bottomLeft.lat(), MAX_LAT_INCL, bottomLeft.lon(), topRight.lon());
}
}
/**

View File

@ -98,6 +98,11 @@ public class GeoHashTests extends ESTestCase {
}
}
public void testNorthPoleBoundingBox() {
Rectangle bbox = GeoHashUtils.bbox("zzbxfpgzupbx"); // Bounding box with maximum precision touching north pole
assertEquals(90.0, bbox.maxLat, 0.0000001); // Should be 90 degrees
}
public void testInvalidGeohashes() {
IllegalArgumentException ex;