You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mi...@apache.org on 2015/09/02 21:59:32 UTC

svn commit: r1700883 [3/5] - in /lucene/dev/trunk/lucene: ./ sandbox/src/java/org/apache/lucene/bkdtree/ sandbox/src/java/org/apache/lucene/rangetree/ spatial/src/java/org/apache/lucene/spatial/spatial4j/ spatial/src/test/org/apache/lucene/spatial/spat...

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLatitudeZone.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLatitudeZone.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLatitudeZone.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLatitudeZone.java Wed Sep  2 19:59:31 2015
@@ -86,21 +86,11 @@ public class GeoDegenerateLatitudeZone e
     return p.intersects(planetModel, plane, notablePoints, planePoints, bounds);
   }
 
-  /**
-   * Compute longitude/latitude bounds for the shape.
-   *
-   * @param bounds is the optional input bounds object.  If this is null,
-   *               a bounds object will be created.  Otherwise, the input object will be modified.
-   * @return a Bounds object describing the shape's bounds.  If the bounds cannot
-   * be computed, then return a Bounds object with noLongitudeBound,
-   * noTopLatitudeBound, and noBottomLatitudeBound.
-   */
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noLongitudeBound().addLatitudeZone(latitude);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.noLongitudeBound()
+      .addHorizontalPlane(planetModel, latitude, plane);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLongitudeSlice.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLongitudeSlice.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLongitudeSlice.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateLongitudeSlice.java Wed Sep  2 19:59:31 2015
@@ -98,12 +98,11 @@ public class GeoDegenerateLongitudeSlice
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noTopLatitudeBound().noBottomLatitudeBound();
-    bounds.addLongitudeSlice(longitude, longitude);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds
+      .addVerticalPlane(planetModel, longitude, plane, boundingPlane)
+      .addPoint(planetModel.NORTH_POLE).addPoint(planetModel.SOUTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegeneratePoint.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegeneratePoint.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegeneratePoint.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegeneratePoint.java Wed Sep  2 19:59:31 2015
@@ -74,11 +74,8 @@ public class GeoDegeneratePoint extends
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addPoint(latitude, longitude);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    bounds.addPoint(this);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateVerticalLine.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateVerticalLine.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateVerticalLine.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoDegenerateVerticalLine.java Wed Sep  2 19:59:31 2015
@@ -77,8 +77,8 @@ public class GeoDegenerateVerticalLine e
     final double cosLongitude = Math.cos(longitude);
 
     // Now build the two points
-    this.UHC = new GeoPoint(planetModel, sinTopLat, sinLongitude, cosTopLat, cosLongitude);
-    this.LHC = new GeoPoint(planetModel, sinBottomLat, sinLongitude, cosBottomLat, cosLongitude);
+    this.UHC = new GeoPoint(planetModel, sinTopLat, sinLongitude, cosTopLat, cosLongitude, topLat, longitude);
+    this.LHC = new GeoPoint(planetModel, sinBottomLat, sinLongitude, cosBottomLat, cosLongitude, bottomLat, longitude);
 
     this.plane = new Plane(cosLongitude, sinLongitude);
 
@@ -146,12 +146,10 @@ public class GeoDegenerateVerticalLine e
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat)
-        .addLongitudeSlice(longitude, longitude);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.addVerticalPlane(planetModel, longitude, plane, boundingPlane, topPlane, bottomPlane)
+      .addPoint(UHC).addPoint(LHC);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLatitudeZone.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLatitudeZone.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLatitudeZone.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLatitudeZone.java Wed Sep  2 19:59:31 2015
@@ -121,11 +121,11 @@ public class GeoLatitudeZone extends Geo
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noLongitudeBound().addLatitudeZone(topLat).addLatitudeZone(bottomLat);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.noLongitudeBound()
+      .addHorizontalPlane(planetModel, topLat, topPlane)
+      .addHorizontalPlane(planetModel, bottomLat, bottomPlane);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLongitudeSlice.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLongitudeSlice.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLongitudeSlice.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoLongitudeSlice.java Wed Sep  2 19:59:31 2015
@@ -129,12 +129,13 @@ public class GeoLongitudeSlice extends G
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noTopLatitudeBound().noBottomLatitudeBound();
-    bounds.addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds
+      .addVerticalPlane(planetModel, leftLon, leftPlane, rightPlane)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, leftPlane)
+      .addPoint(planetModel.NORTH_POLE)
+      .addPoint(planetModel.SOUTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthLatitudeZone.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthLatitudeZone.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthLatitudeZone.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthLatitudeZone.java Wed Sep  2 19:59:31 2015
@@ -100,11 +100,10 @@ public class GeoNorthLatitudeZone extend
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noLongitudeBound().noTopLatitudeBound().addLatitudeZone(bottomLat);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds
+      .addHorizontalPlane(planetModel, bottomLat, bottomPlane);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthRectangle.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoNorthRectangle.java Wed Sep  2 19:59:31 2015
@@ -90,8 +90,8 @@ public class GeoNorthRectangle extends G
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the points
-    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon);
-    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon);
+    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
 
     final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5;
     final double sinMiddleLat = Math.sin(middleLat);
@@ -176,12 +176,13 @@ public class GeoNorthRectangle extends G
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noTopLatitudeBound().addLatitudeZone(bottomLat)
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds
+      .addHorizontalPlane(planetModel, bottomLat, bottomPlane, leftPlane, rightPlane)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, bottomPlane, rightPlane)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, bottomPlane, leftPlane)
+      .addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPath.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPath.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPath.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPath.java Wed Sep  2 19:59:31 2015
@@ -140,10 +140,14 @@ public class GeoPath extends GeoBaseDist
       }
       final GeoPoint upperPoint = new GeoPoint(planetModel, upperLat, upperLon);
       final GeoPoint lowerPoint = new GeoPoint(planetModel, lowerLat, lowerLon);
+      final GeoPoint point = points.get(0);
+      
+      // Construct normal plane
+      final Plane normalPlane = Plane.constructNormalizedZPlane(upperPoint, lowerPoint, point);
 
-      final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(points.get(0), upperPoint, lowerPoint);
+      final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(point, normalPlane, upperPoint, lowerPoint);
       endPoints.add(onlyEndpoint);
-      this.edgePoints = new GeoPoint[]{upperPoint};
+      this.edgePoints = new GeoPoint[]{onlyEndpoint.circlePlane.getSampleIntersectionPoint(planetModel, normalPlane)};
       return;
     }
     
@@ -278,8 +282,8 @@ public class GeoPath extends GeoBaseDist
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    bounds = super.getBounds(bounds);
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
     // For building bounds, order matters.  We want to traverse
     // never more than 180 degrees longitude at a pop or we risk having the
     // bounds object get itself inverted.  So do the edges first.
@@ -289,7 +293,6 @@ public class GeoPath extends GeoBaseDist
     for (SegmentEndpoint pathPoint : endPoints) {
       pathPoint.getBounds(planetModel, bounds);
     }
-    return bounds;
   }
 
   @Override
@@ -339,7 +342,9 @@ public class GeoPath extends GeoBaseDist
     public final GeoPoint[] notablePoints;
     /** No notable points from the circle itself */
     public final static GeoPoint[] circlePoints = new GeoPoint[0];
