committing Karl's latest LUCENE-6196-fixes.patch

https://issues.apache.org/jira/secure/attachment/12725741/LUCENE-6196-fixes.patch
WIP; tests fail

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6196@1674049 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2015-04-16 11:51:41 +00:00
parent 400c39bac7
commit b2c273fa7a
12 changed files with 171 additions and 89 deletions

View File

@ -221,4 +221,11 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Circle: center = ");
sb.append(center).append(" radius = ").append(cutoffAngle);
return sb.toString();
}
}

View File

@ -129,8 +129,14 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
// Let's be sure that our interior point is really inside
for (SidedPlane sp : edges) {
if (!sp.isWithin(interiorPoint))
throw new IllegalArgumentException("Interior point logic failed to produce an interior point");
if (!sp.isWithin(interiorPoint)) {
StringBuilder sb = new StringBuilder("Interior point logic failed to produce an interior point. Vertices: ");
for (GeoPoint p : points) {
sb.append(p).append(" ");
}
sb.append(". Interior point: ").append(interiorPoint);
throw new IllegalArgumentException(sb.toString());
}
}
}

View File

@ -69,7 +69,11 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
this.RHC = new GeoPoint(sinLatitude,sinRightLon,cosLatitude,cosRightLon);
this.plane = new Plane(sinLatitude);
// Normalize
while (leftLon > rightLon) {
rightLon += Math.PI * 2.0;
}
double middleLon = (leftLon + rightLon) * 0.5;
double sinMiddleLon = Math.sin(middleLon);
double cosMiddleLon = Math.cos(middleLon);

View File

@ -43,12 +43,13 @@ public class GeoLatitudeZone implements GeoBBox
Vector topPoint = new Vector(0.0,0.0,sinTopLat);
Vector bottomPoint = new Vector(0.0,0.0,sinBottomLat);
this.topPlane = new SidedPlane(bottomPoint,sinTopLat);
this.bottomPlane = new SidedPlane(topPoint,sinBottomLat);
// Compute an interior point. Pick one whose lat is between top and bottom.
double sinMiddleLat = (topLat + bottomLat) * 0.5;
double middleLat = (topLat + bottomLat) * 0.5;
double sinMiddleLat = Math.sin(middleLat);
interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat);
this.topPlane = new SidedPlane(interiorPoint,sinTopLat);
this.bottomPlane = new SidedPlane(interiorPoint,sinBottomLat);
}
@Override
@ -117,10 +118,6 @@ public class GeoLatitudeZone implements GeoBBox
@Override
public int getRelationship(GeoShape path) {
// First observation: The only way you can draw a path that
// contains this area is to have an unbounded path that circles the globe.
// So we will never return CONTAINS.
// Second, the shortcut of seeing whether endpoints are in/out is not going to
// work with no area endpoints. So we rely entirely on intersections.
@ -128,6 +125,9 @@ public class GeoLatitudeZone implements GeoBBox
path.intersects(bottomPlane,topPlane))
return OVERLAPS;
if (path.isWithin(interiorPoint))
return CONTAINS;
if (isWithin(path.getInteriorPoint()))
return WITHIN;

View File

@ -54,6 +54,10 @@ public class GeoLongitudeSlice implements GeoBBox
double sinRightLon = Math.sin(rightLon);
double cosRightLon = Math.cos(rightLon);
// Normalize
while (leftLon > rightLon) {
rightLon += Math.PI * 2.0;
}
double middleLon = (leftLon + rightLon) * 0.5;
centerPoint = new GeoPoint(0.0,middleLon);
@ -134,22 +138,19 @@ public class GeoLongitudeSlice implements GeoBBox
@Override
public int getRelationship(GeoShape path) {
// It's possible to contain this area. The way we do this is to
// see whether the shape contains both the north and south poles. If it does,
// we make the assumption that it contains the entire shape (which is
// a convenient approximation that, at worst, increases our computation).
if (path.isWithin(0.0,0.0,Math.PI * 0.5) &&
path.isWithin(0.0,0.0,-Math.PI * 0.5))
return CONTAINS;
// Next, look for intersections.
if (path.intersects(leftPlane,rightPlane) ||
path.intersects(rightPlane,leftPlane))
path.intersects(rightPlane,leftPlane)) {
return OVERLAPS;
}
if (isWithin(path.getInteriorPoint()))
if (isWithin(path.getInteriorPoint())) {
return WITHIN;
}
if (path.isWithin(centerPoint)) {
return CONTAINS;
}
return DISJOINT;
}

