mirror of https://github.com/apache/lucene.git
LUCENE-7163: refactor GeoRect, Polygon, and GeoUtils tests to geo package in core.
This commit is contained in:
parent
d0156b1126
commit
6c219e99e4
|
@ -14,21 +14,19 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Represents a closed polygon on the earth's surface.
|
||||
* @lucene.experimental
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public final class Polygon {
|
||||
private final double[] polyLats;
|
||||
private final double[] polyLons;
|
||||
private final Polygon[] holes;
|
||||
|
||||
|
||||
/** minimum latitude of this polygon's bounding box area */
|
||||
public final double minLat;
|
||||
/** maximum latitude of this polygon's bounding box area */
|
||||
|
@ -37,10 +35,13 @@ public final class Polygon {
|
|||
public final double minLon;
|
||||
/** maximum longitude of this polygon's bounding box area */
|
||||
public final double maxLon;
|
||||
|
||||
|
||||
// TODO: refactor to GeoUtils once LUCENE-7165 is complete
|
||||
private static final double ENCODING_TOLERANCE = 1e-6;
|
||||
|
||||
// TODO: we could also compute the maximal inner bounding box, to make relations faster to compute?
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Creates a new Polygon from the supplied latitude/longitude array, and optionally any holes.
|
||||
*/
|
||||
public Polygon(double[] polyLats, double[] polyLons, Polygon... holes) {
|
||||
|
@ -99,14 +100,14 @@ public final class Polygon {
|
|||
this.minLon = minLon;
|
||||
this.maxLon = maxLon;
|
||||
}
|
||||
|
||||
|
||||
/** Returns true if the point is contained within this polygon */
|
||||
public boolean contains(double latitude, double longitude) {
|
||||
// check bounding box
|
||||
if (latitude < minLat || latitude > maxLat || longitude < minLon || longitude > maxLon) {
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
/*
|
||||
* simple even-odd point in polygon computation
|
||||
* 1. Determine if point is contained in the longitudinal range
|
||||
* 2. Determine whether point crosses the edge by computing the latitudinal delta
|
||||
|
@ -139,7 +140,7 @@ public final class Polygon {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Computes whether a rectangle is within a polygon (shared boundaries not allowed)
|
||||
*/
|
||||
|
@ -149,9 +150,9 @@ public final class Polygon {
|
|||
boolean contains = crosses(minLat, maxLat, minLon, maxLon) == false &&
|
||||
contains(minLat, minLon) &&
|
||||
contains(minLat, maxLon) &&
|
||||
contains(maxLat, maxLon) &&
|
||||
contains(maxLat, maxLon) &&
|
||||
contains(maxLat, minLon);
|
||||
|
||||
|
||||
if (contains) {
|
||||
// if we intersect with any hole, game over
|
||||
for (Polygon hole : holes) {
|
||||
|
@ -164,7 +165,7 @@ public final class Polygon {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convenience method for accurately computing whether a rectangle crosses a poly.
|
||||
*/
|
||||
|
@ -207,14 +208,15 @@ public final class Polygon {
|
|||
c2 = a2*polyLons[p+1] + b2*polyLats[p+1];
|
||||
s = (1/d)*(b2*c1 - b1*c2);
|
||||
t = (1/d)*(a1*c2 - a2*c1);
|
||||
x00 = Math.min(bbox[b][0], bbox[b+1][0]) - GeoEncodingUtils.TOLERANCE;
|
||||
x01 = Math.max(bbox[b][0], bbox[b+1][0]) + GeoEncodingUtils.TOLERANCE;
|
||||
y00 = Math.min(bbox[b][1], bbox[b+1][1]) - GeoEncodingUtils.TOLERANCE;
|
||||
y01 = Math.max(bbox[b][1], bbox[b+1][1]) + GeoEncodingUtils.TOLERANCE;
|
||||
x10 = Math.min(polyLons[p], polyLons[p+1]) - GeoEncodingUtils.TOLERANCE;
|
||||
x11 = Math.max(polyLons[p], polyLons[p+1]) + GeoEncodingUtils.TOLERANCE;
|
||||
y10 = Math.min(polyLats[p], polyLats[p+1]) - GeoEncodingUtils.TOLERANCE;
|
||||
y11 = Math.max(polyLats[p], polyLats[p+1]) + GeoEncodingUtils.TOLERANCE;
|
||||
// todo TOLERANCE SHOULD MATCH EVERYWHERE this is currently blocked by LUCENE-7165
|
||||
x00 = Math.min(bbox[b][0], bbox[b+1][0]) - ENCODING_TOLERANCE;
|
||||
x01 = Math.max(bbox[b][0], bbox[b+1][0]) + ENCODING_TOLERANCE;
|
||||
y00 = Math.min(bbox[b][1], bbox[b+1][1]) - ENCODING_TOLERANCE;
|
||||
y01 = Math.max(bbox[b][1], bbox[b+1][1]) + ENCODING_TOLERANCE;
|
||||
x10 = Math.min(polyLons[p], polyLons[p+1]) - ENCODING_TOLERANCE;
|
||||
x11 = Math.max(polyLons[p], polyLons[p+1]) + ENCODING_TOLERANCE;
|
||||
y10 = Math.min(polyLats[p], polyLats[p+1]) - ENCODING_TOLERANCE;
|
||||
y11 = Math.max(polyLats[p], polyLats[p+1]) + ENCODING_TOLERANCE;
|
||||
// check whether the intersection point is touching one of the line segments
|
||||
boolean touching = ((x00 == s && y00 == t) || (x01 == s && y01 == t))
|
||||
|| ((x10 == s && y10 == t) || (x11 == s && y11 == t));
|
||||
|
@ -227,22 +229,22 @@ public final class Polygon {
|
|||
} // for each bbox edge
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Returns a copy of the internal latitude array */
|
||||
public double[] getPolyLats() {
|
||||
return polyLats.clone();
|
||||
}
|
||||
|
||||
|
||||
/** Returns a copy of the internal longitude array */
|
||||
public double[] getPolyLons() {
|
||||
return polyLons.clone();
|
||||
}
|
||||
|
||||
|
||||
/** Returns a copy of the internal holes array */
|
||||
public Polygon[] getHoles() {
|
||||
return holes.clone();
|
||||
}
|
||||
|
||||
|
||||
/** Helper for multipolygon logic: returns true if any of the supplied polygons contain the point */
|
||||
public static boolean contains(Polygon[] polygons, double latitude, double longitude) {
|
||||
for (Polygon polygon : polygons) {
|
||||
|
@ -252,7 +254,7 @@ public final class Polygon {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Helper for multipolygon logic: returns true if any of the supplied polygons contain the rectangle */
|
||||
public static boolean contains(Polygon[] polygons, double minLat, double maxLat, double minLon, double maxLon) {
|
||||
for (Polygon polygon : polygons) {
|
||||
|
@ -262,7 +264,7 @@ public final class Polygon {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Helper for multipolygon logic: returns true if any of the supplied polygons crosses the rectangle */
|
||||
public static boolean crosses(Polygon[] polygons, double minLat, double maxLat, double minLon, double maxLon) {
|
||||
for (Polygon polygon : polygons) {
|
||||
|
@ -272,7 +274,7 @@ public final class Polygon {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
|
@ -14,9 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
import static java.lang.Math.PI;
|
||||
import static java.lang.Math.max;
|
||||
|
@ -38,7 +36,7 @@ import static org.apache.lucene.util.SloppyMath.asin;
|
|||
import static org.apache.lucene.util.SloppyMath.cos;
|
||||
|
||||
/** Represents a lat/lon rectangle. */
|
||||
public class GeoRect {
|
||||
public class Rectangle {
|
||||
/** maximum longitude value (in degrees) */
|
||||
public final double minLat;
|
||||
/** minimum longitude value (in degrees) */
|
||||
|
@ -51,7 +49,7 @@ public class GeoRect {
|
|||
/**
|
||||
* Constructs a bounding box by first validating the provided latitude and longitude coordinates
|
||||
*/
|
||||
public GeoRect(double minLat, double maxLat, double minLon, double maxLon) {
|
||||
public Rectangle(double minLat, double maxLat, double minLon, double maxLon) {
|
||||
GeoUtils.checkLatitude(minLat);
|
||||
GeoUtils.checkLatitude(maxLat);
|
||||
GeoUtils.checkLongitude(minLon);
|
||||
|
@ -90,7 +88,7 @@ public class GeoRect {
|
|||
}
|
||||
|
||||
/** Compute Bounding Box for a circle using WGS-84 parameters */
|
||||
public static GeoRect fromPointDistance(final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
public static Rectangle fromPointDistance(final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
checkLatitude(centerLat);
|
||||
checkLongitude(centerLon);
|
||||
final double radLat = toRadians(centerLat);
|
||||
|
@ -120,7 +118,7 @@ public class GeoRect {
|
|||
maxLon = MAX_LON_RADIANS;
|
||||
}
|
||||
|
||||
return new GeoRect(toDegrees(minLat), toDegrees(maxLat), toDegrees(minLon), toDegrees(maxLon));
|
||||
return new Rectangle(toDegrees(minLat), toDegrees(maxLat), toDegrees(minLon), toDegrees(maxLon));
|
||||
}
|
||||
|
||||
/** maximum error from {@link #axisLat(double, double)}. logic must be prepared to handle this */
|
||||
|
@ -172,7 +170,7 @@ public class GeoRect {
|
|||
}
|
||||
|
||||
/** Returns the bounding box over an array of polygons */
|
||||
public static GeoRect fromPolygon(Polygon[] polygons) {
|
||||
public static Rectangle fromPolygon(Polygon[] polygons) {
|
||||
// compute bounding box
|
||||
double minLat = Double.POSITIVE_INFINITY;
|
||||
double maxLat = Double.NEGATIVE_INFINITY;
|
||||
|
@ -186,6 +184,6 @@ public class GeoRect {
|
|||
maxLon = Math.max(polygons[i].maxLon, maxLon);
|
||||
}
|
||||
|
||||
return new GeoRect(minLat, maxLat, minLon, maxLon);
|
||||
return new Rectangle(minLat, maxLat, minLon, maxLon);
|
||||
}
|
||||
}
|
|
@ -14,12 +14,10 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -61,65 +59,6 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stability of {@link GeoEncodingUtils#geoCodedToPrefixCoded}
|
||||
*/
|
||||
public void testGeoPrefixCoding() throws Exception {
|
||||
int numIters = atLeast(1000);
|
||||
long hash;
|
||||
long decodedHash;
|
||||
BytesRefBuilder brb = new BytesRefBuilder();
|
||||
while (numIters-- >= 0) {
|
||||
hash = GeoEncodingUtils.mortonHash(randomLat(false), randomLon(false));
|
||||
for (int i=32; i<64; ++i) {
|
||||
GeoEncodingUtils.geoCodedToPrefixCoded(hash, i, brb);
|
||||
decodedHash = GeoEncodingUtils.prefixCodedToGeoCoded(brb.get());
|
||||
assertEquals((hash >>> i) << i, decodedHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testMortonEncoding() throws Exception {
|
||||
long hash = GeoEncodingUtils.mortonHash(90, 180);
|
||||
assertEquals(180.0, GeoEncodingUtils.mortonUnhashLon(hash), 0);
|
||||
assertEquals(90.0, GeoEncodingUtils.mortonUnhashLat(hash), 0);
|
||||
}
|
||||
|
||||
public void testEncodeDecode() throws Exception {
|
||||
int iters = atLeast(10000);
|
||||
boolean small = random().nextBoolean();
|
||||
for(int iter=0;iter<iters;iter++) {
|
||||
double lat = randomLat(small);
|
||||
double lon = randomLon(small);
|
||||
|
||||
long enc = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
|
||||
double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
|
||||
|
||||
assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, GeoEncodingUtils.TOLERANCE);
|
||||
assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, GeoEncodingUtils.TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
public void testScaleUnscaleIsStable() throws Exception {
|
||||
int iters = atLeast(1000);
|
||||
boolean small = random().nextBoolean();
|
||||
for(int iter=0;iter<iters;iter++) {
|
||||
double lat = randomLat(small);
|
||||
double lon = randomLon(small);
|
||||
|
||||
long enc = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
|
||||
double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
|
||||
|
||||
long enc2 = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc2 = GeoEncodingUtils.mortonUnhashLat(enc2);
|
||||
double lonEnc2 = GeoEncodingUtils.mortonUnhashLon(enc2);
|
||||
assertEquals(latEnc, latEnc2, 0.0);
|
||||
assertEquals(lonEnc, lonEnc2, 0.0);
|
||||
}
|
||||
}
|
||||
|
||||
// We rely heavily on GeoUtils.circleToBBox so we test it here:
|
||||
public void testRandomCircleToBBox() throws Exception {
|
||||
int iters = atLeast(1000);
|
||||
|
@ -141,7 +80,7 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
|
||||
// TODO: randomly quantize radius too, to provoke exact math errors?
|
||||
|
||||
GeoRect bbox = GeoRect.fromPointDistance(centerLat, centerLon, radiusMeters);
|
||||
Rectangle bbox = Rectangle.fromPointDistance(centerLat, centerLon, radiusMeters);
|
||||
|
||||
int numPointsToTry = 1000;
|
||||
for(int i=0;i<numPointsToTry;i++) {
|
||||
|
@ -197,24 +136,24 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// similar to testRandomCircleToBBox, but different, less evil, maybe simpler
|
||||
public void testBoundingBoxOpto() {
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
double lat = GeoTestUtil.nextLatitude();
|
||||
double lon = GeoTestUtil.nextLongitude();
|
||||
double radius = 50000000 * random().nextDouble();
|
||||
GeoRect box = GeoRect.fromPointDistance(lat, lon, radius);
|
||||
final GeoRect box1;
|
||||
final GeoRect box2;
|
||||
Rectangle box = Rectangle.fromPointDistance(lat, lon, radius);
|
||||
final Rectangle box1;
|
||||
final Rectangle box2;
|
||||
if (box.crossesDateline()) {
|
||||
box1 = new GeoRect(box.minLat, box.maxLat, -180, box.maxLon);
|
||||
box2 = new GeoRect(box.minLat, box.maxLat, box.minLon, 180);
|
||||
box1 = new Rectangle(box.minLat, box.maxLat, -180, box.maxLon);
|
||||
box2 = new Rectangle(box.minLat, box.maxLat, box.minLon, 180);
|
||||
} else {
|
||||
box1 = box;
|
||||
box2 = null;
|
||||
}
|
||||
|
||||
|
||||
for (int j = 0; j < 10000; j++) {
|
||||
double lat2 = GeoTestUtil.nextLatitude();
|
||||
double lon2 = GeoTestUtil.nextLongitude();
|
||||
|
@ -233,12 +172,12 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
double lat = GeoTestUtil.nextLatitude();
|
||||
double lon = GeoTestUtil.nextLongitude();
|
||||
double radius = 50000000 * random().nextDouble();
|
||||
GeoRect box = GeoRect.fromPointDistance(lat, lon, radius);
|
||||
Rectangle box = Rectangle.fromPointDistance(lat, lon, radius);
|
||||
|
||||
if (box.maxLon - lon < 90 && lon - box.minLon < 90) {
|
||||
double minPartialDistance = Math.max(SloppyMath.haversinSortKey(lat, lon, lat, box.maxLon),
|
||||
SloppyMath.haversinSortKey(lat, lon, box.maxLat, lon));
|
||||
|
||||
|
||||
for (int j = 0; j < 10000; j++) {
|
||||
double lat2 = GeoTestUtil.nextLatitude();
|
||||
double lon2 = GeoTestUtil.nextLongitude();
|
||||
|
@ -256,7 +195,7 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
for (int i = 0; i < 1000; i++) {
|
||||
double centerLat = GeoTestUtil.nextLatitude();
|
||||
double centerLon = GeoTestUtil.nextLongitude();
|
||||
GeoRect rect = GeoRect.fromPointDistance(centerLat, centerLon, Double.POSITIVE_INFINITY);
|
||||
Rectangle rect = Rectangle.fromPointDistance(centerLat, centerLon, Double.POSITIVE_INFINITY);
|
||||
assertEquals(-180.0, rect.minLon, 0.0D);
|
||||
assertEquals(180.0, rect.maxLon, 0.0D);
|
||||
assertEquals(-90.0, rect.minLat, 0.0D);
|
||||
|
@ -264,19 +203,19 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
assertFalse(rect.crossesDateline());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testAxisLat() {
|
||||
double earthCircumference = 2D * Math.PI * GeoUtils.EARTH_MEAN_RADIUS_METERS;
|
||||
assertEquals(90, GeoRect.axisLat(0, earthCircumference / 4), 0.0D);
|
||||
assertEquals(90, Rectangle.axisLat(0, earthCircumference / 4), 0.0D);
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
boolean reallyBig = random().nextInt(10) == 0;
|
||||
final double maxRadius = reallyBig ? 1.1 * earthCircumference : earthCircumference / 8;
|
||||
final double radius = maxRadius * random().nextDouble();
|
||||
double prevAxisLat = GeoRect.axisLat(0.0D, radius);
|
||||
double prevAxisLat = Rectangle.axisLat(0.0D, radius);
|
||||
for (double lat = 0.1D; lat < 90D; lat += 0.1D) {
|
||||
double nextAxisLat = GeoRect.axisLat(lat, radius);
|
||||
GeoRect bbox = GeoRect.fromPointDistance(lat, 180D, radius);
|
||||
double nextAxisLat = Rectangle.axisLat(lat, radius);
|
||||
Rectangle bbox = Rectangle.fromPointDistance(lat, 180D, radius);
|
||||
double dist = SloppyMath.haversinMeters(lat, 180D, nextAxisLat, bbox.maxLon);
|
||||
if (nextAxisLat < GeoUtils.MAX_LAT_INCL) {
|
||||
assertEquals("lat = " + lat, dist, radius, 0.1D);
|
||||
|
@ -285,10 +224,10 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
prevAxisLat = nextAxisLat;
|
||||
}
|
||||
|
||||
prevAxisLat = GeoRect.axisLat(-0.0D, radius);
|
||||
prevAxisLat = Rectangle.axisLat(-0.0D, radius);
|
||||
for (double lat = -0.1D; lat > -90D; lat -= 0.1D) {
|
||||
double nextAxisLat = GeoRect.axisLat(lat, radius);
|
||||
GeoRect bbox = GeoRect.fromPointDistance(lat, 180D, radius);
|
||||
double nextAxisLat = Rectangle.axisLat(lat, radius);
|
||||
Rectangle bbox = Rectangle.fromPointDistance(lat, 180D, radius);
|
||||
double dist = SloppyMath.haversinMeters(lat, 180D, nextAxisLat, bbox.maxLon);
|
||||
if (nextAxisLat > GeoUtils.MIN_LAT_INCL) {
|
||||
assertEquals("lat = " + lat, dist, radius, 0.1D);
|
||||
|
@ -298,7 +237,7 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: does not really belong here, but we test it like this for now
|
||||
// we can make a fake IndexReader to send boxes directly to Point visitors instead?
|
||||
public void testCircleOpto() throws Exception {
|
||||
|
@ -307,13 +246,13 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
final double centerLat = -90 + 180.0 * random().nextDouble();
|
||||
final double centerLon = -180 + 360.0 * random().nextDouble();
|
||||
final double radius = 50_000_000D * random().nextDouble();
|
||||
final GeoRect box = GeoRect.fromPointDistance(centerLat, centerLon, radius);
|
||||
final Rectangle box = Rectangle.fromPointDistance(centerLat, centerLon, radius);
|
||||
// TODO: remove this leniency!
|
||||
if (box.crossesDateline()) {
|
||||
--i; // try again...
|
||||
continue;
|
||||
}
|
||||
final double axisLat = GeoRect.axisLat(centerLat, radius);
|
||||
final double axisLat = Rectangle.axisLat(centerLat, radius);
|
||||
|
||||
for (int k = 0; k < 1000; ++k) {
|
||||
|
||||
|
@ -366,7 +305,7 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
"lonMax=%s) == false BUT\n" +
|
||||
"haversin(%s, %s, %s, %s) = %s\nbbox=%s",
|
||||
centerLat, centerLon, radius, latMin, latMax, lonMin, lonMax,
|
||||
centerLat, centerLon, lat, lon, distance, GeoRect.fromPointDistance(centerLat, centerLon, radius)),
|
||||
centerLat, centerLon, lat, lon, distance, Rectangle.fromPointDistance(centerLat, centerLon, radius)),
|
||||
distance > radius);
|
||||
} catch (AssertionError e) {
|
||||
GeoTestUtil.toWebGLEarth(latMin, latMax, lonMin, lonMax, centerLat, centerLon, radius);
|
||||
|
@ -381,9 +320,9 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
static double randomInRange(double min, double max) {
|
||||
return min + (max - min) * random().nextDouble();
|
||||
}
|
||||
|
||||
|
||||
static boolean isDisjoint(double centerLat, double centerLon, double radius, double axisLat, double latMin, double latMax, double lonMin, double lonMax) {
|
||||
if ((centerLon < lonMin || centerLon > lonMax) && (axisLat+GeoRect.AXISLAT_ERROR < latMin || axisLat-GeoRect.AXISLAT_ERROR > latMax)) {
|
||||
if ((centerLon < lonMin || centerLon > lonMax) && (axisLat+ Rectangle.AXISLAT_ERROR < latMin || axisLat- Rectangle.AXISLAT_ERROR > latMax)) {
|
||||
// circle not fully inside / crossing axis
|
||||
if (SloppyMath.haversinMeters(centerLat, centerLon, latMin, lonMin) > radius &&
|
||||
SloppyMath.haversinMeters(centerLat, centerLon, latMin, lonMax) > radius &&
|
||||
|
@ -393,7 +332,7 @@ public class TestGeoUtils extends LuceneTestCase {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ import org.apache.lucene.search.FieldDoc;
|
|||
import org.apache.lucene.search.PointRangeQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
/**
|
||||
* An indexed location field.
|
||||
|
|
|
@ -26,8 +26,7 @@ import org.apache.lucene.index.SortedNumericDocValues;
|
|||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.LeafFieldComparator;
|
||||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.spatial.util.GeoRect;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
||||
/**
|
||||
|
@ -83,7 +82,7 @@ class LatLonPointDistanceComparator extends FieldComparator<Double> implements L
|
|||
// sampling if we get called way too much: don't make gobs of bounding
|
||||
// boxes if comparator hits a worst case order (e.g. backwards distance order)
|
||||
if (setBottomCounter < 1024 || (setBottomCounter & 0x3F) == 0x3F) {
|
||||
GeoRect box = GeoRect.fromPointDistance(latitude, longitude, haversin2(bottom));
|
||||
Rectangle box = Rectangle.fromPointDistance(latitude, longitude, haversin2(bottom));
|
||||
// pre-encode our box to our integer encoding, so we don't have to decode
|
||||
// to double values for uncompetitive hits. This has some cost!
|
||||
minLat = LatLonPoint.encodeLatitude(box.minLat);
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.apache.lucene.document;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.FieldInfo;
|
||||
import org.apache.lucene.index.LeafReader;
|
||||
|
@ -35,7 +36,6 @@ import org.apache.lucene.search.Query;
|
|||
import org.apache.lucene.search.Scorer;
|
||||
import org.apache.lucene.search.TwoPhaseIterator;
|
||||
import org.apache.lucene.search.Weight;
|
||||
import org.apache.lucene.spatial.util.GeoRect;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.util.BitSet;
|
||||
import org.apache.lucene.util.DocIdSetBuilder;
|
||||
|
@ -71,7 +71,7 @@ final class LatLonPointDistanceQuery extends Query {
|
|||
|
||||
@Override
|
||||
public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
|
||||
GeoRect box = GeoRect.fromPointDistance(latitude, longitude, radiusMeters);
|
||||
Rectangle box = Rectangle.fromPointDistance(latitude, longitude, radiusMeters);
|
||||
// create bounding box(es) for the distance range
|
||||
// these are pre-encoded with LatLonPoint's encoding
|
||||
final byte minLat[] = new byte[Integer.BYTES];
|
||||
|
@ -108,7 +108,7 @@ final class LatLonPointDistanceQuery extends Query {
|
|||
maxPartialDistance = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
|
||||
final double axisLat = GeoRect.axisLat(latitude, radiusMeters);
|
||||
final double axisLat = Rectangle.axisLat(latitude, radiusMeters);
|
||||
|
||||
return new ConstantScoreWeight(this) {
|
||||
|
||||
|
@ -196,7 +196,7 @@ final class LatLonPointDistanceQuery extends Query {
|
|||
double latMax = LatLonPoint.decodeLatitude(maxPackedValue, 0);
|
||||
double lonMax = LatLonPoint.decodeLongitude(maxPackedValue, Integer.BYTES);
|
||||
|
||||
if ((longitude < lonMin || longitude > lonMax) && (axisLat+GeoRect.AXISLAT_ERROR < latMin || axisLat-GeoRect.AXISLAT_ERROR > latMax)) {
|
||||
if ((longitude < lonMin || longitude > lonMax) && (axisLat+ Rectangle.AXISLAT_ERROR < latMin || axisLat- Rectangle.AXISLAT_ERROR > latMax)) {
|
||||
// circle not fully inside / crossing axis
|
||||
if (SloppyMath.haversinMeters(latitude, longitude, latMin, lonMin) > radiusMeters &&
|
||||
SloppyMath.haversinMeters(latitude, longitude, latMin, lonMax) > radiusMeters &&
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.lucene.document;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.index.PointValues.IntersectVisitor;
|
||||
import org.apache.lucene.index.PointValues.Relation;
|
||||
import org.apache.lucene.search.ConstantScoreScorer;
|
||||
|
@ -42,8 +43,7 @@ import org.apache.lucene.util.FixedBitSet;
|
|||
import org.apache.lucene.util.NumericUtils;
|
||||
import org.apache.lucene.util.SparseFixedBitSet;
|
||||
import org.apache.lucene.util.StringHelper;
|
||||
import org.apache.lucene.spatial.util.GeoRect;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
/** Finds all previously indexed points that fall within the specified polygons.
|
||||
*
|
||||
|
@ -84,7 +84,7 @@ final class LatLonPointInPolygonQuery extends Query {
|
|||
|
||||
// bounding box over all polygons, this can speed up tree intersection/cheaply improve approximation for complex multi-polygons
|
||||
// these are pre-encoded with LatLonPoint's encoding
|
||||
final GeoRect box = GeoRect.fromPolygon(polygons);
|
||||
final Rectangle box = Rectangle.fromPolygon(polygons);
|
||||
final byte minLat[] = new byte[Integer.BYTES];
|
||||
final byte maxLat[] = new byte[Integer.BYTES];
|
||||
final byte minLon[] = new byte[Integer.BYTES];
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.apache.lucene.document;
|
|||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.geo.GeoTestUtil;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriterConfig;
|
||||
import org.apache.lucene.index.RandomIndexWriter;
|
||||
|
@ -29,7 +30,6 @@ import org.apache.lucene.search.MatchAllDocsQuery;
|
|||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.spatial.util.GeoTestUtil;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.search;
|
|||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.LatLonPoint;
|
||||
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
public class TestLatLonPointQueries extends BaseGeoPointTestCase {
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.apache.lucene.search.BooleanClause;
|
|||
import org.apache.lucene.search.BooleanQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.GeoRect;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
|
||||
/** Implements a simple point distance query on a GeoPoint field. This is based on
|
||||
|
@ -80,10 +80,10 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
* {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding} parameter
|
||||
**/
|
||||
public GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
this(field, termEncoding, GeoRect.fromPointDistance(centerLat, centerLon, checkRadius(radiusMeters)), centerLat, centerLon, radiusMeters);
|
||||
this(field, termEncoding, Rectangle.fromPointDistance(centerLat, centerLon, checkRadius(radiusMeters)), centerLat, centerLon, radiusMeters);
|
||||
}
|
||||
|
||||
private GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final GeoRect bbox,
|
||||
private GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final Rectangle bbox,
|
||||
final double centerLat, final double centerLon, final double radiusMeters) {
|
||||
super(field, termEncoding, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||
|
||||
|
@ -105,7 +105,7 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
unwrappedLon += -360.0D;
|
||||
}
|
||||
GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
|
||||
new GeoRect(minLat, maxLat, GeoUtils.MIN_LON_INCL, maxLon));
|
||||
new Rectangle(minLat, maxLat, GeoUtils.MIN_LON_INCL, maxLon));
|
||||
bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
|
||||
|
||||
if (unwrappedLon < maxLon) {
|
||||
|
@ -113,13 +113,13 @@ public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
|
|||
unwrappedLon += 360.0D;
|
||||
}
|
||||
GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
|
||||
new GeoRect(minLat, maxLat, minLon, GeoUtils.MAX_LON_INCL));
|
||||
new Rectangle(minLat, maxLat, minLon, GeoUtils.MAX_LON_INCL));
|
||||
bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
|
||||
|
||||
return bqb.build();
|
||||
}
|
||||
return new GeoPointDistanceQueryImpl(field, termEncoding, this, centerLon,
|
||||
new GeoRect(this.minLat, this.maxLat, this.minLon, this.maxLon));
|
||||
new Rectangle(this.minLat, this.maxLat, this.minLon, this.maxLon));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.geopoint.search;
|
||||
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.GeoRect;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
||||
/** Package private implementation for the public facing GeoPointDistanceQuery delegate class.
|
||||
|
@ -36,7 +36,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
final double axisLat;
|
||||
|
||||
GeoPointDistanceQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointDistanceQuery q,
|
||||
final double centerLonUnwrapped, final GeoRect bbox) {
|
||||
final double centerLonUnwrapped, final Rectangle bbox) {
|
||||
super(field, termEncoding, bbox.minLat, bbox.maxLat, bbox.minLon, bbox.maxLon);
|
||||
distanceQuery = q;
|
||||
centerLon = centerLonUnwrapped;
|
||||
|
@ -49,7 +49,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
} else {
|
||||
maxPartialDistance = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
axisLat = GeoRect.axisLat(distanceQuery.centerLat, distanceQuery.radiusMeters);
|
||||
axisLat = Rectangle.axisLat(distanceQuery.centerLat, distanceQuery.radiusMeters);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,7 +75,7 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
|
|||
minLat > GeoPointDistanceQueryImpl.this.maxLat ||
|
||||
minLon > GeoPointDistanceQueryImpl.this.maxLon) {
|
||||
return false;
|
||||
} else if ((centerLon < minLon || centerLon > maxLon) && (axisLat+GeoRect.AXISLAT_ERROR < minLat || axisLat-GeoRect.AXISLAT_ERROR > maxLat)) {
|
||||
} else if ((centerLon < minLon || centerLon > maxLon) && (axisLat+ Rectangle.AXISLAT_ERROR < minLat || axisLat- Rectangle.AXISLAT_ERROR > maxLat)) {
|
||||
if (SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, minLat, minLon) > distanceQuery.radiusMeters &&
|
||||
SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, minLat, maxLon) > distanceQuery.radiusMeters &&
|
||||
SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, maxLat, minLon) > distanceQuery.radiusMeters &&
|
||||
|
|
|
@ -23,8 +23,8 @@ import org.apache.lucene.search.Query;
|
|||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.GeoEncodingUtils;
|
||||
import org.apache.lucene.spatial.util.GeoRect;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
/** Implements a simple point in polygon query on a GeoPoint field. This is based on
|
||||
* {@code GeoPointInBBoxQueryImpl} and is implemented using a
|
||||
|
@ -82,11 +82,11 @@ public final class GeoPointInPolygonQuery extends GeoPointInBBoxQuery {
|
|||
* that fall within or on the boundary of the polygon defined by the input parameters.
|
||||
*/
|
||||
public GeoPointInPolygonQuery(String field, TermEncoding termEncoding, Polygon... polygons) {
|
||||
this(field, termEncoding, GeoRect.fromPolygon(polygons), polygons);
|
||||
this(field, termEncoding, Rectangle.fromPolygon(polygons), polygons);
|
||||
}
|
||||
|
||||
// internal constructor
|
||||
private GeoPointInPolygonQuery(String field, TermEncoding termEncoding, GeoRect boundingBox, Polygon... polygons) {
|
||||
private GeoPointInPolygonQuery(String field, TermEncoding termEncoding, Rectangle boundingBox, Polygon... polygons) {
|
||||
super(field, termEncoding, boundingBox.minLat, boundingBox.maxLat, boundingBox.minLon, boundingBox.maxLon);
|
||||
this.polygons = polygons.clone();
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.util.Objects;
|
|||
|
||||
import org.apache.lucene.search.MultiTermQuery;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
|
||||
/** Package private implementation for the public facing GeoPointInPolygonQuery delegate class.
|
||||
*
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.spatial.geopoint.search;
|
|||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.util.GeoEncodingUtils;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.spatial.geopoint.search;
|
|||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.spatial.util.GeoEncodingUtils;
|
||||
import org.apache.lucene.spatial.util.Polygon;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
|
||||
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
|
||||
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
|
||||
|
|
|
@ -37,7 +37,10 @@ import org.apache.lucene.document.Field;
|
|||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.document.StringField;
|
||||
import org.apache.lucene.geo.Rectangle;
|
||||
import org.apache.lucene.geo.GeoTestUtil;
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
|
@ -537,7 +540,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
|
||||
int iters = atLeast(25);
|
||||
for (int iter=0;iter<iters;iter++) {
|
||||
GeoRect rect = randomRect(small);
|
||||
Rectangle rect = randomRect(small);
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("\nTEST: iter=" + iter + " rect=" + rect);
|
||||
|
@ -716,7 +719,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
return lon;
|
||||
}
|
||||
|
||||
protected GeoRect randomRect(boolean small) {
|
||||
protected Rectangle randomRect(boolean small) {
|
||||
if (small) {
|
||||
return GeoTestUtil.nextBoxNear(originLat, originLon);
|
||||
} else {
|
||||
|
@ -732,7 +735,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
|
||||
protected abstract Query newPolygonQuery(String field, Polygon... polygon);
|
||||
|
||||
static final boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
|
||||
static final boolean rectContainsPoint(Rectangle rect, double pointLat, double pointLon) {
|
||||
assert Double.isNaN(pointLat) == false;
|
||||
|
||||
if (rect.minLon < rect.maxLon) {
|
||||
|
@ -820,7 +823,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
System.out.println("\nTEST: iter=" + iter + " s=" + s);
|
||||
}
|
||||
|
||||
GeoRect rect = randomRect(small);
|
||||
Rectangle rect = randomRect(small);
|
||||
|
||||
Query query = newRectQuery(FIELD_NAME, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
|
||||
|
||||
|
@ -1173,7 +1176,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
}
|
||||
|
||||
public void testRectBoundariesAreInclusive() throws Exception {
|
||||
GeoRect rect;
|
||||
Rectangle rect;
|
||||
// TODO: why this dateline leniency???
|
||||
while (true) {
|
||||
rect = randomRect(random().nextBoolean());
|
||||
|
@ -1182,7 +1185,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
// this test works in quantized space: for testing inclusiveness of exact edges it must be aware of index-time quantization!
|
||||
rect = new GeoRect(quantizeLat(rect.minLat), quantizeLat(rect.maxLat), quantizeLon(rect.minLon), quantizeLon(rect.maxLon));
|
||||
rect = new Rectangle(quantizeLat(rect.minLat), quantizeLat(rect.maxLat), quantizeLon(rect.minLon), quantizeLon(rect.maxLon));
|
||||
Directory dir = newDirectory();
|
||||
IndexWriterConfig iwc = newIndexWriterConfig();
|
||||
// Else seeds may not reproduce:
|
||||
|
@ -1341,7 +1344,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
public void testEquals() throws Exception {
|
||||
Query q1, q2;
|
||||
|
||||
GeoRect rect = randomRect(false);
|
||||
Rectangle rect = randomRect(false);
|
||||
|
||||
q1 = newRectQuery("field", rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
|
||||
q2 = newRectQuery("field", rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
package org.apache.lucene.spatial.util;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.util.BytesRefBuilder;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextLatitude;
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextLongitude;
|
||||
|
||||
/**
|
||||
* Tests methods in {@link GeoEncodingUtils}
|
||||
*/
|
||||
public class TestGeoEncodingUtils extends LuceneTestCase {
|
||||
/**
|
||||
* Tests stability of {@link GeoEncodingUtils#geoCodedToPrefixCoded}
|
||||
*/
|
||||
public void testGeoPrefixCoding() throws Exception {
|
||||
int numIters = atLeast(1000);
|
||||
long hash;
|
||||
long decodedHash;
|
||||
BytesRefBuilder brb = new BytesRefBuilder();
|
||||
while (numIters-- >= 0) {
|
||||
hash = GeoEncodingUtils.mortonHash(nextLatitude(), nextLongitude());
|
||||
for (int i=32; i<64; ++i) {
|
||||
GeoEncodingUtils.geoCodedToPrefixCoded(hash, i, brb);
|
||||
decodedHash = GeoEncodingUtils.prefixCodedToGeoCoded(brb.get());
|
||||
assertEquals((hash >>> i) << i, decodedHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void testMortonEncoding() throws Exception {
|
||||
long hash = GeoEncodingUtils.mortonHash(90, 180);
|
||||
assertEquals(180.0, GeoEncodingUtils.mortonUnhashLon(hash), 0);
|
||||
assertEquals(90.0, GeoEncodingUtils.mortonUnhashLat(hash), 0);
|
||||
}
|
||||
|
||||
public void testEncodeDecode() throws Exception {
|
||||
int iters = atLeast(10000);
|
||||
for(int iter=0;iter<iters;iter++) {
|
||||
double lat = nextLatitude();
|
||||
double lon = nextLongitude();
|
||||
|
||||
long enc = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
|
||||
double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
|
||||
|
||||
assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, GeoEncodingUtils.TOLERANCE);
|
||||
assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, GeoEncodingUtils.TOLERANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/** make sure values always go down: this is important for edge case consistency */
|
||||
public void testEncodeDecodeRoundsDown() throws Exception {
|
||||
int iters = atLeast(1000);
|
||||
for(int iter=0;iter<iters;iter++) {
|
||||
double lat = -90 + 180.0 * random().nextDouble();
|
||||
double lon = -180 + 360.0 * random().nextDouble();
|
||||
|
||||
long enc = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
|
||||
double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
|
||||
assertTrue(latEnc <= lat);
|
||||
assertTrue(lonEnc <= lon);
|
||||
}
|
||||
}
|
||||
|
||||
public void testScaleUnscaleIsStable() throws Exception {
|
||||
int iters = atLeast(1000);
|
||||
for(int iter=0;iter<iters;iter++) {
|
||||
double lat = nextLatitude();
|
||||
double lon = nextLongitude();
|
||||
|
||||
long enc = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc = GeoEncodingUtils.mortonUnhashLat(enc);
|
||||
double lonEnc = GeoEncodingUtils.mortonUnhashLon(enc);
|
||||
|
||||
long enc2 = GeoEncodingUtils.mortonHash(lat, lon);
|
||||
double latEnc2 = GeoEncodingUtils.mortonUnhashLat(enc2);
|
||||
double lonEnc2 = GeoEncodingUtils.mortonUnhashLon(enc2);
|
||||
assertEquals(latEnc, latEnc2, 0.0);
|
||||
assertEquals(lonEnc, lonEnc2, 0.0);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,8 +16,15 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import org.apache.lucene.geo.Polygon;
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextLatitude;
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextLatitudeAround;
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextLongitude;
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextLongitudeAround;
|
||||
import static org.apache.lucene.geo.GeoTestUtil.nextPolygon;
|
||||
|
||||
public class TestPolygon extends LuceneTestCase {
|
||||
|
||||
/** null polyLats not allowed */
|
||||
|
@ -109,11 +116,11 @@ public class TestPolygon extends LuceneTestCase {
|
|||
|
||||
public void testBoundingBox() throws Exception {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Polygon polygon = GeoTestUtil.nextPolygon();
|
||||
Polygon polygon = nextPolygon();
|
||||
|
||||
for (int j = 0; j < 100; j++) {
|
||||
double latitude = GeoTestUtil.nextLatitude();
|
||||
double longitude = GeoTestUtil.nextLongitude();
|
||||
double latitude = nextLatitude();
|
||||
double longitude = nextLongitude();
|
||||
// if the point is within poly, then it should be in our bounding box
|
||||
if (polygon.contains(latitude, longitude)) {
|
||||
assertTrue(latitude >= polygon.minLat && latitude <= polygon.maxLat);
|
||||
|
@ -125,11 +132,11 @@ public class TestPolygon extends LuceneTestCase {
|
|||
|
||||
public void testBoundingBoxEdgeCases() throws Exception {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Polygon polygon = GeoTestUtil.nextPolygon();
|
||||
Polygon polygon = nextPolygon();
|
||||
|
||||
for (int j = 0; j < 100; j++) {
|
||||
double latitude = GeoTestUtil.nextLatitudeAround(polygon.minLat, polygon.maxLat);
|
||||
double longitude = GeoTestUtil.nextLongitudeAround(polygon.minLon, polygon.maxLon);
|
||||
double latitude = nextLatitudeAround(polygon.minLat, polygon.maxLat);
|
||||
double longitude = nextLongitudeAround(polygon.minLon, polygon.maxLon);
|
||||
// if the point is within poly, then it should be in our bounding box
|
||||
if (polygon.contains(latitude, longitude)) {
|
||||
assertTrue(latitude >= polygon.minLat && latitude <= polygon.maxLat);
|
||||
|
|
|
@ -14,13 +14,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
package org.apache.lucene.geo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.lucene.geo.GeoUtils;
|
||||
import org.apache.lucene.util.SloppyMath;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.RandomizedContext;
|
||||
|
@ -32,25 +31,25 @@ public class GeoTestUtil {
|
|||
public static double nextLatitude() {
|
||||
return -90 + 180.0 * random().nextDouble();
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom longitude (anywhere) */
|
||||
public static double nextLongitude() {
|
||||
return -180 + 360.0 * random().nextDouble();
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom latitude, kinda close to {@code otherLatitude} */
|
||||
public static double nextLatitudeNear(double otherLatitude) {
|
||||
GeoUtils.checkLatitude(otherLatitude);
|
||||
return normalizeLatitude(otherLatitude + random().nextDouble() - 0.5);
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom longitude, kinda close to {@code otherLongitude} */
|
||||
public static double nextLongitudeNear(double otherLongitude) {
|
||||
GeoUtils.checkLongitude(otherLongitude);
|
||||
return normalizeLongitude(otherLongitude + random().nextDouble() - 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* returns next pseudorandom latitude, kinda close to {@code minLatitude/maxLatitude}
|
||||
* <b>NOTE:</b>minLatitude/maxLatitude are merely guidelines. the returned value is sometimes
|
||||
* outside of that range! this is to facilitate edge testing.
|
||||
|
@ -60,8 +59,8 @@ public class GeoTestUtil {
|
|||
GeoUtils.checkLatitude(maxLatitude);
|
||||
return normalizeLatitude(randomRangeMaybeSlightlyOutside(minLatitude, maxLatitude));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* returns next pseudorandom longitude, kinda close to {@code minLongitude/maxLongitude}
|
||||
* <b>NOTE:</b>minLongitude/maxLongitude are merely guidelines. the returned value is sometimes
|
||||
* outside of that range! this is to facilitate edge testing.
|
||||
|
@ -71,27 +70,27 @@ public class GeoTestUtil {
|
|||
GeoUtils.checkLongitude(maxLongitude);
|
||||
return normalizeLongitude(randomRangeMaybeSlightlyOutside(minLongitude, maxLongitude));
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom box: can cross the 180th meridian */
|
||||
public static GeoRect nextBox() {
|
||||
public static Rectangle nextBox() {
|
||||
return nextBoxInternal(nextLatitude(), nextLatitude(), nextLongitude(), nextLongitude(), true);
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom box, can cross the 180th meridian, kinda close to {@code otherLatitude} and {@code otherLongitude} */
|
||||
public static GeoRect nextBoxNear(double otherLatitude, double otherLongitude) {
|
||||
public static Rectangle nextBoxNear(double otherLatitude, double otherLongitude) {
|
||||
GeoUtils.checkLongitude(otherLongitude);
|
||||
GeoUtils.checkLongitude(otherLongitude);
|
||||
return nextBoxInternal(nextLatitudeNear(otherLatitude), nextLatitudeNear(otherLatitude),
|
||||
return nextBoxInternal(nextLatitudeNear(otherLatitude), nextLatitudeNear(otherLatitude),
|
||||
nextLongitudeNear(otherLongitude), nextLongitudeNear(otherLongitude), true);
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom polygon */
|
||||
public static Polygon nextPolygon() {
|
||||
if (random().nextBoolean()) {
|
||||
return surpriseMePolygon(null, null);
|
||||
}
|
||||
|
||||
GeoRect box = nextBoxInternal(nextLatitude(), nextLatitude(), nextLongitude(), nextLongitude(), false);
|
||||
Rectangle box = nextBoxInternal(nextLatitude(), nextLatitude(), nextLongitude(), nextLongitude(), false);
|
||||
if (random().nextBoolean()) {
|
||||
// box
|
||||
return boxPolygon(box);
|
||||
|
@ -100,14 +99,14 @@ public class GeoTestUtil {
|
|||
return trianglePolygon(box);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom polygon, kinda close to {@code otherLatitude} and {@code otherLongitude} */
|
||||
public static Polygon nextPolygonNear(double otherLatitude, double otherLongitude) {
|
||||
if (random().nextBoolean()) {
|
||||
return surpriseMePolygon(otherLatitude, otherLongitude);
|
||||
}
|
||||
|
||||
GeoRect box = nextBoxInternal(nextLatitudeNear(otherLatitude), nextLatitudeNear(otherLatitude),
|
||||
Rectangle box = nextBoxInternal(nextLatitudeNear(otherLatitude), nextLatitudeNear(otherLatitude),
|
||||
nextLongitudeNear(otherLongitude), nextLongitudeNear(otherLongitude), false);
|
||||
if (random().nextBoolean()) {
|
||||
// box
|
||||
|
@ -118,7 +117,7 @@ public class GeoTestUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static GeoRect nextBoxInternal(double lat0, double lat1, double lon0, double lon1, boolean canCrossDateLine) {
|
||||
private static Rectangle nextBoxInternal(double lat0, double lat1, double lon0, double lon1, boolean canCrossDateLine) {
|
||||
if (lat1 < lat0) {
|
||||
double x = lat0;
|
||||
lat0 = lat1;
|
||||
|
@ -131,10 +130,10 @@ public class GeoTestUtil {
|
|||
lon1 = x;
|
||||
}
|
||||
|
||||
return new GeoRect(lat0, lat1, lon0, lon1);
|
||||
return new Rectangle(lat0, lat1, lon0, lon1);
|
||||
}
|
||||
|
||||
private static Polygon boxPolygon(GeoRect box) {
|
||||
|
||||
private static Polygon boxPolygon(Rectangle box) {
|
||||
assert box.crossesDateline() == false;
|
||||
final double[] polyLats = new double[5];
|
||||
final double[] polyLons = new double[5];
|
||||
|
@ -150,8 +149,8 @@ public class GeoTestUtil {
|
|||
polyLons[4] = box.minLon;
|
||||
return new Polygon(polyLats, polyLons);
|
||||
}
|
||||
|
||||
private static Polygon trianglePolygon(GeoRect box) {
|
||||
|
||||
private static Polygon trianglePolygon(Rectangle box) {
|
||||
assert box.crossesDateline() == false;
|
||||
final double[] polyLats = new double[4];
|
||||
final double[] polyLons = new double[4];
|
||||
|
@ -165,7 +164,7 @@ public class GeoTestUtil {
|
|||
polyLons[3] = box.minLon;
|
||||
return new Polygon(polyLats, polyLons);
|
||||
}
|
||||
|
||||
|
||||
private static Polygon surpriseMePolygon(Double otherLatitude, Double otherLongitude) {
|
||||
// repeat until we get a poly that doesn't cross dateline:
|
||||
newPoly:
|
||||
|
@ -235,7 +234,7 @@ public class GeoTestUtil {
|
|||
return new Polygon(latsArray, lonsArray);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns random double min to max or up to 1% outside of that range */
|
||||
private static double randomRangeMaybeSlightlyOutside(double min, double max) {
|
||||
return min + (random().nextDouble() + (0.5 - random().nextDouble()) * .02) * (max - min);
|
||||
|
@ -249,7 +248,7 @@ public class GeoTestUtil {
|
|||
double off = Math.abs((latitude + 90) % 360);
|
||||
return (off <= 180 ? off : 360-off) - 90;
|
||||
}
|
||||
|
||||
|
||||
/** Puts longitude in range of -180 to +180. */
|
||||
private static double normalizeLongitude(double longitude) {
|
||||
if (longitude >= -180 && longitude <= 180) {
|
||||
|
@ -264,14 +263,14 @@ public class GeoTestUtil {
|
|||
return -180 + off;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Keep it simple, we don't need to take arbitrary Random for geo tests */
|
||||
private static Random random() {
|
||||
return RandomizedContext.current().getRandom();
|
||||
}
|
||||
|
||||
|
||||
// craziness for plotting stuff :)
|
||||
|
||||
|
||||
private static double wrapLat(double lat) {
|
||||
//System.out.println("wrapLat " + lat);
|
||||
if (lat > 90) {
|
||||
|
@ -299,7 +298,7 @@ public class GeoTestUtil {
|
|||
return lon;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static void drawRectApproximatelyOnEarthSurface(String name, String color, double minLat, double maxLat, double minLon, double maxLon) {
|
||||
int steps = 20;
|
||||
System.out.println(" var " + name + " = WE.polygon([");
|
||||
|
@ -324,7 +323,7 @@ public class GeoTestUtil {
|
|||
System.out.println(" ], {color: \"" + color + "\", fillColor: \"" + color + "\"});");
|
||||
System.out.println(" " + name + ".addTo(earth);");
|
||||
}
|
||||
|
||||
|
||||
private static void plotLatApproximatelyOnEarthSurface(String name, String color, double lat, double minLon, double maxLon) {
|
||||
System.out.println(" var " + name + " = WE.polygon([");
|
||||
double lon;
|
||||
|
@ -373,7 +372,7 @@ public class GeoTestUtil {
|
|||
double lon = poly[1][i];
|
||||
System.out.println(" [" + lat + ", " + lon + "],");
|
||||
}
|
||||
System.out.println(" ], {color: '#00ff00'});");
|
||||
System.out.println(" ], {color: '#00ff00'});");
|
||||
System.out.println(" poly" + count + ".addTo(earth);");
|
||||
}
|
||||
|
||||
|
@ -399,7 +398,7 @@ public class GeoTestUtil {
|
|||
double rectMinLongitude, double rectMaxLongitude,
|
||||
double centerLatitude, double centerLongitude,
|
||||
double radiusMeters) {
|
||||
GeoRect box = GeoRect.fromPointDistance(centerLatitude, centerLongitude, radiusMeters);
|
||||
Rectangle box = Rectangle.fromPointDistance(centerLatitude, centerLongitude, radiusMeters);
|
||||
System.out.println("<!DOCTYPE HTML>");
|
||||
System.out.println("<html>");
|
||||
System.out.println(" <head>");
|
||||
|
@ -413,7 +412,7 @@ public class GeoTestUtil {
|
|||
StringBuilder b = new StringBuilder();
|
||||
inverseHaversin(b, centerLatitude, centerLongitude, radiusMeters);
|
||||
System.out.println(b);
|
||||
System.out.println(" ], {color: '#00ff00'});");
|
||||
System.out.println(" ], {color: '#00ff00'});");
|
||||
System.out.println(" polygonB.addTo(earth);");
|
||||
drawRectApproximatelyOnEarthSurface("bbox", "#00ff00", box.minLat, box.maxLat, box.minLon, box.maxLon);
|
||||
System.out.println(" WE.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{");
|
||||
|
@ -421,7 +420,7 @@ public class GeoTestUtil {
|
|||
System.out.println(" }).addTo(earth);");
|
||||
plotLatApproximatelyOnEarthSurface("lat0", "#ffffff", 4.68, 0.0, 360.0);
|
||||
plotLatApproximatelyOnEarthSurface("lat1", "#ffffff", 180-93.09, 0.0, 360.0);
|
||||
plotLatApproximatelyOnEarthSurface("axisLat", "#00ff00", GeoRect.axisLat(centerLatitude, radiusMeters), box.minLon, box.maxLon);
|
||||
plotLatApproximatelyOnEarthSurface("axisLat", "#00ff00", Rectangle.axisLat(centerLatitude, radiusMeters), box.minLon, box.maxLon);
|
||||
plotLonApproximatelyOnEarthSurface("axisLon", "#00ff00", centerLongitude, box.minLat, box.maxLat);
|
||||
System.out.println(" }");
|
||||
System.out.println(" </script>");
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Reusable Geo test utilities.
|
||||
*/
|
||||
package org.apache.lucene.geo;
|
Loading…
Reference in New Issue