mirror of https://github.com/apache/lucene.git
Port RectIntersectionTestHelper (and TestLog and RandomizedShapeTest) from Spatial4j. Add Geo3dShapeTest, initially added GeoCircle; others are TBD. Test fails.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6196@1674845 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c3a4ee1c3b
commit
bf74c72eb2
|
@ -0,0 +1,87 @@
|
||||||
|
package org.apache.lucene.spatial.spatial4j;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 java.util.Random;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.RandomizedContext;
|
||||||
|
import com.carrotsearch.randomizedtesting.annotations.Seed;
|
||||||
|
import com.spatial4j.core.context.SpatialContext;
|
||||||
|
import com.spatial4j.core.distance.DistanceUtils;
|
||||||
|
import com.spatial4j.core.shape.Point;
|
||||||
|
import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle;
|
||||||
|
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint;
|
||||||
|
import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS;
|
||||||
|
|
||||||
|
public class Geo3dShapeTest extends RandomizedShapeTest {
|
||||||
|
@Rule
|
||||||
|
public final TestLog testLog = TestLog.instance;
|
||||||
|
|
||||||
|
static Random random() {
|
||||||
|
return RandomizedContext.current().getRandom();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
ctx = SpatialContext.GEO;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Seed("FAD1BAB12B6DCCFE")
|
||||||
|
public void testGeoCircleRect() {
|
||||||
|
new RectIntersectionTestHelper<Geo3dShape>(ctx) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Geo3dShape generateRandomShape(Point nearP) {
|
||||||
|
// Circles
|
||||||
|
while (true) {
|
||||||
|
final int circleRadius = random().nextInt(180);
|
||||||
|
final Point point = nearP;
|
||||||
|
try {
|
||||||
|
final GeoShape shape = new GeoCircle(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
|
||||||
|
circleRadius * DEGREES_TO_RADIANS);
|
||||||
|
return new Geo3dShape(shape, ctx);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
// This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where
|
||||||
|
// the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Point randomPointInEmptyShape(Geo3dShape shape) {
|
||||||
|
GeoPoint geoPoint = ((GeoCircle)shape.shape).center;
|
||||||
|
return geoPointToSpatial4jPoint(geoPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
}.testRelateWithRectangle();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO PORT OTHER TESTS
|
||||||
|
|
||||||
|
|
||||||
|
private Point geoPointToSpatial4jPoint(GeoPoint geoPoint) {
|
||||||
|
return ctx.makePoint(geoPoint.x * DistanceUtils.RADIANS_TO_DEGREES,
|
||||||
|
geoPoint.y * DistanceUtils.RADIANS_TO_DEGREES);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
package org.apache.lucene.spatial.spatial4j;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 com.carrotsearch.randomizedtesting.RandomizedTest;
|
||||||
|
import com.spatial4j.core.context.SpatialContext;
|
||||||
|
import com.spatial4j.core.distance.DistanceUtils;
|
||||||
|
import com.spatial4j.core.shape.Circle;
|
||||||
|
import com.spatial4j.core.shape.Point;
|
||||||
|
import com.spatial4j.core.shape.Rectangle;
|
||||||
|
import com.spatial4j.core.shape.Shape;
|
||||||
|
import com.spatial4j.core.shape.SpatialRelation;
|
||||||
|
import com.spatial4j.core.shape.impl.Range;
|
||||||
|
|
||||||
|
import static com.spatial4j.core.shape.SpatialRelation.CONTAINS;
|
||||||
|
import static com.spatial4j.core.shape.SpatialRelation.WITHIN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A base test class with utility methods to help test shapes.
|
||||||
|
* Extends from RandomizedTest.
|
||||||
|
*/
|
||||||
|
public abstract class RandomizedShapeTest extends RandomizedTest {
|
||||||
|
|
||||||
|
protected static final double EPS = 10e-9;
|
||||||
|
|
||||||
|
protected SpatialContext ctx;//needs to be set ASAP
|
||||||
|
|
||||||
|
/** Used to reduce the space of numbers to increase the likelihood that
|
||||||
|
* random numbers become equivalent, and thus trigger different code paths.
|
||||||
|
* Also makes some random shapes easier to manually examine.
|
||||||
|
*/
|
||||||
|
protected final double DIVISIBLE = 2;// even coordinates; (not always used)
|
||||||
|
|
||||||
|
protected RandomizedShapeTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public RandomizedShapeTest(SpatialContext ctx) {
|
||||||
|
this.ctx = ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkShapesImplementEquals( Class[] classes ) {
|
||||||
|
for( Class clazz : classes ) {
|
||||||
|
try {
|
||||||
|
clazz.getDeclaredMethod( "equals", Object.class );
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Shape needs to define 'equals' : " + clazz.getName());
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
clazz.getDeclaredMethod( "hashCode" );
|
||||||
|
} catch (Exception e) {
|
||||||
|
fail("Shape needs to define 'hashCode' : " + clazz.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//These few norm methods normalize the arguments for creating a shape to
|
||||||
|
// account for the dateline. Some tests loop past the dateline or have offsets
|
||||||
|
// that go past it and it's easier to have them coded that way and correct for
|
||||||
|
// it here. These norm methods should be used when needed, not frivolously.
|
||||||
|
|
||||||
|
protected double normX(double x) {
|
||||||
|
return ctx.isGeo() ? DistanceUtils.normLonDEG(x) : x;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double normY(double y) {
|
||||||
|
return ctx.isGeo() ? DistanceUtils.normLatDEG(y) : y;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Rectangle makeNormRect(double minX, double maxX, double minY, double maxY) {
|
||||||
|
if (ctx.isGeo()) {
|
||||||
|
if (Math.abs(maxX - minX) >= 360) {
|
||||||
|
minX = -180;
|
||||||
|
maxX = 180;
|
||||||
|
} else {
|
||||||
|
minX = DistanceUtils.normLonDEG(minX);
|
||||||
|
maxX = DistanceUtils.normLonDEG(maxX);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (maxX < minX) {
|
||||||
|
double t = minX;
|
||||||
|
minX = maxX;
|
||||||
|
maxX = t;
|
||||||
|
}
|
||||||
|
minX = boundX(minX, ctx.getWorldBounds());
|
||||||
|
maxX = boundX(maxX, ctx.getWorldBounds());
|
||||||
|
}
|
||||||
|
if (maxY < minY) {
|
||||||
|
double t = minY;
|
||||||
|
minY = maxY;
|
||||||
|
maxY = t;
|
||||||
|
}
|
||||||
|
minY = boundY(minY, ctx.getWorldBounds());
|
||||||
|
maxY = boundY(maxY, ctx.getWorldBounds());
|
||||||
|
return ctx.makeRectangle(minX, maxX, minY, maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double divisible(double v, double divisible) {
|
||||||
|
return (int) (Math.round(v / divisible) * divisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected double divisible(double v) {
|
||||||
|
return divisible(v, DIVISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** reset()'s p, and confines to world bounds. Might not be divisible if
|
||||||
|
* the world bound isn't divisible too.
|
||||||
|
*/
|
||||||
|
protected Point divisible(Point p) {
|
||||||
|
Rectangle bounds = ctx.getWorldBounds();
|
||||||
|
double newX = boundX( divisible(p.getX()), bounds );
|
||||||
|
double newY = boundY( divisible(p.getY()), bounds );
|
||||||
|
p.reset(newX, newY);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double boundX(double i, Rectangle bounds) {
|
||||||
|
return bound(i, bounds.getMinX(), bounds.getMaxX());
|
||||||
|
}
|
||||||
|
|
||||||
|
static double boundY(double i, Rectangle bounds) {
|
||||||
|
return bound(i, bounds.getMinY(), bounds.getMaxY());
|
||||||
|
}
|
||||||
|
|
||||||
|
static double bound(double i, double min, double max) {
|
||||||
|
if (i < min) return min;
|
||||||
|
if (i > max) return max;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertRelation(SpatialRelation expected, Shape a, Shape b) {
|
||||||
|
assertRelation(null, expected, a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertRelation(String msg, SpatialRelation expected, Shape a, Shape b) {
|
||||||
|
_assertIntersect(msg, expected, a, b);
|
||||||
|
//check flipped a & b w/ transpose(), while we're at it
|
||||||
|
_assertIntersect(msg, expected.transpose(), b, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void _assertIntersect(String msg, SpatialRelation expected, Shape a, Shape b) {
|
||||||
|
SpatialRelation sect = a.relate(b);
|
||||||
|
if (sect == expected)
|
||||||
|
return;
|
||||||
|
msg = ((msg == null) ? "" : msg+"\r") + a +" intersect "+b;
|
||||||
|
if (expected == WITHIN || expected == CONTAINS) {
|
||||||
|
if (a.getClass().equals(b.getClass())) // they are the same shape type
|
||||||
|
assertEquals(msg,a,b);
|
||||||
|
else {
|
||||||
|
//they are effectively points or lines that are the same location
|
||||||
|
assertTrue(msg,!a.hasArea());
|
||||||
|
assertTrue(msg,!b.hasArea());
|
||||||
|
|
||||||
|
Rectangle aBBox = a.getBoundingBox();
|
||||||
|
Rectangle bBBox = b.getBoundingBox();
|
||||||
|
if (aBBox.getHeight() == 0 && bBBox.getHeight() == 0
|
||||||
|
&& (aBBox.getMaxY() == 90 && bBBox.getMaxY() == 90
|
||||||
|
|| aBBox.getMinY() == -90 && bBBox.getMinY() == -90))
|
||||||
|
;//== a point at the pole
|
||||||
|
else
|
||||||
|
assertEquals(msg, aBBox, bBBox);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assertEquals(msg,expected,sect);//always fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertEqualsRatio(String msg, double expected, double actual) {
|
||||||
|
double delta = Math.abs(actual - expected);
|
||||||
|
double base = Math.min(actual, expected);
|
||||||
|
double deltaRatio = base==0 ? delta : Math.min(delta,delta / base);
|
||||||
|
assertEquals(msg,0,deltaRatio, EPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int randomIntBetweenDivisible(int start, int end) {
|
||||||
|
return randomIntBetweenDivisible(start, end, (int)DIVISIBLE);
|
||||||
|
}
|
||||||
|
/** Returns a random integer between [start, end]. Integers between must be divisible by the 3rd argument. */
|
||||||
|
protected int randomIntBetweenDivisible(int start, int end, int divisible) {
|
||||||
|
// DWS: I tested this
|
||||||
|
int divisStart = (int) Math.ceil( (start+1) / (double)divisible );
|
||||||
|
int divisEnd = (int) Math.floor( (end-1) / (double)divisible );
|
||||||
|
int divisRange = Math.max(0,divisEnd - divisStart + 1);
|
||||||
|
int r = randomInt(1 + divisRange);//remember that '0' is counted
|
||||||
|
if (r == 0)
|
||||||
|
return start;
|
||||||
|
if (r == 1)
|
||||||
|
return end;
|
||||||
|
return (r-2 + divisStart)*divisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Rectangle randomRectangle(Point nearP) {
|
||||||
|
Rectangle bounds = ctx.getWorldBounds();
|
||||||
|
if (nearP == null)
|
||||||
|
nearP = randomPointIn(bounds);
|
||||||
|
|
||||||
|
Range xRange = randomRange(rarely() ? 0 : nearP.getX(), Range.xRange(bounds, ctx));
|
||||||
|
Range yRange = randomRange(rarely() ? 0 : nearP.getY(), Range.yRange(bounds, ctx));
|
||||||
|
|
||||||
|
return makeNormRect(
|
||||||
|
divisible(xRange.getMin()),
|
||||||
|
divisible(xRange.getMax()),
|
||||||
|
divisible(yRange.getMin()),
|
||||||
|
divisible(yRange.getMax()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private Range randomRange(double near, Range bounds) {
|
||||||
|
double mid = near + randomGaussian() * bounds.getWidth() / 6;
|
||||||
|
double width = Math.abs(randomGaussian()) * bounds.getWidth() / 6;//1/3rd
|
||||||
|
return new Range(mid - width / 2, mid + width / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double randomGaussianZeroTo(double max) {
|
||||||
|
if (max == 0)
|
||||||
|
return max;
|
||||||
|
assert max > 0;
|
||||||
|
double r;
|
||||||
|
do {
|
||||||
|
r = Math.abs(randomGaussian()) * (max * 0.50);
|
||||||
|
} while (r > max);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Rectangle randomRectangle(int divisible) {
|
||||||
|
double rX = randomIntBetweenDivisible(-180, 180, divisible);
|
||||||
|
double rW = randomIntBetweenDivisible(0, 360, divisible);
|
||||||
|
double rY1 = randomIntBetweenDivisible(-90, 90, divisible);
|
||||||
|
double rY2 = randomIntBetweenDivisible(-90, 90, divisible);
|
||||||
|
double rYmin = Math.min(rY1,rY2);
|
||||||
|
double rYmax = Math.max(rY1,rY2);
|
||||||
|
if (rW > 0 && rX == 180)
|
||||||
|
rX = -180;
|
||||||
|
return makeNormRect(rX, rX + rW, rYmin, rYmax);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Point randomPoint() {
|
||||||
|
return randomPointIn(ctx.getWorldBounds());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Point randomPointIn(Circle c) {
|
||||||
|
double d = c.getRadius() * randomDouble();
|
||||||
|
double angleDEG = 360 * randomDouble();
|
||||||
|
Point p = ctx.getDistCalc().pointOnBearing(c.getCenter(), d, angleDEG, ctx, null);
|
||||||
|
assertEquals(CONTAINS,c.relate(p));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Point randomPointIn(Rectangle r) {
|
||||||
|
double x = r.getMinX() + randomDouble()*r.getWidth();
|
||||||
|
double y = r.getMinY() + randomDouble()*r.getHeight();
|
||||||
|
x = normX(x);
|
||||||
|
y = normY(y);
|
||||||
|
Point p = ctx.makePoint(x,y);
|
||||||
|
assertEquals(CONTAINS,r.relate(p));
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Point randomPointIn(Shape shape) {
|
||||||
|
if (!shape.hasArea())// or try the center?
|
||||||
|
throw new UnsupportedOperationException("Need area to define shape!");
|
||||||
|
Rectangle bbox = shape.getBoundingBox();
|
||||||
|
Point p;
|
||||||
|
do {
|
||||||
|
p = randomPointIn(bbox);
|
||||||
|
} while (!bbox.relate(p).intersects());
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
package org.apache.lucene.spatial.spatial4j;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 com.spatial4j.core.context.SpatialContext;
|
||||||
|
import com.spatial4j.core.shape.Point;
|
||||||
|
import com.spatial4j.core.shape.Rectangle;
|
||||||
|
import com.spatial4j.core.shape.Shape;
|
||||||
|
import com.spatial4j.core.shape.SpatialRelation;
|
||||||
|
import com.spatial4j.core.shape.impl.InfBufLine;
|
||||||
|
import com.spatial4j.core.shape.impl.PointImpl;
|
||||||
|
|
||||||
|
import static com.spatial4j.core.shape.SpatialRelation.CONTAINS;
|
||||||
|
import static com.spatial4j.core.shape.SpatialRelation.DISJOINT;
|
||||||
|
|
||||||
|
public abstract class RectIntersectionTestHelper<S extends Shape> extends RandomizedShapeTest {
|
||||||
|
|
||||||
|
public RectIntersectionTestHelper(SpatialContext ctx) {
|
||||||
|
super(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract S generateRandomShape(Point nearP);
|
||||||
|
|
||||||
|
protected abstract Point randomPointInEmptyShape(S shape);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected Point randomPointIn(Shape shape) {
|
||||||
|
if (!shape.hasArea())
|
||||||
|
return randomPointInEmptyShape((S) shape);
|
||||||
|
return super.randomPointIn(shape);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testRelateWithRectangle() {
|
||||||
|
//counters for the different intersection cases
|
||||||
|
int i_C = 0, i_I = 0, i_W = 0, i_D = 0, i_bboxD = 0;
|
||||||
|
int laps = 0;
|
||||||
|
final int MINLAPSPERCASE = scaledRandomIntBetween(20, 200);
|
||||||
|
while(i_C < MINLAPSPERCASE || i_I < MINLAPSPERCASE || i_W < MINLAPSPERCASE
|
||||||
|
|| i_D < MINLAPSPERCASE || i_bboxD < MINLAPSPERCASE) {
|
||||||
|
laps++;
|
||||||
|
|
||||||
|
TestLog.clear();
|
||||||
|
|
||||||
|
Point nearP = randomPointIn(ctx.getWorldBounds());
|
||||||
|
|
||||||
|
S s = generateRandomShape(nearP);
|
||||||
|
|
||||||
|
Rectangle r = randomRectangle(s.getBoundingBox().getCenter());
|
||||||
|
|
||||||
|
SpatialRelation ic = s.relate(r);
|
||||||
|
|
||||||
|
TestLog.log("S-R Rel: {}, Shape {}, Rectangle {}", ic, s, r);
|
||||||
|
|
||||||
|
try {
|
||||||
|
int MAX_TRIES = scaledRandomIntBetween(10, 100);
|
||||||
|
switch (ic) {
|
||||||
|
case CONTAINS:
|
||||||
|
i_C++;
|
||||||
|
for (int j = 0; j < MAX_TRIES; j++) {
|
||||||
|
Point p = randomPointIn(r);
|
||||||
|
assertRelation(null, CONTAINS, s, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WITHIN:
|
||||||
|
i_W++;
|
||||||
|
for (int j = 0; j < MAX_TRIES; j++) {
|
||||||
|
Point p = randomPointIn(s);
|
||||||
|
assertRelation(null, CONTAINS, r, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DISJOINT:
|
||||||
|
if (!s.getBoundingBox().relate(r).intersects()) {//bboxes are disjoint
|
||||||
|
i_bboxD++;
|
||||||
|
if (i_bboxD > MINLAPSPERCASE)
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
i_D++;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < MAX_TRIES; j++) {
|
||||||
|
Point p = randomPointIn(r);
|
||||||
|
assertRelation(null, DISJOINT, s, p);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case INTERSECTS:
|
||||||
|
i_I++;
|
||||||
|
SpatialRelation pointR = null;//set once
|
||||||
|
Rectangle randomPointSpace = null;
|
||||||
|
MAX_TRIES = 1000;//give many attempts
|
||||||
|
for (int j = 0; j < MAX_TRIES; j++) {
|
||||||
|
Point p;
|
||||||
|
if (j < 4) {
|
||||||
|
p = new PointImpl(0, 0, ctx);
|
||||||
|
InfBufLine.cornerByQuadrant(r, j + 1, p);
|
||||||
|
} else {
|
||||||
|
if (randomPointSpace == null) {
|
||||||
|
if (pointR == DISJOINT) {
|
||||||
|
randomPointSpace = intersectRects(r,s.getBoundingBox());
|
||||||
|
} else {//CONTAINS
|
||||||
|
randomPointSpace = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p = randomPointIn(randomPointSpace);
|
||||||
|
}
|
||||||
|
SpatialRelation pointRNew = s.relate(p);
|
||||||
|
if (pointR == null) {
|
||||||
|
pointR = pointRNew;
|
||||||
|
} else if (pointR != pointRNew) {
|
||||||
|
break;
|
||||||
|
} else if (j >= MAX_TRIES) {
|
||||||
|
//TODO consider logging instead of failing
|
||||||
|
fail("Tried intersection brute-force too many times without success");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: fail(""+ic);
|
||||||
|
}
|
||||||
|
} catch (AssertionError e) {
|
||||||
|
onAssertFail(e, s, r, ic);
|
||||||
|
}
|
||||||
|
if (laps > MINLAPSPERCASE * 1000)
|
||||||
|
fail("Did not find enough intersection cases in a reasonable number" +
|
||||||
|
" of random attempts. CWIDbD: "+i_C+","+i_W+","+i_I+","+i_D+","+i_bboxD
|
||||||
|
+ " Laps exceeded "+MINLAPSPERCASE * 1000);
|
||||||
|
}
|
||||||
|
System.out.println("Laps: "+laps + " CWIDbD: "+i_C+","+i_W+","+i_I+","+i_D+","+i_bboxD);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onAssertFail(AssertionError e, S s, Rectangle r, SpatialRelation ic) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rectangle intersectRects(Rectangle r1, Rectangle r2) {
|
||||||
|
assert r1.relate(r2).intersects();
|
||||||
|
final double minX, maxX;
|
||||||
|
if (r1.relateXRange(r2.getMinX(),r2.getMinX()).intersects()) {
|
||||||
|
minX = r2.getMinX();
|
||||||
|
} else {
|
||||||
|
minX = r1.getMinX();
|
||||||
|
}
|
||||||
|
if (r1.relateXRange(r2.getMaxX(),r2.getMaxX()).intersects()) {
|
||||||
|
maxX = r2.getMaxX();
|
||||||
|
} else {
|
||||||
|
maxX = r1.getMaxX();
|
||||||
|
}
|
||||||
|
final double minY, maxY;
|
||||||
|
if (r1.relateYRange(r2.getMinY(),r2.getMinY()).intersects()) {
|
||||||
|
minY = r2.getMinY();
|
||||||
|
} else {
|
||||||
|
minY = r1.getMinY();
|
||||||
|
}
|
||||||
|
if (r1.relateYRange(r2.getMaxY(),r2.getMaxY()).intersects()) {
|
||||||
|
maxY = r2.getMaxY();
|
||||||
|
} else {
|
||||||
|
maxY = r1.getMaxY();
|
||||||
|
}
|
||||||
|
return ctx.makeRectangle(minX, maxX, minY, maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
package org.apache.lucene.spatial.spatial4j;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility logger for tests in which log statements are logged following
|
||||||
|
* test failure only. Add this to a JUnit based test class with a {@link org.junit.Rule}
|
||||||
|
* annotation.
|
||||||
|
*/
|
||||||
|
public class TestLog extends TestRuleAdapter {
|
||||||
|
|
||||||
|
//TODO does this need to be threadsafe (such as via thread-local state)?
|
||||||
|
private static ArrayList<LogEntry> logStack = new ArrayList<LogEntry>();
|
||||||
|
private static final int MAX_LOGS = 1000;
|
||||||
|
|
||||||
|
public static final TestLog instance = new TestLog();
|
||||||
|
|
||||||
|
private TestLog() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void before() throws Throwable {
|
||||||
|
logStack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void afterAlways(List<Throwable> errors) throws Throwable {
|
||||||
|
if (!errors.isEmpty())
|
||||||
|
logThenClear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logThenClear() {
|
||||||
|
for (LogEntry entry : logStack) {
|
||||||
|
//no SLF4J in Lucene... fallback to this
|
||||||
|
if (entry.args != null && entry.args.length > 0) {
|
||||||
|
System.out.println(entry.msg + " " + Arrays.asList(entry.args) + "(no slf4j subst; sorry)");
|
||||||
|
} else {
|
||||||
|
System.out.println(entry.msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logStack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear() {
|
||||||
|
logStack.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enqueues a log message with substitution arguments ala SLF4J (i.e. {} syntax).
|
||||||
|
* If the test fails then it'll be logged then, otherwise it'll be forgotten.
|
||||||
|
*/
|
||||||
|
public static void log(String msg, Object... args) {
|
||||||
|
if (logStack.size() > MAX_LOGS) {
|
||||||
|
throw new RuntimeException("Too many log statements: "+logStack.size() + " > "+MAX_LOGS);
|
||||||
|
}
|
||||||
|
LogEntry entry = new LogEntry();
|
||||||
|
entry.msg = msg;
|
||||||
|
entry.args = args;
|
||||||
|
logStack.add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LogEntry { String msg; Object[] args; }
|
||||||
|
}
|
Loading…
Reference in New Issue