-
+    /** Null membership */
+    public final static Membership[] NO_MEMBERSHIP = new Membership[0];
+    
     /** Base case.  Does nothing at all.
      */
     public SegmentEndpoint(final GeoPoint point) {
@@ -355,14 +360,12 @@ public class GeoPath extends GeoBaseDist
      *@param upperPoint is a point that must be on the circle plane.
      *@param lowerPoint is another point that must be on the circle plane.
      */
-    public SegmentEndpoint(final GeoPoint point, final GeoPoint upperPoint, final GeoPoint lowerPoint) {
+    public SegmentEndpoint(final GeoPoint point, final Plane normalPlane, final GeoPoint upperPoint, final GeoPoint lowerPoint) {
       this.point = point;
-      // Construct normal plane
-      final Plane normalPlane = Plane.constructNormalizedVerticalPlane(upperPoint, lowerPoint, point);
       // Construct a sided plane that goes through the two points and whose normal is in the normalPlane.
       this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, normalPlane, upperPoint, lowerPoint);
-      this.cutoffPlanes = new Membership[0];
-      this.notablePoints = new GeoPoint[0];
+      this.cutoffPlanes = NO_MEMBERSHIP;
+      this.notablePoints = circlePoints;
     }
     
     /** Constructor for case (2).
@@ -533,7 +536,7 @@ public class GeoPath extends GeoBaseDist
       bounds.addPoint(point);
       if (circlePlane == null)
         return;
-      circlePlane.recordBounds(planetModel, bounds);
+      bounds.addPlane(planetModel, circlePlane);
     }
 
     @Override
@@ -769,15 +772,11 @@ public class GeoPath extends GeoBaseDist
      */
     public void getBounds(final PlanetModel planetModel, Bounds bounds) {
       // We need to do all bounding planes as well as corner points
-      bounds.addPoint(start).addPoint(end);
-      upperConnectingPlane.recordBounds(planetModel, startCutoffPlane, bounds, lowerConnectingPlane, endCutoffPlane);
-      startCutoffPlane.recordBounds(planetModel, lowerConnectingPlane, bounds, endCutoffPlane, upperConnectingPlane);
-      lowerConnectingPlane.recordBounds(planetModel, endCutoffPlane, bounds, upperConnectingPlane, startCutoffPlane);
-      endCutoffPlane.recordBounds(planetModel, upperConnectingPlane, bounds, startCutoffPlane, lowerConnectingPlane);
-      upperConnectingPlane.recordBounds(planetModel, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane);
-      lowerConnectingPlane.recordBounds(planetModel, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
-      startCutoffPlane.recordBounds(planetModel, bounds, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
-      endCutoffPlane.recordBounds(planetModel, bounds, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
+      bounds.addPoint(start).addPoint(end).addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
+      bounds.addPlane(planetModel, upperConnectingPlane, lowerConnectingPlane, startCutoffPlane, endCutoffPlane);
+      bounds.addPlane(planetModel, lowerConnectingPlane, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
+      bounds.addPlane(planetModel, startCutoffPlane, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
+      bounds.addPlane(planetModel, endCutoffPlane, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
     }
 
   }

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPoint.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPoint.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPoint.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoPoint.java Wed Sep  2 19:59:31 2015
@@ -85,10 +85,12 @@ public class GeoPoint extends Vector {
   public GeoPoint(final double magnitude, final double x, final double y, final double z, double lat, double lon) {
     super(x * magnitude, y * magnitude, z * magnitude);
     this.magnitude = magnitude;
-    if (lat > Math.PI * 0.5 || lat < -Math.PI * 0.5)
-      throw new IllegalArgumentException("Latitude out of range");
-    if (lon < -Math.PI || lon > Math.PI)
-      throw new IllegalArgumentException("Longitude out of range");
+    if (lat > Math.PI * 0.5 || lat < -Math.PI * 0.5) {
+      throw new IllegalArgumentException("Latitude " + lat + " is out of range: must range from -Math.PI/2 to Math.PI/2");
+    }
+    if (lon < -Math.PI || lon > Math.PI) {
+      throw new IllegalArgumentException("Longitude " + lon + " is out of range: must range from -Math.PI to Math.PI");
+    }
     this.latitude = lat;
     this.longitude = lon;
   }
@@ -169,4 +171,24 @@ public class GeoPoint extends Vector {
     }
     return mag;
   }
+  
+  /** Compute whether point matches another.
+   *@param x is the x value
+   *@param y is the y value
+   *@param z is the z value
+   *@return true if the same.
+   */
+  public boolean isIdentical(final double x, final double y, final double z) {
+    return Math.abs(this.x - x) < MINIMUM_RESOLUTION &&
+      Math.abs(this.y - y) < MINIMUM_RESOLUTION &&
+      Math.abs(this.z - z) < MINIMUM_RESOLUTION;
+  }
+  
+  @Override
+  public String toString() {
+    if (this.longitude == Double.NEGATIVE_INFINITY) {
+      return super.toString();
+    }
+    return "[lat="+getLatitude()+", lon="+getLongitude()+"]";
+  }
 }

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoRectangle.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoRectangle.java Wed Sep  2 19:59:31 2015
@@ -112,10 +112,10 @@ public class GeoRectangle extends GeoBas
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the four points
-    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon);
-    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon);
-    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon);
-    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon);
+    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
+    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
 
     final double middleLat = (topLat + bottomLat) * 0.5;
     final double sinMiddleLat = Math.sin(middleLat);
@@ -198,12 +198,13 @@ public class GeoRectangle extends GeoBas
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat)
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.addHorizontalPlane(planetModel, topLat, topPlane, bottomPlane, leftPlane, rightPlane)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane, bottomPlane, leftPlane)
+      .addHorizontalPlane(planetModel, bottomLat, bottomPlane, topPlane, leftPlane, rightPlane)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane, bottomPlane, rightPlane)
+      .addPoint(ULHC).addPoint(URHC).addPoint(LLHC).addPoint(LRHC);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoShape.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoShape.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoShape.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoShape.java Wed Sep  2 19:59:31 2015
@@ -54,14 +54,11 @@ public interface GeoShape extends Member
   public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds);
 
   /**
-   * Compute longitude/latitude bounds for the shape.
+   * Compute bounds for the shape.
    *
-   * @param bounds is the optional input bounds object.  If this is null,
-   *               a bounds object will be created.  Otherwise, the input object will be modified.
-   * @return a Bounds object describing the shape's bounds.  If the bounds cannot
-   * be computed, then return a Bounds object with noLongitudeBound,
-   * noTopLatitudeBound, and noBottomLatitudeBound.
+   * @param bounds is the input bounds object.
+   *             The input object will be modified.
    */
-  public Bounds getBounds(final Bounds bounds);
+  public void getBounds(final Bounds bounds);
 
 }

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthLatitudeZone.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthLatitudeZone.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthLatitudeZone.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthLatitudeZone.java Wed Sep  2 19:59:31 2015
@@ -102,21 +102,11 @@ public class GeoSouthLatitudeZone extend
     return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds);
   }
 
-  /**
-   * Compute longitude/latitude bounds for the shape.
-   *
-   * @param bounds is the optional input bounds object.  If this is null,
-   *               a bounds object will be created.  Otherwise, the input object will be modified.
-   * @return a Bounds object describing the shape's bounds.  If the bounds cannot
-   * be computed, then return a Bounds object with noLongitudeBound,
-   * noTopLatitudeBound, and noBottomLatitudeBound.
-   */
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noLongitudeBound().addLatitudeZone(topLat).noBottomLatitudeBound();
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds
+      .addHorizontalPlane(planetModel, topLat, topPlane);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthRectangle.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoSouthRectangle.java Wed Sep  2 19:59:31 2015
@@ -94,8 +94,8 @@ public class GeoSouthRectangle extends G
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the four points
-    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon);
-    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon);
+    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
 
     final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
     final double sinMiddleLat = Math.sin(middleLat);
@@ -174,12 +174,13 @@ public class GeoSouthRectangle extends G
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addLatitudeZone(topLat).noBottomLatitudeBound()
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds
+      .addHorizontalPlane(planetModel, topLat, topPlane, leftPlane, rightPlane)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane, rightPlane)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane, leftPlane)
+      .addPoint(URHC).addPoint(ULHC).addPoint(planetModel.SOUTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideDegenerateHorizontalLine.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideDegenerateHorizontalLine.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideDegenerateHorizontalLine.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideDegenerateHorizontalLine.java Wed Sep  2 19:59:31 2015
@@ -90,8 +90,8 @@ public class GeoWideDegenerateHorizontal
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the two points
-    this.LHC = new GeoPoint(planetModel, sinLatitude, sinLeftLon, cosLatitude, cosLeftLon);
-    this.RHC = new GeoPoint(planetModel, sinLatitude, sinRightLon, cosLatitude, cosRightLon);
+    this.LHC = new GeoPoint(planetModel, sinLatitude, sinLeftLon, cosLatitude, cosLeftLon, latitude, leftLon);
+    this.RHC = new GeoPoint(planetModel, sinLatitude, sinRightLon, cosLatitude, cosRightLon, latitude, rightLon);
 
     this.plane = new Plane(planetModel, sinLatitude);
 
@@ -167,12 +167,12 @@ public class GeoWideDegenerateHorizontal
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addLatitudeZone(latitude)
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.isWide()
+      .addHorizontalPlane(planetModel, latitude, plane, eitherBound)
+      .addPoint(LHC)
+      .addPoint(RHC);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideLongitudeSlice.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideLongitudeSlice.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideLongitudeSlice.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideLongitudeSlice.java Wed Sep  2 19:59:31 2015
@@ -135,12 +135,13 @@ public class GeoWideLongitudeSlice exten
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noTopLatitudeBound().noBottomLatitudeBound();
-    bounds.addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.isWide()
+      .addVerticalPlane(planetModel, leftLon, leftPlane)
+      .addVerticalPlane(planetModel, rightLon, rightPlane)
+      .addPoint(planetModel.NORTH_POLE)
+      .addPoint(planetModel.SOUTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideNorthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideNorthRectangle.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideNorthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideNorthRectangle.java Wed Sep  2 19:59:31 2015
@@ -94,8 +94,8 @@ public class GeoWideNorthRectangle exten
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the four points
-    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon);
-    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon);
+    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
 
     final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5;
     final double sinMiddleLat = Math.sin(middleLat);
