| diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java |
| index fb024b6..e2625b5 100755 |
| --- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java |
| +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java |
| @@ -33,6 +33,8 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| protected final List<GeoPoint> points; |
| /** A bitset describing, for each edge, whether it is internal or not */ |
| protected final BitSet isInternalEdges; |
| + /** The list of holes. If a point is in the hole, it is *not* in the polygon */ |
| + protected final List<GeoPolygon> holes; |
| |
| /** A list of edges */ |
| protected SidedPlane[] edges = null; |
| @@ -44,7 +46,7 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| protected double fullDistance = 0.0; |
| /** Set to true when the polygon is complete */ |
| protected boolean isDone = false; |
| - |
| + |
| /** |
| * Create a convex polygon from a list of points. The first point must be on the |
| * external edge. |
| @@ -52,8 +54,20 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| *@param pointList is the list of points to create the polygon from. |
| */ |
| public GeoConvexPolygon(final PlanetModel planetModel, final List<GeoPoint> pointList) { |
| + this(planetModel, pointList, null); |
| + } |
| + |
| + /** |
| + * Create a convex polygon from a list of points. The first point must be on the |
| + * external edge. |
| + *@param planetModel is the planet model. |
| + *@param pointList is the list of points to create the polygon from. |
| + *@param holes is the list of GeoPolygon objects that describe holes in the complex polygon. Null == no holes. |
| + */ |
| + public GeoConvexPolygon(final PlanetModel planetModel, final List<GeoPoint> pointList, final List<GeoPolygon> holes) { |
| super(planetModel); |
| this.points = pointList; |
| + this.holes = holes; |
| this.isInternalEdges = new BitSet(); |
| done(false); |
| } |
| @@ -66,10 +80,30 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| *@param internalEdgeFlags is a bitset describing whether each edge is internal or not. |
| *@param returnEdgeInternal is true when the final return edge is an internal one. |
| */ |
| - public GeoConvexPolygon(final PlanetModel planetModel, final List<GeoPoint> pointList, final BitSet internalEdgeFlags, |
| - final boolean returnEdgeInternal) { |
| + public GeoConvexPolygon(final PlanetModel planetModel, |
| + final List<GeoPoint> pointList, |
| + final BitSet internalEdgeFlags, |
| + final boolean returnEdgeInternal) { |
| + this(planetModel, pointList, null, internalEdgeFlags, returnEdgeInternal); |
| + } |
| + |
| + /** |
| + * Create a convex polygon from a list of points, keeping track of which boundaries |
| + * are internal. This is used when creating a polygon as a building block for another shape. |
| + *@param planetModel is the planet model. |
| + *@param pointList is the set of points to create the polygon from. |
| + *@param holes is the list of GeoPolygon objects that describe holes in the complex polygon. Null == no holes. |
| + *@param internalEdgeFlags is a bitset describing whether each edge is internal or not. |
| + *@param returnEdgeInternal is true when the final return edge is an internal one. |
| + */ |
| + public GeoConvexPolygon(final PlanetModel planetModel, |
| + final List<GeoPoint> pointList, |
| + final List<GeoPolygon> holes, |
| + final BitSet internalEdgeFlags, |
| + final boolean returnEdgeInternal) { |
| super(planetModel); |
| this.points = pointList; |
| + this.holes = holes; |
| this.isInternalEdges = internalEdgeFlags; |
| done(returnEdgeInternal); |
| } |
| @@ -81,9 +115,27 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| *@param startLatitude is the latitude of the first point. |
| *@param startLongitude is the longitude of the first point. |
| */ |
| - public GeoConvexPolygon(final PlanetModel planetModel, final double startLatitude, final double startLongitude) { |
| + public GeoConvexPolygon(final PlanetModel planetModel, |
| + final double startLatitude, |
| + final double startLongitude) { |
| + this(planetModel, startLatitude, startLongitude, null); |
| + } |
| + |
| + /** |
| + * Create a convex polygon, with a starting latitude and longitude. |
| + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} |
| + *@param planetModel is the planet model. |
| + *@param startLatitude is the latitude of the first point. |
| + *@param startLongitude is the longitude of the first point. |
| + *@param holes is the list of GeoPolygon objects that describe holes in the complex polygon. Null == no holes. |
| + */ |
| + public GeoConvexPolygon(final PlanetModel planetModel, |
| + final double startLatitude, |
| + final double startLongitude, |
| + final List<GeoPolygon> holes) { |
| super(planetModel); |
| points = new ArrayList<>(); |
| + this.holes = holes; |
| isInternalEdges = new BitSet(); |
| points.add(new GeoPoint(planetModel, startLatitude, startLongitude)); |
| } |
| @@ -138,12 +190,6 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| edges[i] = sp; |
| notableEdgePoints[i] = new GeoPoint[]{start, end}; |
| } |
| - createCenterPoint(); |
| - } |
| - |
| - /** Compute a reasonable center point. |
| - */ |
| - protected void createCenterPoint() { |
| // In order to naively confirm that the polygon is convex, I would need to |
| // check every edge, and verify that every point (other than the edge endpoints) |
| // is within the edge's sided plane. This is an order n^2 operation. That's still |
| @@ -157,6 +203,7 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| } |
| } |
| } |
| + // Pick an edge point arbitrarily |
| edgePoints = new GeoPoint[]{points.get(0)}; |
| } |
| |
| @@ -176,6 +223,13 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| if (!edge.isWithin(x, y, z)) |
| return false; |
| } |
| + if (holes != null) { |
| + for (final GeoPolygon polygon : holes) { |
| + if (polygon.isWithin(x, y, z)) { |
| + return false; |
| + } |
| + } |
| + } |
| return true; |
| } |
| |
| @@ -207,6 +261,14 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| } |
| } |
| } |
| + if (holes != null) { |
| + // Each hole needs to be looked at for intersection too, since a shape can be entirely within the hole |
| + for (final GeoPolygon hole : holes) { |
| + if (hole.intersects(p, notablePoints, bounds)) { |
| + return true; |
| + } |
| + } |
| + } |
| //System.err.println(" no intersection"); |
| return false; |
| } |
| @@ -270,6 +332,14 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| return false; |
| if (!other.isInternalEdges.equals(isInternalEdges)) |
| return false; |
| + if (other.holes != null || holes != null) { |
| + if (other.holes == null || holes == null) { |
| + return false; |
| + } |
| + if (!other.holes.equals(holes)) { |
| + return false; |
| + } |
| + } |
| return (other.points.equals(points)); |
| } |
| |
| @@ -277,12 +347,15 @@ public class GeoConvexPolygon extends GeoBasePolygon { |
| public int hashCode() { |
| int result = super.hashCode(); |
| result = 31 * result + points.hashCode(); |
| + if (holes != null) { |
| + result = 31 * result + holes.hashCode(); |
| + } |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| - return "GeoConvexPolygon: {planetmodel=" + planetModel + ", points=" + points + "}"; |
| + return "GeoConvexPolygon: {planetmodel=" + planetModel + ", points=" + points + ((holes== null)?"":", holes=" + holes) + "}"; |
| } |
| } |
| |
| diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java |
| index 8ee4290..cfcd1ac 100755 |
| --- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java |
| +++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygonFactory.java |
| @@ -37,13 +37,32 @@ public class GeoPolygonFactory { |
| * its neighbors determines inside/outside for the entire polygon. |
| * @return a GeoPolygon corresponding to what was specified. |
| */ |
| - public static GeoPolygon makeGeoPolygon(final PlanetModel planetModel, final List<GeoPoint> pointList, final int convexPointIndex) { |
| + public static GeoPolygon makeGeoPolygon(final PlanetModel planetModel, |
| + final List<GeoPoint> pointList, |
| + final int convexPointIndex) { |
| + return makeGeoPolygon(planetModel, pointList, convexPointIndex, null); |
| + } |
| + |
| + /** |
| + * Create a GeoMembershipShape of the right kind given the specified bounds. |
| + * |
| + * @param pointList is a list of the GeoPoints to build an arbitrary polygon out of. |
| + * @param convexPointIndex is the index of a single convex point whose conformation with |
| + * its neighbors determines inside/outside for the entire polygon. |
| + * @param holes is a list of polygons representing "holes" in the outside polygon. Null == none. |
| + * @return a GeoPolygon corresponding to what was specified. |
| + */ |
| + public static GeoPolygon makeGeoPolygon(final PlanetModel planetModel, |
| + final List<GeoPoint> pointList, |
| + final int convexPointIndex, |
| + final List<GeoPolygon> holes) { |
| // The basic operation uses a set of points, two points determining one particular edge, and a sided plane |
| // describing membership. |
| return buildPolygonShape(planetModel, pointList, convexPointIndex, getLegalIndex(convexPointIndex + 1, pointList.size()), |
| new SidedPlane(pointList.get(getLegalIndex(convexPointIndex - 1, pointList.size())), |
| pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex + 1, pointList.size()))), |
| - false); |
| + false, |
| + holes); |
| } |
| |
| /** Build a GeoMembershipShape given points, starting edge, and whether starting edge is internal or not. |
| @@ -52,9 +71,17 @@ public class GeoPolygonFactory { |
| * @param endPointIndex is another of the points constituting the starting edge. |
| * @param startingEdge is the plane describing the starting edge. |
| * @param isInternalEdge is true if the specified edge is an internal one. |
| + * @param holes is the list of holes in the polygon, or null if none. |
| * @return a GeoMembershipShape corresponding to what was specified. |
| */ |
| - public static GeoPolygon buildPolygonShape(final PlanetModel planetModel, final List<GeoPoint> pointsList, final int startPointIndex, final int endPointIndex, final SidedPlane startingEdge, final boolean isInternalEdge) { |
| + public static GeoPolygon buildPolygonShape( |
| + final PlanetModel planetModel, |
| + final List<GeoPoint> pointsList, |
| + final int startPointIndex, |
| + final int endPointIndex, |
| + final SidedPlane startingEdge, |
| + final boolean isInternalEdge, |
| + final List<GeoPolygon> holes) { |
| // Algorithm as follows: |
| // Start with sided edge. Go through all points in some order. For each new point, determine if the point is within all edges considered so far. |
| // If not, put it into a list of points for recursion. If it is within, add new edge and keep going. |
| @@ -119,7 +146,7 @@ public class GeoPolygonFactory { |
| } |
| // We want the other side for the recursion |
| SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); |
| - rval.addShape(buildPolygonShape(planetModel, recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); |
| + rval.addShape(buildPolygonShape(planetModel, recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true, holes)); |
| recursionList.clear(); |
| } |
| currentList.add(newPoint); |
| @@ -147,11 +174,11 @@ public class GeoPolygonFactory { |
| 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 |
| SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); |
| - rval.addShape(buildPolygonShape(planetModel, recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); |
| + rval.addShape(buildPolygonShape(planetModel, recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true, holes)); |
| recursionList.clear(); |
| } |
| // Now, add in the current shape. |
| - rval.addShape(new GeoConvexPolygon(planetModel, currentList, internalEdgeList, returnEdgeInternalBoundary)); |
| + rval.addShape(new GeoConvexPolygon(planetModel, currentList, holes, internalEdgeList, returnEdgeInternalBoundary)); |
| //System.out.println("Done creating polygon"); |
| return rval; |
| } |