mirror of https://github.com/apache/lucene.git
Improved comments and readability of this test.
And used Spatial4j ShapeCollection to reduce some lines of code. git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1568001 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b1658e5d7c
commit
6475bda7f5
|
@ -18,11 +18,10 @@ package org.apache.lucene.spatial.prefix;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||||
import com.spatial4j.core.context.SpatialContext;
|
|
||||||
import com.spatial4j.core.context.SpatialContextFactory;
|
import com.spatial4j.core.context.SpatialContextFactory;
|
||||||
import com.spatial4j.core.shape.Point;
|
|
||||||
import com.spatial4j.core.shape.Rectangle;
|
import com.spatial4j.core.shape.Rectangle;
|
||||||
import com.spatial4j.core.shape.Shape;
|
import com.spatial4j.core.shape.Shape;
|
||||||
|
import com.spatial4j.core.shape.ShapeCollection;
|
||||||
import com.spatial4j.core.shape.SpatialRelation;
|
import com.spatial4j.core.shape.SpatialRelation;
|
||||||
import com.spatial4j.core.shape.impl.RectangleImpl;
|
import com.spatial4j.core.shape.impl.RectangleImpl;
|
||||||
import org.apache.lucene.document.Document;
|
import org.apache.lucene.document.Document;
|
||||||
|
@ -41,6 +40,7 @@ import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -200,7 +200,7 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
final boolean biasContains = (operation == SpatialOperation.Contains);
|
final boolean biasContains = (operation == SpatialOperation.Contains);
|
||||||
|
|
||||||
Map<String, Shape> indexedShapes = new LinkedHashMap<String, Shape>();
|
Map<String, Shape> indexedShapes = new LinkedHashMap<String, Shape>();
|
||||||
Map<String, Shape> indexedShapesGS = new LinkedHashMap<String, Shape>();
|
Map<String, Shape> indexedShapesGS = new LinkedHashMap<String, Shape>();//grid snapped
|
||||||
final int numIndexedShapes = randomIntBetween(1, 6);
|
final int numIndexedShapes = randomIntBetween(1, 6);
|
||||||
for (int i = 0; i < numIndexedShapes; i++) {
|
for (int i = 0; i < numIndexedShapes; i++) {
|
||||||
String id = "" + i;
|
String id = "" + i;
|
||||||
|
@ -210,7 +210,7 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
if (R == 0) {//1 in 12
|
if (R == 0) {//1 in 12
|
||||||
indexedShape = null; //no shape for this doc
|
indexedShape = null; //no shape for this doc
|
||||||
indexedShapeGS = null;
|
indexedShapeGS = null;
|
||||||
} else if (R % 3 == 0) {//4-1 in 12
|
} else if (R % 3 == 0) {//4 in 12 (0,3,6,9)
|
||||||
//comprised of more than one shape
|
//comprised of more than one shape
|
||||||
Rectangle shape1 = randomRectangle();
|
Rectangle shape1 = randomRectangle();
|
||||||
Rectangle shape2 = randomRectangle();
|
Rectangle shape2 = randomRectangle();
|
||||||
|
@ -221,6 +221,7 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
indexedShape = randomRectangle();
|
indexedShape = randomRectangle();
|
||||||
indexedShapeGS = gridSnap(indexedShape);
|
indexedShapeGS = gridSnap(indexedShape);
|
||||||
}
|
}
|
||||||
|
//TODO sometimes index a point. Need to fix LUCENE-4978 first though.
|
||||||
indexedShapes.put(id, indexedShape);
|
indexedShapes.put(id, indexedShape);
|
||||||
indexedShapesGS.put(id, indexedShapeGS);
|
indexedShapesGS.put(id, indexedShapeGS);
|
||||||
|
|
||||||
|
@ -230,6 +231,7 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
commit();//intermediate commit, produces extra segments
|
commit();//intermediate commit, produces extra segments
|
||||||
|
|
||||||
}
|
}
|
||||||
|
//delete some documents randomly
|
||||||
Iterator<String> idIter = indexedShapes.keySet().iterator();
|
Iterator<String> idIter = indexedShapes.keySet().iterator();
|
||||||
while (idIter.hasNext()) {
|
while (idIter.hasNext()) {
|
||||||
String id = idIter.next();
|
String id = idIter.next();
|
||||||
|
@ -246,39 +248,44 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
for (int i = 0; i < numQueryShapes; i++) {
|
for (int i = 0; i < numQueryShapes; i++) {
|
||||||
int scanLevel = randomInt(grid.getMaxLevels());
|
int scanLevel = randomInt(grid.getMaxLevels());
|
||||||
((RecursivePrefixTreeStrategy) strategy).setPrefixGridScanLevel(scanLevel);
|
((RecursivePrefixTreeStrategy) strategy).setPrefixGridScanLevel(scanLevel);
|
||||||
|
|
||||||
final Shape queryShape = randomRectangle();
|
final Shape queryShape = randomRectangle();
|
||||||
|
|
||||||
final boolean DISJOINT = operation.equals(SpatialOperation.IsDisjointTo);
|
final boolean opIsDisjoint = operation == SpatialOperation.IsDisjointTo;
|
||||||
|
|
||||||
//Generate truth via brute force:
|
//Generate truth via brute force:
|
||||||
// We really try to ensure true-positive matches (if the predicate on the raw shapes match
|
// We ensure true-positive matches (if the predicate on the raw shapes match
|
||||||
// then the search should find those same matches).
|
// then the search should find those same matches).
|
||||||
// approximations, false-positive matches
|
// approximations, false-positive matches
|
||||||
Set <String> expectedIds = new LinkedHashSet<String>();//true-positives
|
Set<String> expectedIds = new LinkedHashSet<String>();//true-positives
|
||||||
Set<String> secondaryIds = new LinkedHashSet<String>();//false-positives (unless disjoint)
|
Set<String> secondaryIds = new LinkedHashSet<String>();//false-positives (unless disjoint)
|
||||||
for (Map.Entry<String, Shape> entry : indexedShapes.entrySet()) {
|
for (Map.Entry<String, Shape> entry : indexedShapes.entrySet()) {
|
||||||
|
String id = entry.getKey();
|
||||||
Shape indexedShapeCompare = entry.getValue();
|
Shape indexedShapeCompare = entry.getValue();
|
||||||
if (indexedShapeCompare == null)
|
if (indexedShapeCompare == null)
|
||||||
continue;
|
continue;
|
||||||
Shape queryShapeCompare = queryShape;
|
Shape queryShapeCompare = queryShape;
|
||||||
String id = entry.getKey();
|
|
||||||
if (operation.evaluate(indexedShapeCompare, queryShapeCompare)) {
|
if (operation.evaluate(indexedShapeCompare, queryShapeCompare)) {
|
||||||
expectedIds.add(id);
|
expectedIds.add(id);
|
||||||
if (DISJOINT) {
|
if (opIsDisjoint) {
|
||||||
//if no longer intersect after buffering them, for disjoint, remember this
|
//if no longer intersect after buffering them, for disjoint, remember this
|
||||||
indexedShapeCompare = indexedShapesGS.get(entry.getKey());
|
indexedShapeCompare = indexedShapesGS.get(id);
|
||||||
queryShapeCompare = gridSnap(queryShape);
|
queryShapeCompare = gridSnap(queryShape);
|
||||||
if (!operation.evaluate(indexedShapeCompare, queryShapeCompare))
|
if (!operation.evaluate(indexedShapeCompare, queryShapeCompare))
|
||||||
secondaryIds.add(id);
|
secondaryIds.add(id);
|
||||||
}
|
}
|
||||||
} else if (!DISJOINT) {
|
} else if (!opIsDisjoint) {
|
||||||
//buffer either the indexed or query shape (via gridSnap) and try again
|
//buffer either the indexed or query shape (via gridSnap) and try again
|
||||||
if (operation.equals(SpatialOperation.Intersects)) {
|
if (operation == SpatialOperation.Intersects) {
|
||||||
indexedShapeCompare = indexedShapesGS.get(entry.getKey());
|
indexedShapeCompare = indexedShapesGS.get(id);
|
||||||
queryShapeCompare = gridSnap(queryShape);
|
queryShapeCompare = gridSnap(queryShape);
|
||||||
} else if (operation.equals(SpatialOperation.Contains)) {
|
//TODO Unfortunately, grid-snapping both can result in intersections that otherwise
|
||||||
indexedShapeCompare = indexedShapesGS.get(entry.getKey());
|
// wouldn't happen when the grids are adjacent. Not a big deal but our test is just a
|
||||||
} else if (operation.equals(SpatialOperation.IsWithin)) {
|
// bit more lenient.
|
||||||
|
} else if (operation == SpatialOperation.Contains) {
|
||||||
|
indexedShapeCompare = indexedShapesGS.get(id);
|
||||||
|
} else if (operation == SpatialOperation.IsWithin) {
|
||||||
queryShapeCompare = gridSnap(queryShape);
|
queryShapeCompare = gridSnap(queryShape);
|
||||||
}
|
}
|
||||||
if (operation.evaluate(indexedShapeCompare, queryShapeCompare))
|
if (operation.evaluate(indexedShapeCompare, queryShapeCompare))
|
||||||
|
@ -294,11 +301,11 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
for (SearchResult result : got.results) {
|
for (SearchResult result : got.results) {
|
||||||
String id = result.getId();
|
String id = result.getId();
|
||||||
boolean removed = remainingExpectedIds.remove(id);
|
boolean removed = remainingExpectedIds.remove(id);
|
||||||
if (!removed && (!DISJOINT && !secondaryIds.contains(id))) {
|
if (!removed && (!opIsDisjoint && !secondaryIds.contains(id))) {
|
||||||
fail("Shouldn't match", id, indexedShapes, indexedShapesGS, queryShape);
|
fail("Shouldn't match", id, indexedShapes, indexedShapesGS, queryShape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DISJOINT)
|
if (opIsDisjoint)
|
||||||
remainingExpectedIds.removeAll(secondaryIds);
|
remainingExpectedIds.removeAll(secondaryIds);
|
||||||
if (!remainingExpectedIds.isEmpty()) {
|
if (!remainingExpectedIds.isEmpty()) {
|
||||||
String id = remainingExpectedIds.iterator().next();
|
String id = remainingExpectedIds.iterator().next();
|
||||||
|
@ -327,30 +334,27 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
List<Cell> cells = grid.getCells(snapMe, detailLevel, false, true);
|
List<Cell> cells = grid.getCells(snapMe, detailLevel, false, true);
|
||||||
|
|
||||||
//calc bounding box of cells.
|
//calc bounding box of cells.
|
||||||
double minX = Double.POSITIVE_INFINITY, maxX = Double.NEGATIVE_INFINITY;
|
List<Shape> cellShapes = new ArrayList<Shape>(cells.size());
|
||||||
double minY = Double.POSITIVE_INFINITY, maxY = Double.NEGATIVE_INFINITY;
|
|
||||||
for (Cell cell : cells) {
|
for (Cell cell : cells) {
|
||||||
assert cell.getLevel() <= detailLevel;
|
cellShapes.add(cell.getShape());
|
||||||
Rectangle cellR = cell.getShape().getBoundingBox();
|
|
||||||
|
|
||||||
minX = Math.min(minX, cellR.getMinX());
|
|
||||||
maxX = Math.max(maxX, cellR.getMaxX());
|
|
||||||
minY = Math.min(minY, cellR.getMinY());
|
|
||||||
maxY = Math.max(maxY, cellR.getMaxY());
|
|
||||||
}
|
}
|
||||||
return ctx.makeRectangle(minX, maxX, minY, maxY);
|
return new ShapeCollection<Shape>(cellShapes, ctx).getBoundingBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An aggregate of 2 shapes. Only implements what's necessary for the test
|
* An aggregate of 2 shapes. Unfortunately we can't simply use a ShapeCollection because:
|
||||||
* here. TODO replace with Spatial4j trunk ShapeCollection.
|
* (a) ambiguity between CONTAINS & WITHIN for equal shapes, and
|
||||||
|
* (b) adjacent pairs could as a whole contain the input shape.
|
||||||
|
* The tests here are sensitive to these matters, although in practice ShapeCollection
|
||||||
|
* is fine.
|
||||||
*/
|
*/
|
||||||
private class ShapePair implements Shape {
|
private class ShapePair extends ShapeCollection<Rectangle> {
|
||||||
|
|
||||||
final Rectangle shape1, shape2;
|
final Rectangle shape1, shape2;
|
||||||
final boolean biasContainsThenWithin;//a hack
|
final boolean biasContainsThenWithin;//a hack
|
||||||
|
|
||||||
public ShapePair(Rectangle shape1, Rectangle shape2, boolean containsThenWithin) {
|
public ShapePair(Rectangle shape1, Rectangle shape2, boolean containsThenWithin) {
|
||||||
|
super(Arrays.asList(shape1, shape2), ctx);
|
||||||
this.shape1 = shape1;
|
this.shape1 = shape1;
|
||||||
this.shape2 = shape2;
|
this.shape2 = shape2;
|
||||||
biasContainsThenWithin = containsThenWithin;
|
biasContainsThenWithin = containsThenWithin;
|
||||||
|
@ -359,15 +363,20 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
@Override
|
@Override
|
||||||
public SpatialRelation relate(Shape other) {
|
public SpatialRelation relate(Shape other) {
|
||||||
SpatialRelation r = relateApprox(other);
|
SpatialRelation r = relateApprox(other);
|
||||||
if (r != INTERSECTS && !(r == WITHIN && biasContainsThenWithin))
|
if (r == CONTAINS)
|
||||||
return r;
|
return r;
|
||||||
|
if (r == DISJOINT)
|
||||||
|
return r;
|
||||||
|
if (r == WITHIN && !biasContainsThenWithin)
|
||||||
|
return r;
|
||||||
|
|
||||||
//See if the correct answer is actually Contains, when the indexed shapes are adjacent,
|
//See if the correct answer is actually Contains, when the indexed shapes are adjacent,
|
||||||
// creating a larger shape that contains the input shape.
|
// creating a larger shape that contains the input shape.
|
||||||
Rectangle oRect = (Rectangle)other;
|
|
||||||
boolean pairTouches = shape1.relate(shape2).intersects();
|
boolean pairTouches = shape1.relate(shape2).intersects();
|
||||||
if (!pairTouches)
|
if (!pairTouches)
|
||||||
return r;
|
return r;
|
||||||
//test all 4 corners
|
//test all 4 corners
|
||||||
|
Rectangle oRect = (Rectangle)other;
|
||||||
if (relate(ctx.makePoint(oRect.getMinX(), oRect.getMinY())) == CONTAINS
|
if (relate(ctx.makePoint(oRect.getMinX(), oRect.getMinY())) == CONTAINS
|
||||||
&& relate(ctx.makePoint(oRect.getMinX(), oRect.getMaxY())) == CONTAINS
|
&& relate(ctx.makePoint(oRect.getMinX(), oRect.getMaxY())) == CONTAINS
|
||||||
&& relate(ctx.makePoint(oRect.getMaxX(), oRect.getMinY())) == CONTAINS
|
&& relate(ctx.makePoint(oRect.getMaxX(), oRect.getMinY())) == CONTAINS
|
||||||
|
@ -391,40 +400,10 @@ public class SpatialOpRecursivePrefixTreeTest extends StrategyTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (shape1.relate(other).intersects() || shape2.relate(other).intersects())
|
if (shape1.relate(other).intersects() || shape2.relate(other).intersects())
|
||||||
return INTERSECTS;//might actually be 'CONTAINS' if these 2 are adjacent
|
return INTERSECTS;//might actually be 'CONTAINS' if the pair are adjacent but we handle that later
|
||||||
return DISJOINT;
|
return DISJOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Rectangle getBoundingBox() {
|
|
||||||
return ctx.getWorldBounds();//good enough
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasArea() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double getArea(SpatialContext ctx) {
|
|
||||||
throw new UnsupportedOperationException("TODO unimplemented");//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Point getCenter() {
|
|
||||||
throw new UnsupportedOperationException("TODO unimplemented");//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Shape getBuffered(double distance, SpatialContext ctx) {
|
|
||||||
throw new UnsupportedOperationException("TODO unimplemented");//TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ShapePair(" + shape1 + " , " + shape2 + ")";
|
return "ShapePair(" + shape1 + " , " + shape2 + ")";
|
||||||
|
|
Loading…
Reference in New Issue