@@ -178,12 +178,13 @@ public class GeoWideNorthRectangle exten
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noTopLatitudeBound().addLatitudeZone(bottomLat)
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.isWide()
+      .addHorizontalPlane(planetModel, bottomLat, bottomPlane, eitherBound)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, bottomPlane)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, bottomPlane)
+      .addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideRectangle.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideRectangle.java Wed Sep  2 19:59:31 2015
@@ -111,10 +111,10 @@ public class GeoWideRectangle extends Ge
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the four points
-    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon);
-    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon);
-    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon);
-    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon);
+    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
+    this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+    this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
 
     final double middleLat = (topLat + bottomLat) * 0.5;
     final double sinMiddleLat = Math.sin(middleLat);
@@ -206,12 +206,14 @@ public class GeoWideRectangle extends Ge
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat)
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.isWide()
+      .addHorizontalPlane(planetModel, topLat, topPlane, bottomPlane, eitherBound)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane, bottomPlane)
+      .addHorizontalPlane(planetModel, bottomLat, bottomPlane, topPlane, eitherBound)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane, bottomPlane)
+      .addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideSouthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideSouthRectangle.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideSouthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWideSouthRectangle.java Wed Sep  2 19:59:31 2015
@@ -94,8 +94,8 @@ public class GeoWideSouthRectangle exten
     final double cosRightLon = Math.cos(rightLon);
 
     // Now build the four points
-    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon);
-    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon);
+    this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+    this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
 
     final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
     final double sinMiddleLat = Math.sin(middleLat);
@@ -177,12 +177,13 @@ public class GeoWideSouthRectangle exten
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.addLatitudeZone(topLat).noBottomLatitudeBound()
-        .addLongitudeSlice(leftLon, rightLon);
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.isWide()
+      .addHorizontalPlane(planetModel, topLat, topPlane, eitherBound)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane)
+      .addPoint(ULHC).addPoint(URHC).addPoint(planetModel.SOUTH_POLE);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWorld.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWorld.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWorld.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/GeoWorld.java Wed Sep  2 19:59:31 2015
@@ -68,11 +68,10 @@ public class GeoWorld extends GeoBaseBBo
   }
 
   @Override
-  public Bounds getBounds(Bounds bounds) {
-    if (bounds == null)
-      bounds = new Bounds();
-    bounds.noLongitudeBound().noTopLatitudeBound().noBottomLatitudeBound();
-    return bounds;
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    // Unnecessary
+    //bounds.noLongitudeBound().noTopLatitudeBound().noBottomLatitudeBound();
   }
 
   @Override