View File

@ -69,6 +69,11 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
points.add(se);
}
public void done() {
if (points.size() == 0)
throw new IllegalArgumentException("Path must have at least one point");
}
/** Compute an estimate of "distance" to the GeoPoint.
* A return value of Double.MAX_VALUE should be returned for
* points outside of the shape.

View File

@ -96,6 +96,11 @@ public class GeoPolygonFactory
// Handle exclusion
recursionList.add(newPoint);
recursionList.add(currentList.get(currentList.size()-1));
if (recursionList.size() == pointsList.size()) {
// We are trying to recurse with a list the same size as the one we started with.
// Clearly, the polygon cannot be constructed
throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts");
}
// We want the other side for the recursion
SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary);
rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary));
@ -115,6 +120,11 @@ public class GeoPolygonFactory
// The last step back to the start point had a recursion, so take care of that before we complete our work
recursionList.add(currentList.get(0));
recursionList.add(currentList.get(currentList.size()-1));
if (recursionList.size() == pointsList.size()) {
// We are trying to recurse with a list the same size as the one we started with.
// Clearly, the polygon cannot be constructed
throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts");
}
// Construct a sided plane based on these two points, and the previous point
SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size()-2),currentList.get(0),currentList.get(currentList.size()-1));
// We want the other side for the recursion

View File

@ -86,6 +86,10 @@ public class GeoRectangle implements GeoBBox
double middleLat = (topLat + bottomLat) * 0.5;
double sinMiddleLat = Math.sin(middleLat);
cosMiddleLat = Math.cos(middleLat);
// Normalize
while (leftLon > rightLon) {
rightLon += Math.PI * 2.0;
}
double middleLon = (leftLon + rightLon) * 0.5;
double sinMiddleLon = Math.sin(middleLon);
double cosMiddleLon = Math.cos(middleLon);
@ -181,29 +185,6 @@ public class GeoRectangle implements GeoBBox
@Override
public int getRelationship(GeoShape path) {
// There are many cases here. I'll go through them in order
boolean ulhcWithin = path.isWithin(ULHC);
boolean urhcWithin = path.isWithin(URHC);
boolean lrhcWithin = path.isWithin(LRHC);
boolean llhcWithin = path.isWithin(LLHC);
// If there are some that are in, and some that are out, we've got overlap. Otherwise, things are different.
if (ulhcWithin && urhcWithin && lrhcWithin && llhcWithin) {
// It's not precisely correct, but at this point we CHOOSE to claim that the entire rectangle is within the path.
// This in practice will mean that we generate more geotokens than are strictly needed, but otherwise this case
// would be expensive to disentangle.
return CONTAINS;
}
if (ulhcWithin || urhcWithin || lrhcWithin || llhcWithin) {
// Some are in, some are out: definite overlap
return OVERLAPS;
}
// All rectangle endpoints are outside the path. The three possible cases are WITHIN, OVERLAPS, and DISJOINT.
// The only way to distinguish between them is to look at whether any of the four rectangle sides intersect
// the path edges. If there is no intersection, AND any path point is within the rectangle, THEN return WITHIN.
if (path.intersects(topPlane,bottomPlane,leftPlane,rightPlane) ||
path.intersects(bottomPlane,topPlane,leftPlane,rightPlane) ||
path.intersects(leftPlane,topPlane,bottomPlane,rightPlane) ||
@ -213,6 +194,9 @@ public class GeoRectangle implements GeoBBox
if (isWithin(path.getInteriorPoint()))
return WITHIN;
if (path.isWithin(centerPoint))
return CONTAINS;
return DISJOINT;
}

View File

@ -71,7 +71,11 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
this.RHC = new GeoPoint(sinLatitude,sinRightLon,cosLatitude,cosRightLon);
this.plane = new Plane(sinLatitude);
// Normalize
while (leftLon > rightLon) {
rightLon += Math.PI * 2.0;
}
double middleLon = (leftLon + rightLon) * 0.5;
double sinMiddleLon = Math.sin(middleLon);
double cosMiddleLon = Math.cos(middleLon);

View File

@ -54,7 +54,11 @@ public class GeoWideLongitudeSlice implements GeoBBox
double cosLeftLon = Math.cos(leftLon);
double sinRightLon = Math.sin(rightLon);
double cosRightLon = Math.cos(rightLon);
// Normalize
while (leftLon > rightLon) {
rightLon += Math.PI * 2.0;
}
double middleLon = (leftLon + rightLon) * 0.5;
centerPoint = new GeoPoint(0.0,middleLon);
@ -136,15 +140,6 @@ public class GeoWideLongitudeSlice implements GeoBBox
@Override
public int getRelationship(GeoShape path) {
// It's possible to contain this area. The way we do this is to
// see whether the shape contains both the north and south poles. If it does,
// we make the assumption that it contains the entire shape (which is
// a convenient approximation that, at worst, increases our computation).
if (path.isWithin(0.0,0.0,Math.PI * 0.5) &&
path.isWithin(0.0,0.0,-Math.PI * 0.5))
return CONTAINS;
// Next, look for intersections. No bounds because it is a wide variant.
if (path.intersects(leftPlane) ||
path.intersects(rightPlane))
return OVERLAPS;
@ -152,6 +147,9 @@ public class GeoWideLongitudeSlice implements GeoBBox
if (isWithin(path.getInteriorPoint()))
return WITHIN;
if (path.isWithin(centerPoint))
return CONTAINS;
return DISJOINT;
}

View File

@ -89,6 +89,10 @@ public class GeoWideRectangle implements GeoBBox
double middleLat = (topLat + bottomLat) * 0.5;
double sinMiddleLat = Math.sin(middleLat);
cosMiddleLat = Math.cos(middleLat);
// Normalize
while (leftLon > rightLon) {
rightLon += Math.PI * 2.0;
}
double middleLon = (leftLon + rightLon) * 0.5;
double sinMiddleLon = Math.sin(middleLon);
double cosMiddleLon = Math.cos(middleLon);
@ -187,29 +191,6 @@ public class GeoWideRectangle implements GeoBBox
@Override
public int getRelationship(GeoShape path) {
// There are many cases here. I'll go through them in order
boolean ulhcWithin = path.isWithin(ULHC);
boolean urhcWithin = path.isWithin(URHC);
boolean lrhcWithin = path.isWithin(LRHC);
boolean llhcWithin = path.isWithin(LLHC);
// If there are some that are in, and some that are out, we've got overlap. Otherwise, things are different.
if (ulhcWithin && urhcWithin && lrhcWithin && llhcWithin) {
// It's not precisely correct, but at this point we CHOOSE to claim that the entire rectangle is within the path.
// This in practice will mean that we generate more geotokens than are strictly needed, but otherwise this case
// would be expensive to disentangle.
return CONTAINS;
}
if (ulhcWithin || urhcWithin || lrhcWithin || llhcWithin) {
// Some are in, some are out: definite overlap
return OVERLAPS;
}
// All rectangle endpoints are outside the path. The three possible cases are WITHIN, OVERLAPS, and DISJOINT.
// The only way to distinguish between them is to look at whether any of the four rectangle sides intersect
// the path edges. If there is no intersection, AND any path point is within the rectangle, THEN return WITHIN.
if (path.intersects(topPlane,bottomPlane,eitherBound) ||
path.intersects(bottomPlane,topPlane,eitherBound) ||
path.intersects(leftPlane,topPlane,bottomPlane) ||
@ -219,6 +200,9 @@ public class GeoWideRectangle implements GeoBBox
if (isWithin(path.getInteriorPoint()))
return WITHIN;
if (path.isWithin(centerPoint))
return CONTAINS;
return DISJOINT;
}

View File

@ -42,6 +42,9 @@ import org.apache.lucene.spatial.spatial4j.Geo3dShape;
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint;
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPolygonFactory;
import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape;
import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle;
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPath;
import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBoxFactory;
import org.junit.Test;
import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS;
@ -91,6 +94,14 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
@Test
public void testTriangleDisjointRect() throws IOException {
setupStrategy();
Rectangle rect = ctx.makeRectangle(-180, 180, 77, 84);
Shape triangle = makeTriangle(-149, 35, 88, -11, -27, -18);
assertTrue(rect.relate(triangle).intersects()); //unsure if this is correct or not but passes
//if they intersect, then the following rect cell can be "within" the triangle
final Rectangle cellRect = ctx.makeRectangle(-180, -168.75, 73.125, 78.75);
assert cellRect.relate(rect).intersects();
//assertTrue(cellRect.relate(triangle) != SpatialRelation.WITHIN);
}
private Shape makeTriangle(double x1, double y1, double x2, double y2, double x3, double y3) {
@ -110,17 +121,85 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
@Override
protected Shape randomQueryShape() {
//random triangle
final List<GeoPoint> geoPoints = new ArrayList<>();
while (geoPoints.size() < 3) {
final Point point = randomPoint();
final GeoPoint gPt = new GeoPoint(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS);
if (geoPoints.contains(gPt) == false) {
geoPoints.add(gPt);
final int shapeType = random().nextInt(4);
switch (shapeType) {
case 0: {
// Polygons
final int vertexCount = random().nextInt(3) + 3;
while (true) {
final List<GeoPoint> geoPoints = new ArrayList<>();
while (geoPoints.size() < vertexCount) {
final Point point = randomPoint();
final GeoPoint gPt = new GeoPoint(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS);
geoPoints.add(gPt);
}
final int convexPointIndex = random().nextInt(vertexCount); //If we get this wrong, hopefully we get IllegalArgumentException
try {
final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(geoPoints, convexPointIndex);
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;
}
}
}
case 1: {
// Circles
while (true) {
final int circleRadius = random().nextInt(180);
final Point point = randomPoint();
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;
}
}
}
case 2: {
// Rectangles
while (true) {
final Point ulhcPoint = randomPoint();
final Point lrhcPoint = randomPoint();
try {
final GeoShape shape = GeoBBoxFactory.makeGeoBBox(ulhcPoint.getY() * DEGREES_TO_RADIANS,
lrhcPoint.getY() * DEGREES_TO_RADIANS,
ulhcPoint.getX() * DEGREES_TO_RADIANS,
lrhcPoint.getX() * 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;
}
}
}
case 3: {
// Paths
final int pointCount = random().nextInt(5) + 1;
final double width = random().nextInt(90) * DEGREES_TO_RADIANS;
while (true) {
try {
final GeoPath path = new GeoPath(width);
for (int i = 0; i < pointCount; i++) {
final Point nextPoint = randomPoint();
path.addPoint(nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
}
path.done();
return new Geo3dShape(path, 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;
}
}
}
default:
throw new IllegalStateException("Unexpected shape type");
}
final int convexPointIndex = random().nextInt(3);
final GeoShape shape = GeoPolygonFactory.makeGeoPolygon(geoPoints, convexPointIndex);
return new Geo3dShape(shape, ctx);
}
}