mirror of https://github.com/apache/lucene.git
factor out polygon/box generation into GeoTestUtil. removes lots of leniency and unnecessary quantization! may cause failures!!!!
This commit is contained in:
parent
010f02216d
commit
956e4363f1
|
@ -19,13 +19,11 @@ package org.apache.lucene.spatial.util;
|
|||
import java.io.IOException;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.DecimalFormatSymbols;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.codecs.FilterCodec;
|
||||
|
@ -438,7 +436,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
|
||||
int iters = atLeast(25);
|
||||
for (int iter=0;iter<iters;iter++) {
|
||||
GeoRect rect = randomRect(small, small == false);
|
||||
GeoRect rect = randomRect(small);
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("\nTEST: iter=" + iter + " rect=" + rect);
|
||||
|
@ -609,67 +607,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
return quantizeLon(result);
|
||||
}
|
||||
|
||||
/** Returns {polyLats, polyLons} double[] array */
|
||||
private double[][] surpriseMePolygon() {
|
||||
// repeat until we get a poly that doesn't cross dateline:
|
||||
newPoly:
|
||||
while (true) {
|
||||
//System.out.println("\nPOLY ITER");
|
||||
double centerLat = randomLat(false);
|
||||
double centerLon = randomLon(false);
|
||||
|
||||
double radius = 0.1 + 20 * random().nextDouble();
|
||||
double radiusDelta = random().nextDouble();
|
||||
|
||||
ArrayList<Double> lats = new ArrayList<>();
|
||||
ArrayList<Double> lons = new ArrayList<>();
|
||||
double angle = 0.0;
|
||||
while (true) {
|
||||
angle += random().nextDouble()*40.0;
|
||||
//System.out.println(" angle " + angle);
|
||||
if (angle > 360) {
|
||||
break;
|
||||
}
|
||||
double len = radius * (1.0 - radiusDelta + radiusDelta * random().nextDouble());
|
||||
//System.out.println(" len=" + len);
|
||||
double lat = centerLat + len * Math.cos(Math.toRadians(angle));
|
||||
double lon = centerLon + len * Math.sin(Math.toRadians(angle));
|
||||
if (lon <= GeoUtils.MIN_LON_INCL || lon >= GeoUtils.MAX_LON_INCL) {
|
||||
// cannot cross dateline: try again!
|
||||
continue newPoly;
|
||||
}
|
||||
if (lat > 90) {
|
||||
// cross the north pole
|
||||
lat = 180 - lat;
|
||||
lon = 180 - lon;
|
||||
} else if (lat < -90) {
|
||||
// cross the south pole
|
||||
lat = -180 - lat;
|
||||
lon = 180 - lon;
|
||||
}
|
||||
if (lon <= GeoUtils.MIN_LON_INCL || lon >= GeoUtils.MAX_LON_INCL) {
|
||||
// cannot cross dateline: try again!
|
||||
continue newPoly;
|
||||
}
|
||||
lats.add(lat);
|
||||
lons.add(lon);
|
||||
|
||||
//System.out.println(" lat=" + lats.get(lats.size()-1) + " lon=" + lons.get(lons.size()-1));
|
||||
}
|
||||
|
||||
// close it
|
||||
lats.add(lats.get(0));
|
||||
lons.add(lons.get(0));
|
||||
|
||||
double[] latsArray = new double[lats.size()];
|
||||
double[] lonsArray = new double[lons.size()];
|
||||
for(int i=0;i<lats.size();i++) {
|
||||
latsArray[i] = lats.get(i);
|
||||
lonsArray[i] = lons.get(i);
|
||||
}
|
||||
return new double[][] {latsArray, lonsArray};
|
||||
}
|
||||
}
|
||||
|
||||
/** Override this to quantize randomly generated lat, so the test won't fail due to quantization errors, which are 1) annoying to debug,
|
||||
* and 2) should never affect "real" usage terribly. */
|
||||
|
@ -683,25 +621,12 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
return lon;
|
||||
}
|
||||
|
||||
protected GeoRect randomRect(boolean small, boolean canCrossDateLine) {
|
||||
double lat0 = randomLat(small);
|
||||
double lat1 = randomLat(small);
|
||||
double lon0 = randomLon(small);
|
||||
double lon1 = randomLon(small);
|
||||
|
||||
if (lat1 < lat0) {
|
||||
double x = lat0;
|
||||
lat0 = lat1;
|
||||
lat1 = x;
|
||||
protected GeoRect randomRect(boolean small) {
|
||||
if (small) {
|
||||
return GeoTestUtil.nextBoxNear(originLat, originLon);
|
||||
} else {
|
||||
return GeoTestUtil.nextBox();
|
||||
}
|
||||
|
||||
if (canCrossDateLine == false && lon1 < lon0) {
|
||||
double x = lon0;
|
||||
lon0 = lon1;
|
||||
lon1 = x;
|
||||
}
|
||||
|
||||
return new GeoRect(lat0, lat1, lon0, lon1);
|
||||
}
|
||||
|
||||
protected void initIndexWriterConfig(String field, IndexWriterConfig iwc) {
|
||||
|
@ -726,17 +651,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
|| GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, 180.0);
|
||||
}
|
||||
}
|
||||
|
||||
static final boolean polygonContainsPoint(double polyLats[], double polyLons[], double pointLat, double pointLon) {
|
||||
return GeoRelationUtils.pointInPolygon(polyLats, polyLons, pointLat, pointLon);
|
||||
}
|
||||
|
||||
static final boolean circleContainsPoint(double centerLat, double centerLon, double radiusMeters, double pointLat, double pointLon) {
|
||||
double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
|
||||
boolean result = distanceMeters <= radiusMeters;
|
||||
//System.out.println(" shouldMatch? centerLon=" + centerLon + " centerLat=" + centerLat + " pointLon=" + pointLon + " pointLat=" + pointLat + " result=" + result + " distanceMeters=" + (distanceKM * 1000));
|
||||
return result;
|
||||
}
|
||||
|
||||
private void verify(boolean small, double[] lats, double[] lons) throws Exception {
|
||||
verifyRandomRectangles(small, lats, lons);
|
||||
|
@ -800,8 +714,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
System.out.println("\nTEST: iter=" + iter + " s=" + s);
|
||||
}
|
||||
|
||||
// Rect: don't allow dateline crossing when testing small:
|
||||
GeoRect rect = randomRect(small, small == false);
|
||||
GeoRect rect = randomRect(small);
|
||||
|
||||
Query query = newRectQuery(FIELD_NAME, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
|
||||
|
||||
|
@ -981,7 +894,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
} else if (Double.isNaN(lats[id])) {
|
||||
expected = false;
|
||||
} else {
|
||||
expected = circleContainsPoint(centerLat, centerLon, radiusMeters, lats[id], lons[id]);
|
||||
expected = SloppyMath.haversinMeters(centerLat, centerLon, lats[id], lons[id]) <= radiusMeters;
|
||||
}
|
||||
|
||||
if (hits.get(docID) != expected) {
|
||||
|
@ -1073,49 +986,16 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
System.out.println("\nTEST: iter=" + iter + " s=" + s);
|
||||
}
|
||||
|
||||
// TODO: poly query can't handle dateline crossing yet:
|
||||
final GeoRect bbox = randomRect(small, false);
|
||||
|
||||
// Polygon
|
||||
final double[] polyLats;
|
||||
final double[] polyLons;
|
||||
// TODO: factor this out, maybe if we add Polygon class?
|
||||
switch (random().nextInt(3)) {
|
||||
case 0:
|
||||
// box
|
||||
polyLats = new double[5];
|
||||
polyLons = new double[5];
|
||||
polyLats[0] = bbox.minLat;
|
||||
polyLons[0] = bbox.minLon;
|
||||
polyLats[1] = bbox.maxLat;
|
||||
polyLons[1] = bbox.minLon;
|
||||
polyLats[2] = bbox.maxLat;
|
||||
polyLons[2] = bbox.maxLon;
|
||||
polyLats[3] = bbox.minLat;
|
||||
polyLons[3] = bbox.maxLon;
|
||||
polyLats[4] = bbox.minLat;
|
||||
polyLons[4] = bbox.minLon;
|
||||
break;
|
||||
case 1:
|
||||
// right triangle
|
||||
polyLats = new double[4];
|
||||
polyLons = new double[4];
|
||||
polyLats[0] = bbox.minLat;
|
||||
polyLons[0] = bbox.minLon;
|
||||
polyLats[1] = bbox.maxLat;
|
||||
polyLons[1] = bbox.minLon;
|
||||
polyLats[2] = bbox.maxLat;
|
||||
polyLons[2] = bbox.maxLon;
|
||||
polyLats[3] = bbox.minLat;
|
||||
polyLons[3] = bbox.minLon;
|
||||
break;
|
||||
default:
|
||||
// surprise me!
|
||||
double[][] res = surpriseMePolygon();
|
||||
polyLats = res[0];
|
||||
polyLons = res[1];
|
||||
break;
|
||||
final double[][] polygon;
|
||||
if (small) {
|
||||
polygon = GeoTestUtil.nextPolygonNear(originLat, originLon);
|
||||
} else {
|
||||
polygon = GeoTestUtil.nextPolygon();
|
||||
}
|
||||
|
||||
final double[] polyLats = polygon[0];
|
||||
final double[] polyLons = polygon[1];
|
||||
Query query = newPolygonQuery(FIELD_NAME, polyLats, polyLons);
|
||||
|
||||
if (VERBOSE) {
|
||||
|
@ -1153,7 +1033,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
} else if (Double.isNaN(lats[id])) {
|
||||
expected = false;
|
||||
} else {
|
||||
expected = polygonContainsPoint(polyLats, polyLons, lats[id], lons[id]);
|
||||
expected = GeoRelationUtils.pointInPolygon(polyLats, polyLons, lats[id], lons[id]);
|
||||
}
|
||||
|
||||
if (hits.get(docID) != expected) {
|
||||
|
@ -1186,7 +1066,14 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
}
|
||||
|
||||
public void testRectBoundariesAreInclusive() throws Exception {
|
||||
GeoRect rect = randomRect(random().nextBoolean(), false);
|
||||
GeoRect rect;
|
||||
// TODO: why this dateline leniency???
|
||||
while (true) {
|
||||
rect = randomRect(random().nextBoolean());
|
||||
if (rect.crossesDateline() == false) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Directory dir = newDirectory();
|
||||
IndexWriterConfig iwc = newIndexWriterConfig();
|
||||
RandomIndexWriter w = new RandomIndexWriter(random(), dir, iwc);
|
||||
|
@ -1319,7 +1206,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
public void testEquals() throws Exception {
|
||||
Query q1, q2;
|
||||
|
||||
GeoRect rect = randomRect(false, true);
|
||||
GeoRect 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);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.apache.lucene.spatial.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.RandomizedContext;
|
||||
|
@ -27,7 +28,6 @@ public class GeoTestUtil {
|
|||
public static double nextLatitude() {
|
||||
return -90 + 180.0 * random().nextDouble();
|
||||
}
|
||||
|
||||
|
||||
/** returns next pseudorandom longitude (anywhere) */
|
||||
public static double nextLongitude() {
|
||||
|
@ -68,6 +68,171 @@ public class GeoTestUtil {
|
|||
return normalizeLongitude(randomRangeMaybeSlightlyOutside(minLongitude, maxLongitude));
|
||||
}
|
||||
|
||||
/** returns next pseudorandom box: can cross the 180th meridian */
|
||||
public static GeoRect 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) {
|
||||
GeoUtils.checkLongitude(otherLongitude);
|
||||
GeoUtils.checkLongitude(otherLongitude);
|
||||
return nextBoxInternal(nextLatitudeNear(otherLatitude), nextLatitudeNear(otherLatitude),
|
||||
nextLongitudeNear(otherLongitude), nextLongitudeNear(otherLongitude), true);
|
||||
}
|
||||
|
||||
/** returns next pseudorandom polygon */
|
||||
public static double[][] nextPolygon() {
|
||||
if (random().nextBoolean()) {
|
||||
return surpriseMePolygon(null, null);
|
||||
}
|
||||
|
||||
GeoRect box = nextBoxInternal(nextLatitude(), nextLatitude(), nextLongitude(), nextLongitude(), false);
|
||||
if (random().nextBoolean()) {
|
||||
// box
|
||||
return boxPolygon(box);
|
||||
} else {
|
||||
// triangle
|
||||
return trianglePolygon(box);
|
||||
}
|
||||
}
|
||||
|
||||
/** returns next pseudorandom polygon, kinda close to {@code otherLatitude} and {@code otherLongitude} */
|
||||
public static double[][] nextPolygonNear(double otherLatitude, double otherLongitude) {
|
||||
if (random().nextBoolean()) {
|
||||
return surpriseMePolygon(otherLatitude, otherLongitude);
|
||||
}
|
||||
|
||||
GeoRect box = nextBoxInternal(nextLatitudeNear(otherLatitude), nextLatitudeNear(otherLatitude),
|
||||
nextLongitudeNear(otherLongitude), nextLongitudeNear(otherLongitude), false);
|
||||
if (random().nextBoolean()) {
|
||||
// box
|
||||
return boxPolygon(box);
|
||||
} else {
|
||||
// triangle
|
||||
return trianglePolygon(box);
|
||||
}
|
||||
}
|
||||
|
||||
private static GeoRect nextBoxInternal(double lat0, double lat1, double lon0, double lon1, boolean canCrossDateLine) {
|
||||
if (lat1 < lat0) {
|
||||
double x = lat0;
|
||||
lat0 = lat1;
|
||||
lat1 = x;
|
||||
}
|
||||
|
||||
if (canCrossDateLine == false && lon1 < lon0) {
|
||||
double x = lon0;
|
||||
lon0 = lon1;
|
||||
lon1 = x;
|
||||
}
|
||||
|
||||
return new GeoRect(lat0, lat1, lon0, lon1);
|
||||
}
|
||||
|
||||
private static double[][] boxPolygon(GeoRect box) {
|
||||
assert box.crossesDateline() == false;
|
||||
final double[] polyLats = new double[5];
|
||||
final double[] polyLons = new double[5];
|
||||
polyLats[0] = box.minLat;
|
||||
polyLons[0] = box.minLon;
|
||||
polyLats[1] = box.maxLat;
|
||||
polyLons[1] = box.minLon;
|
||||
polyLats[2] = box.maxLat;
|
||||
polyLons[2] = box.maxLon;
|
||||
polyLats[3] = box.minLat;
|
||||
polyLons[3] = box.maxLon;
|
||||
polyLats[4] = box.minLat;
|
||||
polyLons[4] = box.minLon;
|
||||
return new double[][] { polyLats, polyLons };
|
||||
}
|
||||
|
||||
private static double[][] trianglePolygon(GeoRect box) {
|
||||
assert box.crossesDateline() == false;
|
||||
final double[] polyLats = new double[4];
|
||||
final double[] polyLons = new double[4];
|
||||
polyLats[0] = box.minLat;
|
||||
polyLons[0] = box.minLon;
|
||||
polyLats[1] = box.maxLat;
|
||||
polyLons[1] = box.minLon;
|
||||
polyLats[2] = box.maxLat;
|
||||
polyLons[2] = box.maxLon;
|
||||
polyLats[3] = box.minLat;
|
||||
polyLons[3] = box.minLon;
|
||||
return new double[][] { polyLats, polyLons };
|
||||
}
|
||||
|
||||
/** Returns {polyLats, polyLons} double[] array */
|
||||
private static double[][] surpriseMePolygon(Double otherLatitude, Double otherLongitude) {
|
||||
// repeat until we get a poly that doesn't cross dateline:
|
||||
newPoly:
|
||||
while (true) {
|
||||
//System.out.println("\nPOLY ITER");
|
||||
final double centerLat;
|
||||
final double centerLon;
|
||||
if (otherLatitude == null) {
|
||||
centerLat = nextLatitude();
|
||||
centerLon = nextLongitude();
|
||||
} else {
|
||||
GeoUtils.checkLatitude(otherLatitude);
|
||||
GeoUtils.checkLongitude(otherLongitude);
|
||||
centerLat = nextLatitudeNear(otherLatitude);
|
||||
centerLon = nextLongitudeNear(otherLongitude);
|
||||
}
|
||||
|
||||
double radius = 0.1 + 20 * random().nextDouble();
|
||||
double radiusDelta = random().nextDouble();
|
||||
|
||||
ArrayList<Double> lats = new ArrayList<>();
|
||||
ArrayList<Double> lons = new ArrayList<>();
|
||||
double angle = 0.0;
|
||||
while (true) {
|
||||
angle += random().nextDouble()*40.0;
|
||||
//System.out.println(" angle " + angle);
|
||||
if (angle > 360) {
|
||||
break;
|
||||
}
|
||||
double len = radius * (1.0 - radiusDelta + radiusDelta * random().nextDouble());
|
||||
//System.out.println(" len=" + len);
|
||||
double lat = centerLat + len * Math.cos(Math.toRadians(angle));
|
||||
double lon = centerLon + len * Math.sin(Math.toRadians(angle));
|
||||
if (lon <= GeoUtils.MIN_LON_INCL || lon >= GeoUtils.MAX_LON_INCL) {
|
||||
// cannot cross dateline: try again!
|
||||
continue newPoly;
|
||||
}
|
||||
if (lat > 90) {
|
||||
// cross the north pole
|
||||
lat = 180 - lat;
|
||||
lon = 180 - lon;
|
||||
} else if (lat < -90) {
|
||||
// cross the south pole
|
||||
lat = -180 - lat;
|
||||
lon = 180 - lon;
|
||||
}
|
||||
if (lon <= GeoUtils.MIN_LON_INCL || lon >= GeoUtils.MAX_LON_INCL) {
|
||||
// cannot cross dateline: try again!
|
||||
continue newPoly;
|
||||
}
|
||||
lats.add(lat);
|
||||
lons.add(lon);
|
||||
|
||||
//System.out.println(" lat=" + lats.get(lats.size()-1) + " lon=" + lons.get(lons.size()-1));
|
||||
}
|
||||
|
||||
// close it
|
||||
lats.add(lats.get(0));
|
||||
lons.add(lons.get(0));
|
||||
|
||||
double[] latsArray = new double[lats.size()];
|
||||
double[] lonsArray = new double[lons.size()];
|
||||
for(int i=0;i<lats.size();i++) {
|
||||
latsArray[i] = lats.get(i);
|
||||
lonsArray[i] = lons.get(i);
|
||||
}
|
||||
return new double[][] {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);
|
||||
|
|
Loading…
Reference in New Issue