Added: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/LatLonBounds.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/LatLonBounds.java?rev=1700883&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/LatLonBounds.java (added)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/LatLonBounds.java Wed Sep  2 19:59:31 2015
@@ -0,0 +1,323 @@
+package org.apache.lucene.geo3d;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * An object for accumulating latitude/longitude bounds information.
+ *
+ * @lucene.experimental
+ */
+public class LatLonBounds implements Bounds {
+
+  /** Set to true if no longitude bounds can be stated */
+  protected boolean noLongitudeBound = false;
+  /** Set to true if no top latitude bound can be stated */
+  protected boolean noTopLatitudeBound = false;
+  /** Set to true if no bottom latitude bound can be stated */
+  protected boolean noBottomLatitudeBound = false;
+
+  /** If non-null, the minimum latitude bound */
+  protected Double minLatitude = null;
+  /** If non-null, the maximum latitude bound */
+  protected Double maxLatitude = null;
+
+  // For longitude bounds, this class needs to worry about keeping track of the distinction
+  // between left-side bounds and right-side bounds.  Points are always submitted in pairs
+  // which have a maximum longitude separation of Math.PI.  It's therefore always possible
+  // to determine which point represents a left bound, and which point represents a right
+  // bound.
+  //
+  // The next problem is how to compare two of the same kind of bound, e.g. two left bounds.
+  // We need to keep track of the leftmost longitude of the shape, but since this is a circle,
+  // this is arbitrary.  What we could try to do instead would be to find a pair of (left,right) bounds such
+  // that:
+  // (1) all other bounds are within, and
+  // (2) the left minus right distance is minimized
+  // Unfortunately, there are still shapes that cannot be summarized in this way correctly.
+  // For example. consider a spiral that entirely circles the globe; we might arbitrarily choose
+  // lat/lon bounds that do not in fact circle the globe.
+  //
+  // One way to handle the longitude issue correctly is therefore to stipulate that we
+  // walk the bounds of the shape in some kind of connected order.  Each point or circle is therefore
+  // added in a sequence.  We also need an interior point to make sure we have the right
+  // choice of longitude bounds.  But even with this, we still can't always choose whether the actual shape
+  // goes right or left.
+  //
+  // We can make the specification truly general by submitting the following in order:
+  // addSide(PlaneSide side, Membership... constraints)
+  // ...
+  // This is unambiguous, but I still can't see yet how this would help compute the bounds.  The plane
+  // solution would in general seem to boil down to the same logic that relies on points along the path
+  // to define the shape boundaries.  I guess the one thing that you do know for a bounded edge is that
+  // the endpoints are actually connected.  But it is not clear whether relationship helps in any way.
+  //
+  // In any case, if we specify shapes by a sequence of planes, we should stipulate that multiple sequences
+  // are allowed, provided they progressively tile an area of the sphere that is connected and sequential.
+  // For example, paths do alternating rectangles and circles, in sequence.  Each sequence member is
+  // described by a sequence of planes.  I think it would also be reasonable to insist that the first segment
+  // of a shape overlap or adjoin the previous shape.
+  //
+  // Here's a way to think about it that might help: Traversing every edge should grow the longitude bounds
+  // in the direction of the traversal.  So if the traversal is always known to be less than PI in total longitude
+  // angle, then it is possible to use the endpoints to determine the unambiguous extension of the envelope.
+  // For example, say you are currently at longitude -0.5.  The next point is at longitude PI-0.1.  You could say
+  // that the difference in longitude going one way around would be beter than the distance the other way
+  // around, and therefore the longitude envelope should be extended accordingly.  But in practice, when an
+  // edge goes near a pole and may be inclined as well, the longer longitude change might be the right path, even
+  // if the arc length is short.  So this too doesn't work.
+  //
+  // Given we have a hard time making an exact match, here's the current proposal.  The proposal is a
+  // heuristic, based on the idea that most areas are small compared to the circumference of the globe.
+  // We keep track of the last point we saw, and take each point as it arrives, and compute its longitude.
+  // Then, we have a choice as to which way to expand the envelope: we can expand by going to the left or
+  // to the right.  We choose the direction with the least longitude difference.  (If we aren't sure,
+  // and can recognize that, we can set "unconstrained in longitude".)
+
+  /** If non-null, the left longitude bound */
+  protected Double leftLongitude = null;
+  /** If non-null, the right longitude bound */
+  protected Double rightLongitude = null;
+
+  /** Construct an empty bounds object */
+  public LatLonBounds() {
+  }
+
+  // Accessor methods
+  
+  /** Get maximum latitude, if any.
+   *@return maximum latitude or null.
+   */
+  public Double getMaxLatitude() {
+    return maxLatitude;
+  }
+
+  /** Get minimum latitude, if any.
+   *@return minimum latitude or null.
+   */
+  public Double getMinLatitude() {
+    return minLatitude;
+  }
+
+  /** Get left longitude, if any.
+   *@return left longitude, or null.
+   */
+  public Double getLeftLongitude() {
+    return leftLongitude;
+  }
+
+  /** Get right longitude, if any.
+   *@return right longitude, or null.
+   */
+  public Double getRightLongitude() {
+    return rightLongitude;
+  }
+
+  // Degenerate case check
+  
+  /** Check if there's no longitude bound.
+   *@return true if no longitude bound.
+   */
+  public boolean checkNoLongitudeBound() {
+    return noLongitudeBound;
+  }
+
+  /** Check if there's no top latitude bound.
+   *@return true if no top latitude bound.
+   */
+  public boolean checkNoTopLatitudeBound() {
+    return noTopLatitudeBound;
+  }
+
+  /** Check if there's no bottom latitude bound.
+   *@return true if no bottom latitude bound.
+   */
+  public boolean checkNoBottomLatitudeBound() {
+    return noBottomLatitudeBound;
+  }
+
+  // Modification methods
+  
+  @Override
+  public Bounds addPlane(final PlanetModel planetModel, final Plane plane, final Membership... bounds) {
+    plane.recordBounds(planetModel, this, bounds);
+    return this;
+  }
+
+  @Override
+  public Bounds addHorizontalPlane(final PlanetModel planetModel,
+    final double latitude,
+    final Plane horizontalPlane,
+    final Membership... bounds) {
+    if (!noTopLatitudeBound || !noBottomLatitudeBound) {
+      addLatitudeBound(latitude);
+    }
+    return this;
+  }
+    
+  @Override
+  public Bounds addVerticalPlane(final PlanetModel planetModel,
+    final double longitude,
+    final Plane verticalPlane,
+    final Membership... bounds) {
+    if (!noLongitudeBound) {
+      addLongitudeBound(longitude);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds isWide() {
+    return noLongitudeBound();
+  }
+
+  @Override
+  public Bounds addXValue(final GeoPoint point) {
+    if (!noLongitudeBound) {
+      // Get a longitude value
+      addLongitudeBound(point.getLongitude());
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addYValue(final GeoPoint point) {
+    if (!noLongitudeBound) {
+      // Get a longitude value
+      addLongitudeBound(point.getLongitude());
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addZValue(final GeoPoint point) {
+    if (!noTopLatitudeBound || !noBottomLatitudeBound) {
+      // Compute a latitude value
+      double latitude = point.getLatitude();
+      addLatitudeBound(latitude);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addPoint(GeoPoint point) {
+    if (!noLongitudeBound) {
+      // Get a longitude value
+      addLongitudeBound(point.getLongitude());
+    }
+    if (!noTopLatitudeBound || !noBottomLatitudeBound) {
+      // Compute a latitude value
+      addLatitudeBound(point.getLatitude());
+    }
+    return this;
+  }
+  
+  @Override
+  public Bounds noLongitudeBound() {
+    noLongitudeBound = true;
+    leftLongitude = null;
+    rightLongitude = null;
+    return this;
+  }
+
+  @Override
+  public Bounds noTopLatitudeBound() {
+    noTopLatitudeBound = true;
+    maxLatitude = null;
+    return this;
+  }
+
+  @Override
+  public Bounds noBottomLatitudeBound() {
+    noBottomLatitudeBound = true;
+    minLatitude = null;
+    return this;
+  }
+
+  // Protected methods
+  
+  /** Update latitude bound.
+   *@param latitude is the latitude.
+   */
+  protected void addLatitudeBound(double latitude) {
+    if (!noTopLatitudeBound && (maxLatitude == null || latitude > maxLatitude))
+      maxLatitude = latitude;
+    if (!noBottomLatitudeBound && (minLatitude == null || latitude < minLatitude))
+      minLatitude = latitude;
+  }
+
+  /** Update longitude bound.
+   *@param longitude is the new longitude value.
+   */
+  protected void addLongitudeBound(double longitude) {
+    // If this point is within the current bounds, we're done; otherwise
+    // expand one side or the other.
+    if (leftLongitude == null && rightLongitude == null) {
+      leftLongitude = longitude;
+      rightLongitude = longitude;
+    } else {
+      // Compute whether we're to the right of the left value.  But the left value may be greater than
+      // the right value.
+      double currentLeftLongitude = leftLongitude;
+      double currentRightLongitude = rightLongitude;
+      if (currentRightLongitude < currentLeftLongitude)
+        currentRightLongitude += 2.0 * Math.PI;
+      // We have a range to look at that's going in the right way.
+      // Now, do the same trick with the computed longitude.
+      if (longitude < currentLeftLongitude)
+        longitude += 2.0 * Math.PI;
+
+      if (longitude < currentLeftLongitude || longitude > currentRightLongitude) {
+        // Outside of current bounds.  Consider carefully how we'll expand.
+        double leftExtensionAmt;
+        double rightExtensionAmt;
+        if (longitude < currentLeftLongitude) {
+          leftExtensionAmt = currentLeftLongitude - longitude;
+        } else {
+          leftExtensionAmt = currentLeftLongitude + 2.0 * Math.PI - longitude;
+        }
+        if (longitude > currentRightLongitude) {
+          rightExtensionAmt = longitude - currentRightLongitude;
+        } else {
+          rightExtensionAmt = longitude + 2.0 * Math.PI - currentRightLongitude;
+        }
+        if (leftExtensionAmt < rightExtensionAmt) {
+          currentLeftLongitude = leftLongitude - leftExtensionAmt;
+          while (currentLeftLongitude <= -Math.PI) {
+            currentLeftLongitude += 2.0 * Math.PI;
+          }
+          leftLongitude = currentLeftLongitude;
+        } else {
+          currentRightLongitude = rightLongitude + rightExtensionAmt;
+          while (currentRightLongitude > Math.PI) {
+            currentRightLongitude -= 2.0 * Math.PI;
+          }
+          rightLongitude = currentRightLongitude;
+        }
+      }
+    }
+    double testRightLongitude = rightLongitude;
+    if (testRightLongitude < leftLongitude)
+      testRightLongitude += Math.PI * 2.0;
+    if (testRightLongitude - leftLongitude >= Math.PI) {
+      noLongitudeBound = true;
+      leftLongitude = null;
+      rightLongitude = null;
+    }
+  }
+
+}

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Plane.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Plane.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Plane.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Plane.java Wed Sep  2 19:59:31 2015
@@ -28,6 +28,13 @@ public class Plane extends Vector {
   protected final static GeoPoint[] NO_POINTS = new GeoPoint[0];
   /** An array with no bounds in it */
   protected final static Membership[] NO_BOUNDS = new Membership[0];
+  /** A vertical plane normal to the Y axis */
+  protected final static Plane normalYPlane = new Plane(0.0,1.0,0.0,0.0);
+  /** A vertical plane normal to the X axis */
+  protected final static Plane normalXPlane = new Plane(1.0,0.0,0.0,0.0);
+  /** A vertical plane normal to the Z axis */
+  protected final static Plane normalZPlane = new Plane(0.0,0.0,1.0,0.0);
+
   /** Ax + By + Cz + D = 0 */
   public final double D;
 
@@ -88,12 +95,12 @@ public class Plane extends Vector {
     this.D = D;
   }
 
-  /** Construct the most accurate normalized, vertical plane given a set of points.  If none of the points can determine
-   * the plane, return null.
-   * @param planePoints is a set of points to choose from.  The best one for constructing the most precise normal plane is picked.
-   * @return the normal plane
+  /** Construct the most accurate normalized plane through an x-y point and including the Z axis.
+   * If none of the points can determine the plane, return null.
+   * @param planePoints is a set of points to choose from.  The best one for constructing the most precise plane is picked.
+   * @return the plane
    */
-  public static Plane constructNormalizedVerticalPlane(final Vector... planePoints) {
+  public static Plane constructNormalizedZPlane(final Vector... planePoints) {
     // Pick the best one (with the greatest x-y distance)
     double bestDistance = 0.0;
     Vector bestPoint = null;
@@ -104,19 +111,86 @@ public class Plane extends Vector {
         bestPoint = point;
       }
     }
-    return constructNormalizedVerticalPlane(bestPoint.x, bestPoint.y);
+    return constructNormalizedZPlane(bestPoint.x, bestPoint.y);
+  }
+
+  /** Construct the most accurate normalized plane through an x-z point and including the Y axis.
+   * If none of the points can determine the plane, return null.
+   * @param planePoints is a set of points to choose from.  The best one for constructing the most precise plane is picked.
+   * @return the plane
+   */
+  public static Plane constructNormalizedYPlane(final Vector... planePoints) {
+    // Pick the best one (with the greatest x-z distance)
+    double bestDistance = 0.0;
+    Vector bestPoint = null;
+    for (final Vector point : planePoints) {
+      final double pointDist = point.x * point.x + point.z * point.z;
+      if (pointDist > bestDistance) {
+        bestDistance = pointDist;
+        bestPoint = point;
+      }
+    }
+    return constructNormalizedYPlane(bestPoint.x, bestPoint.z, 0.0);
   }
 
-  /** Construct a normalized, vertical plane through an x-y point.  If the x-y point is at (0,0), return null.
+  /** Construct the most accurate normalized plane through an y-z point and including the X axis.
+   * If none of the points can determine the plane, return null.
+   * @param planePoints is a set of points to choose from.  The best one for constructing the most precise plane is picked.
+   * @return the plane
+   */
+  public static Plane constructNormalizedXPlane(final Vector... planePoints) {
+    // Pick the best one (with the greatest y-z distance)
+    double bestDistance = 0.0;
+    Vector bestPoint = null;
+    for (final Vector point : planePoints) {
+      final double pointDist = point.y * point.y + point.z * point.z;
+      if (pointDist > bestDistance) {
+        bestDistance = pointDist;
+        bestPoint = point;
+      }
+    }
+    return constructNormalizedXPlane(bestPoint.y, bestPoint.z, 0.0);
+  }
+
+  /** Construct a normalized plane through an x-y point and including the Z axis.
+   * If the x-y point is at (0,0), return null.
    * @param x is the x value.
    * @param y is the y value.
-   * @return a vertical plane passing through the center and (x,y,0).
+   * @return a plane passing through the Z axis and (x,y,0).
    */
-  public static Plane constructNormalizedVerticalPlane(final double x, final double y) {
+  public static Plane constructNormalizedZPlane(final double x, final double y) {
     if (Math.abs(x) < MINIMUM_RESOLUTION && Math.abs(y) < MINIMUM_RESOLUTION)
       return null;
     final double denom = 1.0 / Math.sqrt(x*x + y*y);
-    return new Plane(x * denom, y * denom);
+    return new Plane(y * denom, -x * denom, 0.0, 0.0);
+  }
+
+  /** Construct a normalized plane through an x-z point and parallel to the Y axis.
+   * If the x-z point is at (0,0), return null.
+   * @param x is the x value.
+   * @param z is the z value.
+   * @param DValue is the offset from the origin for the plane.
+   * @return a plane parallel to the Y axis and perpendicular to the x and z values given.
+   */
+  public static Plane constructNormalizedYPlane(final double x, final double z, final double DValue) {
+    if (Math.abs(x) < MINIMUM_RESOLUTION && Math.abs(z) < MINIMUM_RESOLUTION)
+      return null;
+    final double denom = 1.0 / Math.sqrt(x*x + z*z);
+    return new Plane(z * denom, 0.0, -x * denom, DValue);
+  }
+
+  /** Construct a normalized plane through a y-z point and parallel to the X axis.
+   * If the y-z point is at (0,0), return null.
+   * @param y is the y value.
+   * @param z is the z value.
+   * @param DValue is the offset from the origin for the plane.
+   * @return a plane parallel to the X axis and perpendicular to the y and z values given.
+   */
+  public static Plane constructNormalizedXPlane(final double y, final double z, final double DValue) {
+    if (Math.abs(y) < MINIMUM_RESOLUTION && Math.abs(z) < MINIMUM_RESOLUTION)
+      return null;
+    final double denom = 1.0 / Math.sqrt(y*y + z*z);
+    return new Plane(0.0, z * denom, -y * denom, DValue);
   }
   
   /**
@@ -695,35 +769,149 @@ public class Plane extends Vector {
       throw new RuntimeException("Intersection point not on ellipsoid; point="+point);
   }
   */
-  
+
   /**
-   * Accumulate bounds information for this plane, intersected with another plane
-   * and with the unit sphere.
-   * Updates both latitude and longitude information, using max/min points found
+   * Accumulate (x,y,z) bounds information for this plane, intersected with the unit sphere.
+   * Updates min/max information, using max/min points found
    * within the specified bounds.
    *
-   * @param planetModel is the planet model to use to determine bounding points
-   * @param q          is the plane to intersect with.
-   * @param boundsInfo is the info to update with additional bounding information.
+   * @param planetModel is the planet model to use in determining bounds.
+   * @param boundsInfo is the xyz info to update with additional bounding information.
    * @param bounds     are the surfaces delineating what's inside the shape.
    */
-  public void recordBounds(final PlanetModel planetModel, final Plane q, final Bounds boundsInfo, final Membership... bounds) {
-    final GeoPoint[] intersectionPoints = findIntersections(planetModel, q, bounds, NO_BOUNDS);
-    for (GeoPoint intersectionPoint : intersectionPoints) {
-      boundsInfo.addPoint(intersectionPoint);
+  public void recordBounds(final PlanetModel planetModel, final XYZBounds boundsInfo, final Membership... bounds) {
+    // Basic plan is to do three intersections of the plane and the planet.
+    // For min/max x, we intersect a vertical plane such that y = 0.
+    // For min/max y, we intersect a vertical plane such that x = 0.
+    // For min/max z, we intersect a vertical plane that is chosen to go through the high point of the arc.
+    // For clarity, load local variables with good names
+    final double A = this.x;
+    final double B = this.y;
+    final double C = this.z;
+
+    // For the X and Y values, we need a D value, which is the AVERAGE D value
+    // for two planes that pass through the two Z points determined here, for the axis in question.
+    final GeoPoint[] zPoints;
+    if (!boundsInfo.isSmallestMinX(planetModel) || !boundsInfo.isLargestMaxX(planetModel) ||
+      !boundsInfo.isSmallestMinY(planetModel) || !boundsInfo.isLargestMaxY(planetModel)) {
+      if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) {
+        // We need unconstrained values in order to compute D
+        zPoints = findIntersections(planetModel, constructNormalizedZPlane(A,B), NO_BOUNDS, NO_BOUNDS);
+        if (zPoints.length == 0) {
+          // No intersections, so plane never intersects world.
+          //System.err.println("  plane never intersects world");
+          return;
+        }
+        //for (final GeoPoint p : zPoints) {
+        //  System.err.println("zPoint: "+p);
+        //}
+      } else {
+        zPoints = null;
+      }
+    } else {
+      zPoints = null;
     }
-  }
 
+    // Do Z.
+    if (!boundsInfo.isSmallestMinZ(planetModel) || !boundsInfo.isLargestMaxZ(planetModel)) {
+      //System.err.println("    computing Z bound");
+      // Compute Z bounds for this arc
+      // With ellipsoids, we really have only one viable way to do this computation.
+      // Specifically, we compute an appropriate vertical plane, based on the current plane's x-y orientation, and
+      // then intersect it with this one and with the ellipsoid.  This gives us zero, one, or two points to use
+      // as bounds.
+      // There is one special case: horizontal circles.  These require TWO vertical planes: one for the x, and one for
+      // the y, and we use all four resulting points in the bounds computation.
+      if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) {
+        // NOT a degenerate case
+        //System.err.println("    not degenerate");
+        final Plane normalizedZPlane = constructNormalizedZPlane(A,B);
+        final GeoPoint[] points = findIntersections(planetModel, normalizedZPlane, bounds, NO_BOUNDS);
+        for (final GeoPoint point : points) {
+          assert planetModel.pointOnSurface(point);
+          //System.err.println("      Point = "+point+"; this.evaluate(point)="+this.evaluate(point)+"; normalizedZPlane.evaluate(point)="+normalizedZPlane.evaluate(point));
+          addPoint(boundsInfo, bounds, point);
+        }
+      } else {
+        // Since a==b==0, any plane including the Z axis suffices.
+        //System.err.println("      Perpendicular to z");
+        final GeoPoint[] points = findIntersections(planetModel, normalYPlane, NO_BOUNDS, NO_BOUNDS);
+        boundsInfo.addZValue(points[0]);
+      }
+    }
+        
+    if (!boundsInfo.isSmallestMinX(planetModel) || !boundsInfo.isLargestMaxX(planetModel)) {
+      //System.err.println("    computing X bound");
+      if ((Math.abs(B) >= MINIMUM_RESOLUTION || Math.abs(C) >= MINIMUM_RESOLUTION)) {
+        // NOT a degenerate case.  Compute D.
+        //System.err.println("    not degenerate; B="+B+"; C="+C);
+        final Plane originPlane = constructNormalizedXPlane(B,C,0.0);
+        double DValue = 0.0;
+        if (zPoints != null) {
+          for (final GeoPoint p : zPoints) {
+            final double zValue = originPlane.evaluate(p);
+            //System.err.println("    originPlane.evaluate(zpoint)="+zValue);
+            DValue += zValue;
+          }
+          DValue /= (double)zPoints.length;
+        }
+        final Plane normalizedXPlane = constructNormalizedXPlane(B,C,-DValue);
+        final GeoPoint[] points = findIntersections(planetModel, normalizedXPlane, bounds, NO_BOUNDS);
+        for (final GeoPoint point : points) {
+          assert planetModel.pointOnSurface(point);
+          //System.err.println("      Point = "+point+"; this.evaluate(point)="+this.evaluate(point)+"; normalizedXPlane.evaluate(point)="+normalizedXPlane.evaluate(point));
+          addPoint(boundsInfo, bounds, point);
+        }
+      } else {
+        // Since b==c==0, any plane including the X axis suffices.
+        //System.err.println("      Perpendicular to x");
+        final GeoPoint[] points = findIntersections(planetModel, normalZPlane, NO_BOUNDS, NO_BOUNDS);
+        boundsInfo.addXValue(points[0]);
+      }
+    }
+    
+    // Do Y
+    if (!boundsInfo.isSmallestMinY(planetModel) || !boundsInfo.isLargestMaxY(planetModel)) {
+      //System.err.println("    computing Y bound");
+      if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(C) >= MINIMUM_RESOLUTION)) {
+        // NOT a degenerate case.  Compute D.
+        //System.err.println("    not degenerate");
+        final Plane originPlane = constructNormalizedYPlane(A,C,0.0);
+        double DValue = 0.0;
+        if (zPoints != null) {
+          for (final GeoPoint p : zPoints) {
+            DValue += originPlane.evaluate(p);
+          }
+          DValue /= (double)zPoints.length;
+        }
+        final Plane normalizedYPlane = constructNormalizedYPlane(A,C,-DValue);
+        final GeoPoint[] points = findIntersections(planetModel, normalizedYPlane, bounds, NO_BOUNDS);
+        for (final GeoPoint point : points) {
+          assert planetModel.pointOnSurface(point);
+          //System.err.println("      Point = "+point+"; this.evaluate(point)="+this.evaluate(point)+"; normalizedYPlane.evaluate(point)="+normalizedYPlane.evaluate(point));
+          addPoint(boundsInfo, bounds, point);
+        }
+      } else {
+        // Since a==c==0, any plane including the Y axis suffices.
+        // It doesn't matter that we may discard the point due to bounds, because if there are bounds, we'll have endpoints
+        // that will be tallied separately.
+        //System.err.println("      Perpendicular to y");
+        final GeoPoint[] points = findIntersections(planetModel, normalXPlane, NO_BOUNDS, NO_BOUNDS);
+        boundsInfo.addYValue(points[0]);
+      }
+    }
+  }
+  
   /**
    * Accumulate bounds information for this plane, intersected with the unit sphere.
    * Updates both latitude and longitude information, using max/min points found
    * within the specified bounds.
    *
    * @param planetModel is the planet model to use in determining bounds.
-   * @param boundsInfo is the info to update with additional bounding information.
+   * @param boundsInfo is the lat/lon info to update with additional bounding information.
    * @param bounds     are the surfaces delineating what's inside the shape.
    */
-  public void recordBounds(final PlanetModel planetModel, final Bounds boundsInfo, final Membership... bounds) {
+  public void recordBounds(final PlanetModel planetModel, final LatLonBounds boundsInfo, final Membership... bounds) {
     // For clarity, load local variables with good names
     final double A = this.x;
     final double B = this.y;
@@ -741,18 +929,15 @@ public class Plane extends Vector {
       if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) {
         // NOT a horizontal circle!
         //System.err.println(" Not a horizontal circle");
-        final Plane verticalPlane = constructNormalizedVerticalPlane(A,B);
-        final GeoPoint[] points = findIntersections(planetModel, verticalPlane, NO_BOUNDS, NO_BOUNDS);
+        final Plane verticalPlane = constructNormalizedZPlane(A,B);
+        final GeoPoint[] points = findIntersections(planetModel, verticalPlane, bounds, NO_BOUNDS);
         for (final GeoPoint point : points) {
-          addPoint(boundsInfo, bounds, point.x, point.y, point.z);
+          addPoint(boundsInfo, bounds, point);
         }
       } else {
-        // Horizontal circle.  Since a==b, one vertical plane suffices.
-        final Plane verticalPlane = new Plane(1.0,0.0);
-        final GeoPoint[] points = findIntersections(planetModel, verticalPlane, NO_BOUNDS, NO_BOUNDS);
-        // There will always be two points; we only need one.
-        final GeoPoint point = points[0];
-        boundsInfo.addHorizontalCircle(point.z/Math.sqrt(point.x * point.x + point.y * point.y + point.z * point.z));
+        // Horizontal circle.  Since a==b, any vertical plane suffices.
+        final GeoPoint[] points = findIntersections(planetModel, normalXPlane, NO_BOUNDS, NO_BOUNDS);
+        boundsInfo.addZValue(points[0]);
       }
       //System.err.println("Done latitude bounds");
     }
@@ -808,7 +993,7 @@ public class Plane extends Vector {
               double y0 = -b / (2.0 * a);
               double x0 = (-D - B * y0) / A;
               double z0 = 0.0;
-              addPoint(boundsInfo, bounds, x0, y0, z0);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0, y0, z0));
             } else if (sqrtClause > 0.0) {
               double sqrtResult = Math.sqrt(sqrtClause);
               double denom = 1.0 / (2.0 * a);
@@ -823,8 +1008,8 @@ public class Plane extends Vector {
               double z0a = 0.0;
               double z0b = 0.0;
 
-              addPoint(boundsInfo, bounds, x0a, y0a, z0a);
-              addPoint(boundsInfo, bounds, x0b, y0b, z0b);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0a, y0a, z0a));
+              addPoint(boundsInfo, bounds, new GeoPoint(x0b, y0b, z0b));
             }
 
           } else {
@@ -841,7 +1026,7 @@ public class Plane extends Vector {
               double x0 = -b / (2.0 * a);
               double y0 = (-D - A * x0) / B;
               double z0 = 0.0;
-              addPoint(boundsInfo, bounds, x0, y0, z0);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0, y0, z0));
             } else if (sqrtClause > 0.0) {
               double sqrtResult = Math.sqrt(sqrtClause);
               double denom = 1.0 / (2.0 * a);
@@ -854,8 +1039,8 @@ public class Plane extends Vector {
               double z0a = 0.0;
               double z0b = 0.0;
 
-              addPoint(boundsInfo, bounds, x0a, y0a, z0a);
-              addPoint(boundsInfo, bounds, x0b, y0b, z0b);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0a, y0a, z0a));
+              addPoint(boundsInfo, bounds, new GeoPoint(x0b, y0b, z0b));
             }
           }
         }
