/*******************************************************************************
* Copyright (c) 2015 Voyager Search and MITRE
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Apache License, Version 2.0 which
* accompanies this distribution and is available at
* http://www.apache.org/licenses/LICENSE-2.0.txt
******************************************************************************/
package org.locationtech.spatial4j.shape;
import org.locationtech.spatial4j.TestLog;
import org.locationtech.spatial4j.context.SpatialContext;
import org.locationtech.spatial4j.shape.impl.InfBufLine;
import org.locationtech.spatial4j.shape.impl.PointImpl;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.locationtech.spatial4j.shape.SpatialRelation.CONTAINS;
import static org.locationtech.spatial4j.shape.SpatialRelation.DISJOINT;
public abstract class RectIntersectionTestHelper<S extends Shape> extends RandomizedShapeTest {
public RectIntersectionTestHelper(SpatialContext ctx) {
super(ctx);
}
/** Override to return true if generateRandomShape is essentially a Rectangle. */
protected boolean isRandomShapeRectangular() {
return false;
}
protected abstract S generateRandomShape(Point nearP);
/** shape has no area; return a point in it */
protected abstract Point randomPointInEmptyShape(S shape);
// Minimum distribution of relationships
// Each shape has different characteristics, so we don't expect (for instance) shapes that
// are likely to be long and thin to contain as many rectangles as those that
// short and fat.
protected int getContainsMinimum(int laps) {
return laps/1000;
}
protected int getIntersectsMinimum(int laps) {
return laps/1000;
}
protected int getWithinMinimum(int laps) {
return laps/1000;
}
protected int getDisjointMinimum(int laps) {
return laps/1000;
}
protected int getBoundingMinimum(int laps) {
return laps/1000;
}
@SuppressWarnings("unchecked")
@Override
protected Point randomPointInOrNull(Shape shape) {
if (!shape.hasArea()) {
final Point pt = randomPointInEmptyShape((S) shape);
assert shape.relate(pt).intersects() : "faulty randomPointInEmptyShape";
return pt;
}
return super.randomPointInOrNull(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 MINLAPS = scaledRandomIntBetween(20000, 200000);
while(i_C < getContainsMinimum(MINLAPS) || i_I < getIntersectsMinimum(MINLAPS) || i_W < getWithinMinimum(MINLAPS)
|| (!isRandomShapeRectangular() && i_D < getDisjointMinimum(MINLAPS)) || i_bboxD < getBoundingMinimum(MINLAPS)) {
laps++;
TestLog.clear();
if (laps > MINLAPS) {
fail("Did not find enough contains/within/intersection/disjoint/bounds cases in a reasonable number" +
" of random attempts. CWIDbD: " +
i_C + "("+getContainsMinimum(MINLAPS)+")," +
i_W + "("+getWithinMinimum(MINLAPS)+")," +
i_I + "("+getIntersectsMinimum(MINLAPS)+")," +
i_D + "("+getDisjointMinimum(MINLAPS)+")," +
i_bboxD + "("+getBoundingMinimum(MINLAPS)+")"
+ " Laps exceeded " + MINLAPS);
}
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);
if (ic != DISJOINT) {
assertTrue("if not disjoint then the shape's bbox shouldn't be disjoint",
s.getBoundingBox().relate(r).intersects());
}
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 = randomPointInOrNull(s);
if (p == null) {//couldn't find a random point in shape
break;
}
assertRelation(null, CONTAINS, r, p);
}
break;
case DISJOINT:
if (!s.getBoundingBox().relate(r).intersects()) {//bboxes are disjoint
i_bboxD++;
if (i_bboxD >= getBoundingMinimum(MINLAPS))
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;
}
}
break;
default: fail(""+ic);
} // switch
} catch (AssertionError e) {
onAssertFail(e, s, r, ic);
}
} // while loop
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);
}
}