LUCENE-7192: Permit adjacent points in a polygon to be coplanar.

This commit is contained in:
Karl Wright 2016-04-08 09:14:47 -04:00
parent 9b65416e60
commit 771680cfd0
3 changed files with 70 additions and 16 deletions

View File

@ -46,8 +46,6 @@ class GeoConcavePolygon extends GeoBasePolygon {
protected GeoPoint[][] notableEdgePoints = null;
/** A point which is on the boundary of the polygon */
protected GeoPoint[] edgePoints = null;
/** Tracking the maximum distance we go at any one time, so to be sure it's legal */
protected double fullDistance = 0.0;
/** Set to true when the polygon is complete */
protected boolean isDone = false;
/** A bounds object for each sided plane */
@ -189,10 +187,21 @@ class GeoConcavePolygon extends GeoBasePolygon {
for (int i = 0; i < points.size(); i++) {
final GeoPoint start = points.get(i);
final GeoPoint end = points.get(legalIndex(i + 1));
final double distance = start.arcDistance(end);
if (distance > fullDistance)
fullDistance = distance;
final GeoPoint check = points.get(legalIndex(i + 2));
// We have to find the next point that is not on the plane between start and end.
// If there is no such point, it's an error.
final Plane planeToFind = new Plane(start, end);
int endPointIndex = -1;
for (int j = 0; j < points.size(); j++) {
final int index = legalIndex(j + i + 2);
if (!planeToFind.evaluateIsZero(points.get(index))) {
endPointIndex = index;
break;
}
}
if (endPointIndex == -1) {
throw new IllegalArgumentException("Polygon points are all coplanar");
}
final GeoPoint check = points.get(endPointIndex);
// Here note the flip of the sense of the sided plane!!
final SidedPlane sp = new SidedPlane(check, false, start, end);
//System.out.println("Created edge "+sp+" using start="+start+" end="+end+" check="+check);

View File

@ -44,8 +44,6 @@ class GeoConvexPolygon extends GeoBasePolygon {
protected GeoPoint[][] notableEdgePoints = null;
/** A point which is on the boundary of the polygon */
protected GeoPoint[] edgePoints = null;
/** Tracking the maximum distance we go at any one time, so to be sure it's legal */
protected double fullDistance = 0.0;
/** Set to true when the polygon is complete */
protected boolean isDone = false;
/** A bounds object for each sided plane */
@ -185,10 +183,21 @@ class GeoConvexPolygon extends GeoBasePolygon {
for (int i = 0; i < points.size(); i++) {
final GeoPoint start = points.get(i);
final GeoPoint end = points.get(legalIndex(i + 1));
final double distance = start.arcDistance(end);
if (distance > fullDistance)
fullDistance = distance;
final GeoPoint check = points.get(legalIndex(i + 2));
// We have to find the next point that is not on the plane between start and end.
// If there is no such point, it's an error.
final Plane planeToFind = new Plane(start, end);
int endPointIndex = -1;
for (int j = 0; j < points.size(); j++) {
final int index = legalIndex(j + i + 2);
if (!planeToFind.evaluateIsZero(points.get(index))) {
endPointIndex = index;
break;
}
}
if (endPointIndex == -1) {
throw new IllegalArgumentException("Polygon points are all coplanar");
}
final GeoPoint check = points.get(endPointIndex);
final SidedPlane sp = new SidedPlane(check, start, end);
//System.out.println("Created edge "+sp+" using start="+start+" end="+end+" check="+check);
edges[i] = sp;

View File

@ -714,12 +714,17 @@ public class GeoPolygonFactory {
// the start point of the first edge and the end point of the last edge. If the first edge start point is the same as the last edge end point,
// it's a degenerate case and we want to just clean out the edge buffer entirely.
final List<GeoPoint> points = new ArrayList<GeoPoint>(includedEdges.size());
final BitSet internalEdges = new BitSet(includedEdges.size()-1);
final List<GeoPoint> points = new ArrayList<GeoPoint>(includedEdges.size()+1);
final BitSet internalEdges = new BitSet(includedEdges.size());
final boolean returnIsInternal;
if (firstEdge.startPoint == lastEdge.endPoint) {
// Degenerate case!! There is no return edge -- or rather, we already have it.
if (includedEdges.size() < 3) {
// This means we found a degenerate cycle of edges. If we emit a polygon at this point it
// has no contents, so we've clearly done something wrong, but not sure what.
throw new IllegalArgumentException("polygon was illegal (degenerate illegal two-edge cyclical polygon encountered in processing)");
}
Edge edge = firstEdge;
points.add(edge.startPoint);
int i = 0;
@ -945,8 +950,39 @@ public class GeoPolygonFactory {
// Get the next point
final GeoPoint newPoint = pointList.get(endIndex);
// Build the new edge
final boolean isNewPointWithin = currentEdge.plane.isWithin(newPoint);
final SidedPlane newPlane = new SidedPlane(currentEdge.startPoint, isNewPointWithin, pointList.get(startIndex), newPoint);
// We have to be sure that the point we use as a check does not lie on the plane.
// In order to meet that goal, we need to go hunting for a point that meets the criteria. If we don't
// find one, we've got a linear "polygon" that we cannot use.
// We need to know the sidedness of the new plane. The point we're going to be presenting to it has
// a certain relationship with the sided plane we already have for the current edge. If the current edge
// is colinear with the new edge, then we want to maintain the same relationship. If the new edge
// is not colinear, then we can use the new point's relationship with the current edge as our guide.
final boolean isNewPointWithin;
final GeoPoint pointToPresent;
if (currentEdge.plane.evaluateIsZero(newPoint)) {
// The new point is colinear with the current edge. We'll have to look for the first point that isn't.
int checkPointIndex = -1;
final Plane checkPlane = new Plane(pointList.get(startIndex), newPoint);
for (int i = 0; i < pointList.size(); i++) {
final int index = getLegalIndex(startIndex - 1 - i, pointList.size());
if (!checkPlane.evaluateIsZero(pointList.get(index))) {
checkPointIndex = index;
break;
}
}
if (checkPointIndex == -1) {
throw new IllegalArgumentException("polygon is illegal (linear)");
}
pointToPresent = pointList.get(checkPointIndex);
isNewPointWithin = currentEdge.plane.isWithin(pointToPresent);
} else {
isNewPointWithin = currentEdge.plane.isWithin(newPoint);
pointToPresent = currentEdge.startPoint;
}
final SidedPlane newPlane = new SidedPlane(pointToPresent, isNewPointWithin, pointList.get(startIndex), newPoint);
/*
System.out.println("For next plane, the following points are in/out:");
for (final GeoPoint p: pointList) {