@@ -949,7 +1134,7 @@ public class Plane extends Vector {
               double x0 = (-2.0 * J - I * y0) / H;
               double z0 = (-A * x0 - B * y0 - D) / C;
 
-              addPoint(boundsInfo, bounds, x0, y0, z0);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0, y0, z0));
             } else if (sqrtClause > 0.0) {
               //System.err.println(" Two solutions");
               double sqrtResult = Math.sqrt(sqrtClause);
@@ -964,8 +1149,8 @@ public class Plane extends Vector {
               double z0a = (-A * x0a - B * y0a - D) * Cdenom;
               double z0b = (-A * x0b - B * y0b - D) * Cdenom;
 
-              addPoint(boundsInfo, bounds, x0a, y0a, z0a);
-              addPoint(boundsInfo, bounds, x0b, y0b, z0b);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0a, y0a, z0a));
+              addPoint(boundsInfo, bounds, new GeoPoint(x0b, y0b, z0b));
             }
 
           } else {
@@ -1001,7 +1186,7 @@ public class Plane extends Vector {
               double z0 = (-A * x0 - B * y0 - D) / C;
               // Verify that x&y fulfill the equation
               // 2Ex^2 + 2Fy^2 + 2Gxy + Hx + Iy = 0
-              addPoint(boundsInfo, bounds, x0, y0, z0);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0, y0, z0));
             } else if (sqrtClause > 0.0) {
               //System.err.println(" Two solutions");
               double sqrtResult = Math.sqrt(sqrtClause);
@@ -1016,8 +1201,8 @@ public class Plane extends Vector {
               double z0a = (-A * x0a - B * y0a - D) * Cdenom;
               double z0b = (-A * x0b - B * y0b - D) * Cdenom;
 
-              addPoint(boundsInfo, bounds, x0a, y0a, z0a);
-              addPoint(boundsInfo, bounds, x0b, y0b, z0b);
+              addPoint(boundsInfo, bounds, new GeoPoint(x0a, y0a, z0a));
+              addPoint(boundsInfo, bounds, new GeoPoint(x0b, y0b, z0b));
             }
           }
         }
@@ -1029,10 +1214,26 @@ public class Plane extends Vector {
   /** Add a point to boundsInfo if within a specifically bounded area.
    * @param boundsInfo is the object to be modified.
    * @param bounds is the area that the point must be within.
+   * @param point is the point.
+   */
+  protected static void addPoint(final Bounds boundsInfo, final Membership[] bounds, final GeoPoint point) {
+    // Make sure the discovered point is within the bounds
+    for (Membership bound : bounds) {
+      if (!bound.isWithin(point))
+        return;
+    }
+    // Add the point
+    boundsInfo.addPoint(point);
+  }
+
+  /** Add a point to boundsInfo if within a specifically bounded area.
+   * @param boundsInfo is the object to be modified.
+   * @param bounds is the area that the point must be within.
    * @param x is the x value.
    * @param y is the y value.
    * @param z is the z value.
    */
+  /*
   protected static void addPoint(final Bounds boundsInfo, final Membership[] bounds, final double x, final double y, final double z) {
     //System.err.println(" Want to add point x="+x+" y="+y+" z="+z);
     // Make sure the discovered point is within the bounds
@@ -1045,6 +1246,7 @@ public class Plane extends Vector {
     //System.out.println("Adding point x="+x+" y="+y+" z="+z);
     boundsInfo.addPoint(x, y, z);
   }
+  */
 
   /**
    * Determine whether the plane intersects another plane within the

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/PlanetModel.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/PlanetModel.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/PlanetModel.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/PlanetModel.java Wed Sep  2 19:59:31 2015
@@ -65,6 +65,14 @@ public class PlanetModel {
   public final GeoPoint NORTH_POLE;
   /** South pole */
   public final GeoPoint SOUTH_POLE;
+  /** Min X pole */
+  public final GeoPoint MIN_X_POLE;
+  /** Max X pole */
+  public final GeoPoint MAX_X_POLE;
+  /** Min Y pole */
+  public final GeoPoint MIN_Y_POLE;
+  /** Max Y pole */
+  public final GeoPoint MAX_Y_POLE;
   
   /** Constructor.
    * @param ab is the x/y scaling factor.
@@ -81,6 +89,10 @@ public class PlanetModel {
     this.inverseCSquared = inverseC * inverseC;
     this.NORTH_POLE = new GeoPoint(c, 0.0, 0.0, 1.0, Math.PI * 0.5, 0.0);
     this.SOUTH_POLE = new GeoPoint(c, 0.0, 0.0, -1.0, -Math.PI * 0.5, 0.0);
+    this.MIN_X_POLE = new GeoPoint(ab, -1.0, 0.0, 0.0, 0.0, -Math.PI);
+    this.MAX_X_POLE = new GeoPoint(ab, 1.0, 0.0, 0.0, 0.0, 0.0);
+    this.MIN_Y_POLE = new GeoPoint(ab, 0.0, -1.0, 0.0, 0.0, -Math.PI * 0.5);
+    this.MAX_Y_POLE = new GeoPoint(ab, 0.0, 1.0, 0.0, 0.0, Math.PI * 0.5);
   }
   
   /** Find the minimum magnitude of all points on the ellipsoid.
@@ -97,6 +109,86 @@ public class PlanetModel {
     return Math.max(this.ab, this.c);
   }
   
+  /** Find the minimum x value.
+   *@return the minimum X value.
+   */
+  public double getMinimumXValue() {
+    return -this.ab;
+  }
+  
+  /** Find the maximum x value.
+   *@return the maximum X value.
+   */
+  public double getMaximumXValue() {
+    return this.ab;
+  }
+
+  /** Find the minimum y value.
+   *@return the minimum Y value.
+   */
+  public double getMinimumYValue() {
+    return -this.ab;
+  }
+  
+  /** Find the maximum y value.
+   *@return the maximum Y value.
+   */
+  public double getMaximumYValue() {
+    return this.ab;
+  }
+  
+  /** Find the minimum z value.
+   *@return the minimum Z value.
+   */
+  public double getMinimumZValue() {
+    return -this.c;
+  }
+  
+  /** Find the maximum z value.
+   *@return the maximum Z value.
+   */
+  public double getMaximumZValue() {
+    return this.c;
+  }
+
+  /** Check if point is on surface.
+   * @param v is the point to check.
+   * @return true if the point is on the planet surface.
+   */
+  public boolean pointOnSurface(final Vector v) {
+    return pointOnSurface(v.x, v.y, v.z);
+  }
+  
+  /** Check if point is on surface.
+   * @param x is the x coord.
+   * @param y is the y coord.
+   * @param z is the z coord.
+   */
+  public boolean pointOnSurface(final double x, final double y, final double z) {
+    // Equation of planet surface is:
+    // x^2 / a^2 + y^2 / b^2 + z^2 / c^2 - 1 = 0
+    return Math.abs((x * x + y * y) * inverseAb * inverseAb + z * z * inverseC * inverseC - 1.0) < Vector.MINIMUM_RESOLUTION;
+  }
+
+  /** Check if point is outside surface.
+   * @param v is the point to check.
+   * @return true if the point is outside the planet surface.
+   */
+  public boolean pointOutside(final Vector v) {
+    return pointOutside(v.x, v.y, v.z);
+  }
+  
+  /** Check if point is outside surface.
+   * @param x is the x coord.
+   * @param y is the y coord.
+   * @param z is the z coord.
+   */
+  public boolean pointOutside(final double x, final double y, final double z) {
+    // Equation of planet surface is:
+    // x^2 / a^2 + y^2 / b^2 + z^2 / c^2 - 1 = 0
+    return (x * x + y * y) * inverseAb * inverseAb + z * z * inverseC * inverseC - 1.0 > Vector.MINIMUM_RESOLUTION;
+  }
+  
   /** Compute surface distance between two points.
    * @param p1 is the first point.
    * @param p2 is the second point.

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/SidedPlane.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/SidedPlane.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/SidedPlane.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/SidedPlane.java Wed Sep  2 19:59:31 2015
@@ -48,6 +48,8 @@ public class SidedPlane extends Plane im
   public SidedPlane(Vector p, Vector A, Vector B) {
     super(A, B);
     sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
   }
 
   /**
@@ -60,6 +62,8 @@ public class SidedPlane extends Plane im
   public SidedPlane(Vector p, final PlanetModel planetModel, double sinLat) {
     super(planetModel, sinLat);
     sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
   }
 
   /**
@@ -72,6 +76,8 @@ public class SidedPlane extends Plane im
   public SidedPlane(Vector p, double x, double y) {
     super(x, y);
     sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
   }
 
   /**
@@ -84,6 +90,24 @@ public class SidedPlane extends Plane im
   public SidedPlane(Vector p, Vector v, double D) {
     super(v, D);
     sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
+  }
+
+  /**
+   * Construct a sided plane with a normal vector and offset.
+   *
+   * @param pX X coord of point to evaluate.
+   * @param pY Y coord of point to evaluate.
+   * @param pZ Z coord of point to evaluate.
+   * @param v is the normal vector.
+   * @param D is the origin offset for the plan.
+   */
+  public SidedPlane(double pX, double pY, double pZ, Vector v, double D) {
+    super(v, D);
+    sigNum = Math.signum(evaluate(pX,pY,pZ));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
   }
 
   /** Construct a sided plane from two points and a third normal vector.

Modified: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Vector.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Vector.java?rev=1700883&r1=1700882&r2=1700883&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Vector.java (original)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/Vector.java Wed Sep  2 19:59:31 2015
@@ -28,7 +28,7 @@ public class Vector {
    * Values that are all considered to be essentially zero have a magnitude
    * less than this.
    */
-  public static final double MINIMUM_RESOLUTION = 1e-12;
+  public static final double MINIMUM_RESOLUTION = 5.0e-13;
   /**
    * For squared quantities, the bound is squared too.
    */

Added: lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/XYZBounds.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/XYZBounds.java?rev=1700883&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/XYZBounds.java (added)
+++ lucene/dev/trunk/lucene/spatial3d/src/java/org/apache/lucene/geo3d/XYZBounds.java Wed Sep  2 19:59:31 2015
@@ -0,0 +1,268 @@
+package org.apache.lucene.geo3d;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * An object for accumulating XYZ bounds information.
+ *
+ * @lucene.experimental
+ */
+public class XYZBounds implements Bounds {
+
+  /** A 'fudge factor', which is added to maximums and subtracted from minimums,
+   * in order to compensate for potential error deltas.  This would not be necessary
+   * except that our 'bounds' is defined as always equaling or exceeding the boundary
+   * of the shape, and we cannot guarantee that without making MINIMUM_RESOLUTION
+   * unacceptably large.
+   */
+  protected static final double FUDGE_FACTOR = Vector.MINIMUM_RESOLUTION * 2e6;
+  
+  /** Minimum x */
+  protected Double minX = null;
+  /** Maximum x */
+  protected Double maxX = null;
+  /** Minimum y */
+  protected Double minY = null;
+  /** Maximum y */
+  protected Double maxY = null;
+  /** Minimum z */
+  protected Double minZ = null;
+  /** Maximum z */
+  protected Double maxZ = null;
+  
+  /** Set to true if no longitude bounds can be stated */
+  protected boolean noLongitudeBound = false;
+  /** Set to true if no top latitude bound can be stated */
+  protected boolean noTopLatitudeBound = false;
+  /** Set to true if no bottom latitude bound can be stated */
+  protected boolean noBottomLatitudeBound = false;
+
+  /** Construct an empty bounds object */
+  public XYZBounds() {
+  }
+
+  // Accessor methods
+  
+  /** Return the minimum X value.
+   *@return minimum X value.
+   */
+  public Double getMinimumX() {
+    return minX;
+  }
+
+  /** Return the maximum X value.
+   *@return maximum X value.
+   */
+  public Double getMaximumX() {
+    return maxX;
+  }
+
+  /** Return the minimum Y value.
+   *@return minimum Y value.
+   */
+  public Double getMinimumY() {
+    return minY;
+  }
+
+  /** Return the maximum Y value.
+   *@return maximum Y value.
+   */
+  public Double getMaximumY() {
+    return maxY;
+  }
+  
+  /** Return the minimum Z value.
+   *@return minimum Z value.
+   */
+  public Double getMinimumZ() {
+    return minZ;
+  }
+
+  /** Return the maximum Z value.
+   *@return maximum Z value.
+   */
+  public Double getMaximumZ() {
+    return maxZ;
+  }
+
+  /** Return true if minX is as small as the planet model allows.
+   *@return true if minX has reached its bound.
+   */
+  public boolean isSmallestMinX(final PlanetModel planetModel) {
+    if (minX == null)
+      return false;
+    return minX - planetModel.getMinimumXValue() < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if maxX is as large as the planet model allows.
+   *@return true if maxX has reached its bound.
+   */
+  public boolean isLargestMaxX(final PlanetModel planetModel) {
+    if (maxX == null)
+      return false;
+    return planetModel.getMaximumXValue() - maxX < Vector.MINIMUM_RESOLUTION;
+  }
+
+  /** Return true if minY is as small as the planet model allows.
+   *@return true if minY has reached its bound.
+   */
+  public boolean isSmallestMinY(final PlanetModel planetModel) {
+    if (minY == null)
+      return false;
+    return minY - planetModel.getMinimumYValue() < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if maxY is as large as the planet model allows.
+   *@return true if maxY has reached its bound.
+   */
+  public boolean isLargestMaxY(final PlanetModel planetModel) {
+    if (maxY == null)
+      return false;
+    return planetModel.getMaximumYValue() - maxY < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if minZ is as small as the planet model allows.
+   *@return true if minZ has reached its bound.
+   */
+  public boolean isSmallestMinZ(final PlanetModel planetModel) {
+    if (minZ == null)
+      return false;
+    return minZ - planetModel.getMinimumZValue() < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if maxZ is as large as the planet model allows.
+   *@return true if maxZ has reached its bound.
+   */
+  public boolean isLargestMaxZ(final PlanetModel planetModel) {
+    if (maxZ == null)
+      return false;
+    return planetModel.getMaximumZValue() - maxZ < Vector.MINIMUM_RESOLUTION;
+  }
+
+  // Modification methods
+  
+  @Override
+  public Bounds addPlane(final PlanetModel planetModel, final Plane plane, final Membership... bounds) {
+    plane.recordBounds(planetModel, this, bounds);
+    return this;
+  }
+
+  /** Add a horizontal plane to the bounds description.
+   * This method should EITHER use the supplied latitude, OR use the supplied
+   * plane, depending on what is most efficient.
+   *@param planetModel is the planet model.
+   *@param latitude is the latitude.
+   *@param horizontalPlane is the plane.
+   *@param bounds are the constraints on the plane.
+   *@return updated Bounds object.
+   */
+  public Bounds addHorizontalPlane(final PlanetModel planetModel,
+    final double latitude,
+    final Plane horizontalPlane,
+    final Membership... bounds) {
+    return addPlane(planetModel, horizontalPlane, bounds);
+  }
+    
+  /** Add a vertical plane to the bounds description.
+   * This method should EITHER use the supplied longitude, OR use the supplied
+   * plane, depending on what is most efficient.
+   *@param planetModel is the planet model.
+   *@param longitude is the longitude.
+   *@param verticalPlane is the plane.
+   *@param bounds are the constraints on the plane.
+   *@return updated Bounds object.
+   */
+  public Bounds addVerticalPlane(final PlanetModel planetModel,
+    final double longitude,
+    final Plane verticalPlane,
+    final Membership... bounds) {
+    return addPlane(planetModel, verticalPlane, bounds);
+  }
+
+  @Override
+  public Bounds addXValue(final GeoPoint point) {
+    final double x = point.x;
+    final double small = x - FUDGE_FACTOR;
+    if (minX == null || minX > small) {
+      minX = new Double(small);
+    }
+    final double large = x + FUDGE_FACTOR;
+    if (maxX == null || maxX < large) {
+      maxX = new Double(large);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addYValue(final GeoPoint point) {
+    final double y = point.y;
+    final double small = y - FUDGE_FACTOR;
+    if (minY == null || minY > small) {
+      minY = new Double(small);
+    }
+    final double large = y + FUDGE_FACTOR;
+    if (maxY == null || maxY < large) {
+      maxY = new Double(large);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addZValue(final GeoPoint point) {
+    final double z = point.z;
+    final double small = z - FUDGE_FACTOR;
+    if (minZ == null || minZ > small) {
+      minZ = new Double(small);
+    }
+    final double large = z + FUDGE_FACTOR;
+    if (maxZ == null || maxZ < large) {
+      maxZ = new Double(large);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addPoint(final GeoPoint point) {
+    return addXValue(point).addYValue(point).addZValue(point);
+  }
+
+  @Override
+  public Bounds isWide() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+  @Override
+  public Bounds noLongitudeBound() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+  @Override
+  public Bounds noTopLatitudeBound() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+  @Override
+  public Bounds noBottomLatitudeBound() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+}