You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2015/07/01 04:03:44 UTC

svn commit: r1688545 [2/2] - in /lucene/dev/trunk/lucene: ./ spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/ spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java Wed Jul  1 02:03:43 2015
@@ -19,17 +19,19 @@ package org.apache.lucene.spatial.spatia
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
- * GeoSearchableShape representing a path across the surface of the globe,
+ * GeoShape representing a path across the surface of the globe,
  * with a specified half-width.  Path is described by a series of points.
  * Distances are measured from the starting point along the path, and then at right
  * angles to the path.
  *
  * @lucene.experimental
  */
-public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape {
+public class GeoPath extends GeoBaseDistanceShape {
   
   public final double cutoffAngle;
 
@@ -83,7 +85,7 @@ public class GeoPath extends GeoBaseExte
     GeoPoint lastPoint = null;
     for (final GeoPoint end : points) {
       if (lastPoint != null) {
-        final Plane normalizedConnectingPlane = new Plane(lastPoint, end).normalize();
+        final Plane normalizedConnectingPlane = new Plane(lastPoint, end);
         if (normalizedConnectingPlane == null) {
           continue;
         }
@@ -166,173 +168,46 @@ public class GeoPath extends GeoBaseExte
 
   }
 
-  /**
-   * Compute an estimate of "distance" to the GeoPoint.
-   * A return value of Double.MAX_VALUE should be returned for
-   * points outside of the shape.
-   */
-  @Override
-  public double computeNormalDistance(final GeoPoint point) {
-    // Algorithm:
-    // (1) If the point is within any of the segments along the path, return that value.
-    // (2) If the point is within any of the segment end circles along the path, return that value.
-    double currentDistance = 0.0;
-    for (PathSegment segment : segments) {
-      double distance = segment.pathNormalDistance(point);
-      if (distance != Double.MAX_VALUE)
-        return currentDistance + distance;
-      currentDistance += segment.fullNormalDistance;
-    }
-
-    int segmentIndex = 0;
-    currentDistance = 0.0;
-    for (SegmentEndpoint endpoint : endPoints) {
-      double distance = endpoint.pathNormalDistance(point);
-      if (distance != Double.MAX_VALUE)
-        return currentDistance + distance;
-      if (segmentIndex < segments.size())
-        currentDistance += segments.get(segmentIndex++).fullNormalDistance;
-    }
-
-    return Double.MAX_VALUE;
-  }
-
-  /**
-   * Compute an estimate of "distance" to the GeoPoint.
-   * A return value of Double.MAX_VALUE should be returned for
-   * points outside of the shape.
-   */
-  @Override
-  public double computeNormalDistance(final double x, final double y, final double z) {
-    return computeNormalDistance(new GeoPoint(x, y, z));
-  }
-
-  /**
-   * Compute a squared estimate of the "distance" to the
-   * GeoPoint.  Double.MAX_VALUE indicates a point outside of the
-   * shape.
-   */
-  @Override
-  public double computeSquaredNormalDistance(final GeoPoint point) {
-    double pd = computeNormalDistance(point);
-    if (pd == Double.MAX_VALUE)
-      return pd;
-    return pd * pd;
-  }
-
-  /**
-   * Compute a squared estimate of the "distance" to the
-   * GeoPoint.  Double.MAX_VALUE indicates a point outside of the
-   * shape.
-   */
-  @Override
-  public double computeSquaredNormalDistance(final double x, final double y, final double z) {
-    return computeSquaredNormalDistance(new GeoPoint(x, y, z));
-  }
-
-  /**
-   * Compute a linear distance to the point.
-   */
-  @Override
-  public double computeLinearDistance(final GeoPoint point) {
-    // Algorithm:
-    // (1) If the point is within any of the segments along the path, return that value.
-    // (2) If the point is within any of the segment end circles along the path, return that value.
-    double currentDistance = 0.0;
-    for (PathSegment segment : segments) {
-      double distance = segment.pathLinearDistance(point);
-      if (distance != Double.MAX_VALUE)
-        return currentDistance + distance;
-      currentDistance += segment.fullLinearDistance;
-    }
-
-    int segmentIndex = 0;
-    currentDistance = 0.0;
-    for (SegmentEndpoint endpoint : endPoints) {
-      double distance = endpoint.pathLinearDistance(point);
-      if (distance != Double.MAX_VALUE)
-        return currentDistance + distance;
-      if (segmentIndex < segments.size())
-        currentDistance += segments.get(segmentIndex++).fullLinearDistance;
-    }
-
-    return Double.MAX_VALUE;
-  }
-
-  /**
-   * Compute a linear distance to the point.
-   */
-  @Override
-  public double computeLinearDistance(final double x, final double y, final double z) {
-    return computeLinearDistance(new GeoPoint(x, y, z));
-  }
-
-  /**
-   * Compute a squared linear distance to the vector.
-   */
-  @Override
-  public double computeSquaredLinearDistance(final GeoPoint point) {
-    double pd = computeLinearDistance(point);
-    if (pd == Double.MAX_VALUE)
-      return pd;
-    return pd * pd;
-  }
-
-  /**
-   * Compute a squared linear distance to the vector.
-   */
-  @Override
-  public double computeSquaredLinearDistance(final double x, final double y, final double z) {
-    return computeSquaredLinearDistance(new GeoPoint(x, y, z));
-  }
-
-  /**
-   * Compute a true, accurate, great-circle distance.
-   * Double.MAX_VALUE indicates a point is outside of the shape.
-   */
   @Override
-  public double computeArcDistance(final GeoPoint point) {
+  protected double distance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     // Algorithm:
     // (1) If the point is within any of the segments along the path, return that value.
     // (2) If the point is within any of the segment end circles along the path, return that value.
     double currentDistance = 0.0;
     for (PathSegment segment : segments) {
-      double distance = segment.pathDistance(point);
+      double distance = segment.pathDistance(planetModel, distanceStyle, x,y,z);
       if (distance != Double.MAX_VALUE)
         return currentDistance + distance;
-      currentDistance += segment.fullDistance;
+      currentDistance += segment.fullPathDistance(distanceStyle);
     }
 
     int segmentIndex = 0;
     currentDistance = 0.0;
     for (SegmentEndpoint endpoint : endPoints) {
-      double distance = endpoint.pathDistance(point);
+      double distance = endpoint.pathDistance(distanceStyle, x, y, z);
       if (distance != Double.MAX_VALUE)
         return currentDistance + distance;
       if (segmentIndex < segments.size())
-        currentDistance += segments.get(segmentIndex++).fullDistance;
+        currentDistance += segments.get(segmentIndex++).fullPathDistance(distanceStyle);
     }
 
     return Double.MAX_VALUE;
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    //System.err.println("Assessing whether point "+point+" is within geopath "+this);
-    for (SegmentEndpoint pathPoint : endPoints) {
-      if (pathPoint.isWithin(point)) {
-        //System.err.println(" point is within SegmentEndpoint "+pathPoint);
-        return true;
-      }
-    }
-    for (PathSegment pathSegment : segments) {
-      if (pathSegment.isWithin(point)) {
-        //System.err.println(" point is within PathSegment "+pathSegment);
-        return true;
-      }
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    double minDistance = Double.MAX_VALUE;
+    for (final SegmentEndpoint endpoint : endPoints) {
+      final double newDistance = endpoint.outsideDistance(distanceStyle, x,y,z);
+      if (newDistance < minDistance)
+        minDistance = newDistance;
+    }
+    for (final PathSegment segment : segments) {
+      final double newDistance = segment.outsideDistance(planetModel, distanceStyle, x, y, z);
+      if (newDistance < minDistance)
+        minDistance = newDistance;
     }
-    //System.err.println(" point is not within geopath");
-    return false;
+    return minDistance;
   }
 
   @Override
@@ -380,15 +255,6 @@ public class GeoPath extends GeoBaseExte
     return false;
   }
 
-  /**
-   * 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) {
     bounds = super.getBounds(bounds);
@@ -562,22 +428,14 @@ public class GeoPath extends GeoBaseExte
       return circlePlane.isWithin(x, y, z);
     }
 
-    public double pathDistance(final GeoPoint point) {
-      if (!isWithin(point))
-        return Double.MAX_VALUE;
-      return this.point.arcDistance(point);
-    }
-
-    public double pathNormalDistance(final GeoPoint point) {
-      if (!isWithin(point))
+    public double pathDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      if (!isWithin(x,y,z))
         return Double.MAX_VALUE;
-      return this.point.normalDistance(point);
+      return distanceStyle.computeDistance(this.point, x, y, z);
     }
 
-    public double pathLinearDistance(final GeoPoint point) {
-      if (!isWithin(point))
-        return Double.MAX_VALUE;
-      return this.point.linearDistance(point);
+    public double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      return distanceStyle.computeDistance(this.point, x, y, z);
     }
 
     public boolean intersects(final PlanetModel planetModel, final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
@@ -614,14 +472,12 @@ public class GeoPath extends GeoBaseExte
   }
 
   /**
-   * This is the precalculated data for a path segment.
+   * This is the pre-calculated data for a path segment.
    */
   public static class PathSegment {
     public final GeoPoint start;
     public final GeoPoint end;
-    public final double fullDistance;
-    public final double fullNormalDistance;
-    public final double fullLinearDistance;
+    public final Map<DistanceStyle,Double> fullDistanceCache = new HashMap<DistanceStyle,Double>();
     public final Plane normalizedConnectingPlane;
     public final SidedPlane upperConnectingPlane;
     public final SidedPlane lowerConnectingPlane;
@@ -644,9 +500,6 @@ public class GeoPath extends GeoBaseExte
       this.normalizedConnectingPlane = normalizedConnectingPlane;
       this.planeBoundingOffset = planeBoundingOffset;
 
-      fullDistance = start.arcDistance(end);
-      fullNormalDistance = start.normalDistance(end);
-      fullLinearDistance = start.linearDistance(end);
       // Either start or end should be on the correct side
       upperConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, -planeBoundingOffset);
       lowerConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, planeBoundingOffset);
@@ -684,6 +537,17 @@ public class GeoPath extends GeoBaseExte
       endCutoffPlanePoints = new GeoPoint[]{URHC, LRHC};
     }
 
+    public double fullPathDistance(final DistanceStyle distanceStyle) {
+      synchronized (fullDistanceCache) {
+        Double dist = fullDistanceCache.get(distanceStyle);
+        if (dist == null) {
+          dist = new Double(distanceStyle.computeDistance(start, end.x, end.y, end.z));
+          fullDistanceCache.put(distanceStyle, dist);
+        }
+        return dist.doubleValue();
+      }
+    }
+    
     public boolean isWithin(final Vector point) {
       //System.err.println(" assessing whether point "+point+" is within path segment "+this);
       //System.err.println("  within "+startCutoffPlane+": "+startCutoffPlane.isWithin(point));
@@ -704,74 +568,58 @@ public class GeoPath extends GeoBaseExte
           lowerConnectingPlane.isWithin(x, y, z);
     }
 
-    public double pathDistance(final GeoPoint point) {
-      if (!isWithin(point))
-        return Double.MAX_VALUE;
-
-      // Compute the distance, filling in both components.
-      final double perpDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedConnectingPlane.evaluate(point)));
-      final Plane normalizedPerpPlane = new Plane(normalizedConnectingPlane, point).normalize();
-      final double pathDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedPerpPlane.evaluate(start)));
-      return perpDistance + pathDistance;
-    }
-
-    public double pathNormalDistance(final GeoPoint point) {
-      if (!isWithin(point))
-        return Double.MAX_VALUE;
-
-      final double pointEval = Math.abs(normalizedConnectingPlane.evaluate(point));
-
-      // Want no allocations or expensive operations!  so we do this the hard way
-      final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y;
-      final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z;
-      final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
-
-      // If we have a degenerate line, then just compute the normal distance from point x to the start
-      if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION)
-        return point.normalDistance(start);
-
-      final double normFactor = 1.0 / Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ);
-      final double perpEval = Math.abs(perpX * start.x + perpY * start.y + perpZ * start.z);
-      return perpEval * normFactor + pointEval;
-    }
-
-    public double pathLinearDistance(final GeoPoint point) {
-      if (!isWithin(point))
+    public double pathDistance(final PlanetModel planetModel, final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      if (!isWithin(x,y,z))
         return Double.MAX_VALUE;
 
-      // We have a normalized connecting plane.
-      // First, compute the perpendicular plane.
+      // (1) Compute normalizedPerpPlane.  If degenerate, then return point distance from start to point.
       // Want no allocations or expensive operations!  so we do this the hard way
-      final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y;
-      final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z;
-      final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
-
-      // If we have a degenerate line, then just compute the normal distance from point x to the start
-      if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION)
-        return point.linearDistance(start);
-
-      // Next, we need the vector of the line, which is the cross product of the normalized connecting plane
-      // and the perpendicular plane that we just calculated.
-      final double lineX = normalizedConnectingPlane.y * perpZ - normalizedConnectingPlane.z * perpY;
-      final double lineY = normalizedConnectingPlane.z * perpX - normalizedConnectingPlane.x * perpZ;
-      final double lineZ = normalizedConnectingPlane.x * perpY - normalizedConnectingPlane.y * perpX;
-
-      // Now, compute a normalization factor
-      final double normalizer = 1.0 / Math.sqrt(lineX * lineX + lineY * lineY + lineZ * lineZ);
-
-      // Pick which point by using bounding planes
-      double normLineX = lineX * normalizer;
-      double normLineY = lineY * normalizer;
-      double normLineZ = lineZ * normalizer;
-      if (!startCutoffPlane.isWithin(normLineX, normLineY, normLineZ) ||
-          !endCutoffPlane.isWithin(normLineX, normLineY, normLineZ)) {
-        normLineX = -normLineX;
-        normLineY = -normLineY;
-        normLineZ = -normLineZ;
-      }
-
-      // Compute linear distance for the two points
-      return point.linearDistance(normLineX, normLineY, normLineZ) + start.linearDistance(normLineX, normLineY, normLineZ);
+      final double perpX = normalizedConnectingPlane.y * z - normalizedConnectingPlane.z * y;
+      final double perpY = normalizedConnectingPlane.z * x - normalizedConnectingPlane.x * z;
+      final double perpZ = normalizedConnectingPlane.x * y - normalizedConnectingPlane.y * x;
+      final double magnitude = Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ);
+      if (Math.abs(magnitude) < Vector.MINIMUM_RESOLUTION)
+        return distanceStyle.computeDistance(start, x,y,z);
+      final double normFactor = 1.0/magnitude;
+      final Plane normalizedPerpPlane = new Plane(perpX * normFactor, perpY * normFactor, perpZ * normFactor, 0.0);
+      
+      // Old computation: too expensive, because it calculates the intersection point twice.
+      //return distanceStyle.computeDistance(planetModel, normalizedConnectingPlane, x, y, z, startCutoffPlane, endCutoffPlane) +
+      //  distanceStyle.computeDistance(planetModel, normalizedPerpPlane, start.x, start.y, start.z, upperConnectingPlane, lowerConnectingPlane);
+
+      final GeoPoint[] intersectionPoints = normalizedConnectingPlane.findIntersections(planetModel, normalizedPerpPlane);
+      GeoPoint thePoint;
+      if (intersectionPoints.length == 0)
+        throw new RuntimeException("Can't find world intersection for point x="+x+" y="+y+" z="+z);
+      else if (intersectionPoints.length == 1)
+        thePoint = intersectionPoints[0];
+      else {
+        if (startCutoffPlane.isWithin(intersectionPoints[0]) && endCutoffPlane.isWithin(intersectionPoints[0]))
+          thePoint = intersectionPoints[0];
+        else if (startCutoffPlane.isWithin(intersectionPoints[1]) && endCutoffPlane.isWithin(intersectionPoints[1]))
+          thePoint = intersectionPoints[1];
+        else
+          throw new RuntimeException("Can't find world intersection for point x="+x+" y="+y+" z="+z);
+      }
+      return distanceStyle.computeDistance(thePoint, x, y, z) + distanceStyle.computeDistance(start, thePoint.x, thePoint.y, thePoint.z);
+    }
+
+    public double outsideDistance(final PlanetModel planetModel, final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      final double upperDistance = distanceStyle.computeDistance(planetModel, upperConnectingPlane, x,y,z, lowerConnectingPlane, startCutoffPlane, endCutoffPlane);
+      final double lowerDistance = distanceStyle.computeDistance(planetModel, lowerConnectingPlane, x,y,z, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
+      final double startDistance = distanceStyle.computeDistance(planetModel, startCutoffPlane, x,y,z, endCutoffPlane, lowerConnectingPlane, upperConnectingPlane);
+      final double endDistance = distanceStyle.computeDistance(planetModel, endCutoffPlane, x,y,z, startCutoffPlane, lowerConnectingPlane, upperConnectingPlane);
+      final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+      final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+      final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+      final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+      return Math.min(
+        Math.min(
+          Math.min(upperDistance,lowerDistance),
+          Math.min(startDistance,endDistance)),
+        Math.min(
+          Math.min(ULHCDistance, URHCDistance),
+          Math.min(LLHCDistance, LRHCDistance)));
     }
 
     public boolean intersects(final PlanetModel planetModel, final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
@@ -790,12 +638,6 @@ public class GeoPath extends GeoBaseExte
       lowerConnectingPlane.recordBounds(planetModel, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
       startCutoffPlane.recordBounds(planetModel, bounds, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
       endCutoffPlane.recordBounds(planetModel, bounds, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
-      if (fullDistance >= Math.PI) {
-        // Too large a segment basically means that we can confuse the Bounds object.  Specifically, if our span exceeds 180 degrees
-        // in longitude (which even a segment whose actual length is less than that might if it goes close to a pole).
-        // Unfortunately, we can get arbitrarily close to the pole, so this may still not work in all cases.
-        bounds.noLongitudeBound();
-      }
     }
 
   }

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java Wed Jul  1 02:03:43 2015
@@ -18,7 +18,7 @@ package org.apache.lucene.spatial.spatia
  */
 
 /**
- * This class represents a point on the surface of a unit sphere.
+ * This class represents a point on the surface of a sphere or ellipsoid.
  *
  * @lucene.experimental
  */
@@ -124,6 +124,16 @@ public class GeoPoint extends Vector {
     return Tools.safeAcos(dotProduct(v)/(magnitude() * v.magnitude()));
   }
 
+  /** Compute an arc distance between two points.
+   * @param x is the x part of the second point.
+   * @param y is the y part of the second point.
+   * @param z is the z part of the second point.
+   * @return the angle, in radians, between the two points.
+   */
+  public double arcDistance(final double x, final double y, final double z) {
+    return Tools.safeAcos(dotProduct(x,y,z)/(magnitude() * Vector.magnitude(x,y,z)));
+  }
+
   /** Compute the latitude for the point.
    * @return the latitude.
    */

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java Wed Jul  1 02:03:43 2015
@@ -138,14 +138,6 @@ public class GeoRectangle extends GeoBas
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return topPlane.isWithin(point) &&
-        bottomPlane.isWithin(point) &&
-        leftPlane.isWithin(point) &&
-        rightPlane.isWithin(point);
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return topPlane.isWithin(x, y, z) &&
         bottomPlane.isWithin(x, y, z) &&
@@ -169,11 +161,6 @@ public class GeoRectangle extends GeoBas
     return edgePoints;
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     return centerPoint;
@@ -187,15 +174,6 @@ public class GeoRectangle extends GeoBas
         p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane);
   }
 
-  /**
-   * 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)
@@ -243,6 +221,27 @@ public class GeoRectangle extends GeoBas
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, leftPlane, rightPlane);
+    final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane, leftPlane, rightPlane);
+    final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane, topPlane, bottomPlane);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, leftPlane, topPlane, bottomPlane);
+    
+    final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+    final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+    final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+    final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+    
+    return Math.min(
+      Math.min(
+        Math.min(topDistance, bottomDistance),
+        Math.min(leftDistance, rightDistance)),
+      Math.min(
+        Math.min(ULHCDistance, URHCDistance),
+        Math.min(LRHCDistance, LLHCDistance)));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoRectangle))
       return false;

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java Wed Jul  1 02:03:43 2015
@@ -64,8 +64,4 @@ public interface GeoShape extends Member
    */
   public Bounds getBounds(final Bounds bounds);
 
-  /**
-   * Equals
-   */
-  public boolean equals(Object o);
 }

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java Wed Jul  1 02:03:43 2015
@@ -60,11 +60,6 @@ public class GeoSouthLatitudeZone extend
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return topPlane.isWithin(point);
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return topPlane.isWithin(x, y, z);
   }
@@ -149,6 +144,11 @@ public class GeoSouthLatitudeZone extend
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    return distanceStyle.computeDistance(planetModel, topPlane, x,y,z);
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoSouthLatitudeZone))
       return false;

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java Wed Jul  1 02:03:43 2015
@@ -21,7 +21,7 @@ package org.apache.lucene.spatial.spatia
  * Bounding box limited on three sides (top lat, left lon, right lon).  The
  * other corner is the south pole.
  * The left-right maximum extent for this shape is PI; for anything larger, use
- * GeoWideSouthRectangle.
+ * {@link GeoWideSouthRectangle}.
  *
  * @lucene.internal
  */
@@ -124,13 +124,6 @@ public class GeoSouthRectangle extends G
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return topPlane.isWithin(point) &&
-        leftPlane.isWithin(point) &&
-        rightPlane.isWithin(point);
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return topPlane.isWithin(x, y, z) &&
         leftPlane.isWithin(x, y, z) &&
@@ -152,11 +145,6 @@ public class GeoSouthRectangle extends G
     return edgePoints;
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     return centerPoint;
@@ -169,15 +157,6 @@ public class GeoSouthRectangle extends G
         p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane);
   }
 
-  /**
-   * 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)
@@ -224,6 +203,22 @@ public class GeoSouthRectangle extends G
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, leftPlane, rightPlane);
+    final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane, topPlane);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, leftPlane, topPlane);
+    
+    final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+    final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+    
+    return Math.min(
+      Math.min(
+        topDistance,
+        Math.min(leftDistance, rightDistance)),
+      Math.min(ULHCDistance, URHCDistance));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoSouthRectangle))
       return false;

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java Wed Jul  1 02:03:43 2015
@@ -117,15 +117,6 @@ public class GeoWideDegenerateHorizontal
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    if (point == null)
-      return false;
-    return plane.evaluateIsZero(point) &&
-        (leftPlane.isWithin(point) ||
-            rightPlane.isWithin(point));
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return plane.evaluateIsZero(x, y, z) &&
         (leftPlane.isWithin(x, y, z) ||
@@ -142,11 +133,6 @@ public class GeoWideDegenerateHorizontal
     return Math.max(topAngle, bottomAngle);
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     return centerPoint;
@@ -164,15 +150,6 @@ public class GeoWideDegenerateHorizontal
     return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, eitherBound);
   }
 
-  /**
-   * 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)
@@ -196,6 +173,18 @@ public class GeoWideDegenerateHorizontal
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    final double distance = distanceStyle.computeDistance(planetModel, plane, x,y,z, eitherBound);
+    
+    final double LHCDistance = distanceStyle.computeDistance(LHC, x,y,z);
+    final double RHCDistance = distanceStyle.computeDistance(RHC, x,y,z);
+    
+    return Math.min(
+      distance,
+      Math.min(LHCDistance, RHCDistance));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoWideDegenerateHorizontalLine))
       return false;
@@ -221,11 +210,6 @@ public class GeoWideDegenerateHorizontal
     }
 
     @Override
-    public boolean isWithin(final Vector v) {
-      return leftPlane.isWithin(v) || rightPlane.isWithin(v);
-    }
-
-    @Override
     public boolean isWithin(final double x, final double y, final double z) {
       return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
     }

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java Wed Jul  1 02:03:43 2015
@@ -92,12 +92,6 @@ public class GeoWideLongitudeSlice exten
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return leftPlane.isWithin(point) ||
-        rightPlane.isWithin(point);
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return leftPlane.isWithin(x, y, z) ||
         rightPlane.isWithin(x, y, z);
@@ -112,11 +106,6 @@ public class GeoWideLongitudeSlice exten
     return Math.max(Math.PI * 0.5, extent * 0.5);
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     return centerPoint;
@@ -135,15 +124,6 @@ public class GeoWideLongitudeSlice exten
         p.intersects(planetModel, rightPlane, 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)
@@ -178,6 +158,21 @@ public class GeoWideLongitudeSlice exten
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
+    // unbounded distance to both the left and the right and only take the minimum of the two.
+    final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z);
+    
+    final double northDistance = distanceStyle.computeDistance(planetModel.NORTH_POLE, x,y,z);
+    final double southDistance = distanceStyle.computeDistance(planetModel.SOUTH_POLE, x,y,z);
+    
+    return Math.min(
+      Math.min(leftDistance, rightDistance),
+      Math.min(northDistance, southDistance));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoWideLongitudeSlice))
       return false;

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java Wed Jul  1 02:03:43 2015
@@ -125,14 +125,6 @@ public class GeoWideNorthRectangle exten
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return
-        bottomPlane.isWithin(point) &&
-            (leftPlane.isWithin(point) ||
-                rightPlane.isWithin(point));
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return
         bottomPlane.isWithin(x, y, z) &&
@@ -150,11 +142,6 @@ public class GeoWideNorthRectangle exten
     return Math.max(centerAngle, bottomAngle);
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     return centerPoint;
@@ -175,15 +162,6 @@ public class GeoWideNorthRectangle exten
             p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane);
   }
 
-  /**
-   * 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)
@@ -232,6 +210,24 @@ public class GeoWideNorthRectangle exten
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, eitherBound);
+    // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
+    // unbounded distance to both the left and the right and only take the minimum of the two.
+    final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, bottomPlane);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, bottomPlane);
+    
+    final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+    final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+    
+    return Math.min(
+      Math.min(
+        bottomDistance,
+        Math.min(leftDistance, rightDistance)),
+      Math.min(LRHCDistance, LLHCDistance));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoWideNorthRectangle))
       return false;

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java Wed Jul  1 02:03:43 2015
@@ -142,14 +142,6 @@ public class GeoWideRectangle extends Ge
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return topPlane.isWithin(point) &&
-        bottomPlane.isWithin(point) &&
-        (leftPlane.isWithin(point) ||
-            rightPlane.isWithin(point));
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return topPlane.isWithin(x, y, z) &&
         bottomPlane.isWithin(x, y, z) &&
@@ -193,15 +185,6 @@ public class GeoWideRectangle extends Ge
         p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane);
   }
 
-  /**
-   * 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)
@@ -250,6 +233,29 @@ public class GeoWideRectangle extends Ge
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, eitherBound);
+    final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane, eitherBound);
+    // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
+    // unbounded distance to both the left and the right and only take the minimum of the two.
+    final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, topPlane, bottomPlane);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, topPlane, bottomPlane);
+    
+    final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+    final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+    final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+    final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+    
+    return Math.min(
+      Math.min(
+        Math.min(topDistance, bottomDistance),
+        Math.min(leftDistance, rightDistance)),
+      Math.min(
+        Math.min(ULHCDistance, URHCDistance),
+        Math.min(LRHCDistance, LLHCDistance)));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoWideRectangle))
       return false;

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java Wed Jul  1 02:03:43 2015
@@ -126,13 +126,6 @@ public class GeoWideSouthRectangle exten
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return topPlane.isWithin(point) &&
-        (leftPlane.isWithin(point) ||
-            rightPlane.isWithin(point));
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return topPlane.isWithin(x, y, z) &&
         (leftPlane.isWithin(x, y, z) ||
@@ -149,11 +142,6 @@ public class GeoWideSouthRectangle exten
     return Math.max(centerAngle, topAngle);
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     return centerPoint;
@@ -173,15 +161,6 @@ public class GeoWideSouthRectangle exten
         p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane);
   }
 
-  /**
-   * 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)
@@ -229,11 +208,29 @@ public class GeoWideSouthRectangle exten
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, eitherBound);
+    // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
+    // unbounded distance to both the left and the right and only take the minimum of the two.
+    final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, topPlane);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, topPlane);
+    
+    final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+    final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+    
+    return Math.min(
+      Math.min(
+        topDistance,
+        Math.min(leftDistance, rightDistance)),
+      Math.min(ULHCDistance, URHCDistance));
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoWideSouthRectangle))
       return false;
     GeoWideSouthRectangle other = (GeoWideSouthRectangle) o;
-    return super.equals(other) && other.ULHC.equals(ULHC) && other.URHC.equals(URHC);
+    return super.equals(o) && other.ULHC.equals(ULHC) && other.URHC.equals(URHC);
   }
 
   @Override

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java Wed Jul  1 02:03:43 2015
@@ -41,11 +41,6 @@ public class GeoWorld extends GeoBaseBBo
     return Math.PI;
   }
 
-  /**
-   * Returns the center of a circle into which the area will be inscribed.
-   *
-   * @return the center.
-   */
   @Override
   public GeoPoint getCenter() {
     // Totally arbitrary
@@ -53,11 +48,6 @@ public class GeoWorld extends GeoBaseBBo
   }
 
   @Override
-  public boolean isWithin(final Vector point) {
-    return true;
-  }
-
-  @Override
   public boolean isWithin(final double x, final double y, final double z) {
     return true;
   }
@@ -72,15 +62,6 @@ public class GeoWorld extends GeoBaseBBo
     return false;
   }
 
-  /**
-   * 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)
@@ -99,6 +80,11 @@ public class GeoWorld extends GeoBaseBBo
   }
 
   @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    return 0.0;
+  }
+
+  @Override
   public boolean equals(Object o) {
     if (!(o instanceof GeoWorld))
       return false;

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearDistance.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearDistance.java?rev=1688545&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearDistance.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearDistance.java Wed Jul  1 02:03:43 2015
@@ -0,0 +1,51 @@
+package org.apache.lucene.spatial.spatial4j.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.
+ */
+
+/**
+ * Linear distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class LinearDistance implements DistanceStyle {
+  
+  public final static LinearDistance INSTANCE = new LinearDistance();
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.linearDistance(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.linearDistance(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.linearDistance(planetModel, point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.linearDistance(planetModel, x,y,z, bounds);
+  }
+
+}
+
+

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearSquaredDistance.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearSquaredDistance.java?rev=1688545&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearSquaredDistance.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/LinearSquaredDistance.java Wed Jul  1 02:03:43 2015
@@ -0,0 +1,51 @@
+package org.apache.lucene.spatial.spatial4j.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.
+ */
+
+/**
+ * Linear squared distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class LinearSquaredDistance implements DistanceStyle {
+  
+  public final static LinearSquaredDistance INSTANCE = new LinearSquaredDistance();
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.linearDistanceSquared(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.linearDistanceSquared(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.linearDistanceSquared(planetModel, point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.linearDistanceSquared(planetModel, x,y,z, bounds);
+  }
+
+}
+
+

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java Wed Jul  1 02:03:43 2015
@@ -18,7 +18,7 @@ package org.apache.lucene.spatial.spatia
  */
 
 /**
- * Interface describing 3d shape membership methods.
+ * Implemented by Geo3D shapes that can calculate if a point is within it or not.
  *
  * @lucene.experimental
  */
@@ -30,7 +30,9 @@ public interface Membership {
    * @param point is the point to check.
    * @return true if the point is within this shape
    */
-  public boolean isWithin(final Vector point);
+  public default boolean isWithin(final Vector point) {
+    return isWithin(point.x, point.y, point.z);
+  }
 
   /**
    * Check if a point is within this shape.

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalDistance.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalDistance.java?rev=1688545&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalDistance.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalDistance.java Wed Jul  1 02:03:43 2015
@@ -0,0 +1,51 @@
+package org.apache.lucene.spatial.spatial4j.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.
+ */
+
+/**
+ * Normal distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class NormalDistance implements DistanceStyle {
+  
+  public final static NormalDistance INSTANCE = new NormalDistance();
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.normalDistance(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.normalDistance(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.normalDistance(point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.normalDistance(x,y,z, bounds);
+  }
+
+}
+
+

Added: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalSquaredDistance.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalSquaredDistance.java?rev=1688545&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalSquaredDistance.java (added)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/NormalSquaredDistance.java Wed Jul  1 02:03:43 2015
@@ -0,0 +1,51 @@
+package org.apache.lucene.spatial.spatial4j.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.
+ */
+
+/**
+ * Normal squared distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class NormalSquaredDistance implements DistanceStyle {
+  
+  public final static NormalSquaredDistance INSTANCE = new NormalSquaredDistance();
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.normalDistanceSquared(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.normalDistanceSquared(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.normalDistanceSquared(point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.normalDistanceSquared(x,y,z, bounds);
+  }
+
+}
+
+

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java Wed Jul  1 02:03:43 2015
@@ -143,6 +143,178 @@ public class Plane extends Vector {
     return new Plane(normVect, this.D);
   }
 
+  /** @see #arcDistance(PlanetModel, double, double, double, Membership...) */
+  public double arcDistance(final PlanetModel planetModel, final GeoPoint v, final Membership... bounds) {
+    return arcDistance(planetModel, v.x, v.y, v.z, bounds);
+  }
+    
+  /**
+   * Compute arc distance from plane to a vector.
+   * @param x is the x vector value.
+   * @param y is the y vector value.
+   * @param z is the z vector value.
+   * @return the arc distance.
+   */
+  public double arcDistance(final PlanetModel planetModel, final double x, final double y, final double z, final Membership... bounds) {
+
+    if (evaluateIsZero(x,y,z)) {
+      if (meetsAllBounds(x,y,z, bounds))
+        return 0.0;
+      return Double.MAX_VALUE;
+    }
+    
+    // First, compute the perpendicular plane.
+    final Plane perpPlane = new Plane(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x, 0.0);
+
+    // We need to compute the intersection of two planes on the geo surface: this one, and its perpendicular.
+    // Then, we need to choose which of the two points we want to compute the distance to.  We pick the
+    // shorter distance always.
+    
+    final GeoPoint[] intersectionPoints = findIntersections(planetModel, perpPlane);
+    
+    // For each point, compute a linear distance, and take the minimum of them
+    double minDistance = Double.MAX_VALUE;
+    
+    for (final GeoPoint intersectionPoint : intersectionPoints) {
+      if (meetsAllBounds(intersectionPoint, bounds)) {
+        final double theDistance = intersectionPoint.arcDistance(x,y,z);
+        if (theDistance < minDistance) {
+          minDistance = theDistance;
+        }
+      }
+    }
+    return minDistance;
+
+  }
+
+  /**
+   * Compute normal distance from plane to a vector.
+   * @param v is the vector.
+   * @return the normal distance.
+   */
+  public double normalDistance(final Vector v, final Membership... bounds) {
+    return normalDistance(v.x, v.y, v.z, bounds);
+  }
+    
+  /**
+   * Compute normal distance from plane to a vector.
+   * @param x is the vector x.
+   * @param y is the vector y.
+   * @param z is the vector z.
+   * @return the normal distance.
+   */
+  public double normalDistance(final double x, final double y, final double z, final Membership... bounds) {
+
+    final double dist = evaluate(x,y,z);
+    final double perpX = x - dist * this.x;
+    final double perpY = y - dist * this.y;
+    final double perpZ = z - dist * this.z;
+
+    if (!meetsAllBounds(perpX, perpY, perpZ, bounds)) {
+      return Double.MAX_VALUE;
+    }
+    
+    return Math.abs(dist);
+  }
+  
+  /**
+   * Compute normal distance squared from plane to a vector.
+   * @param v is the vector.
+   * @return the normal distance squared.
+   */
+  public double normalDistanceSquared(final Vector v, final Membership... bounds) {
+    return normalDistanceSquared(v.x, v.y, v.z, bounds);
+  }
+  
+  /**
+   * Compute normal distance squared from plane to a vector.
+   * @param x is the vector x.
+   * @param y is the vector y.
+   * @param z is the vector z.
+   * @return the normal distance squared.
+   */
+  public double normalDistanceSquared(final double x, final double y, final double z, final Membership... bounds) {
+    final double normal = normalDistance(x,y,z,bounds);
+    if (normal == Double.MAX_VALUE)
+      return normal;
+    return normal * normal;
+  }
+
+  /**
+   * Compute linear distance from plane to a vector.  This is defined
+   * as the distance from the given point to the nearest intersection of 
+   * this plane with the planet surface.
+   * @param v is the vector.
+   * @return the linear distance.
+   */
+  public double linearDistance(final PlanetModel planetModel, final GeoPoint v, final Membership... bounds) {
+    return linearDistance(planetModel, v.x, v.y, v.z, bounds);
+  }
+    
+  /**
+   * Compute linear distance from plane to a vector.  This is defined
+   * as the distance from the given point to the nearest intersection of 
+   * this plane with the planet surface.
+   * @param x is the vector x.
+   * @param y is the vector y.
+   * @param z is the vector z.
+   * @return the linear distance.
+   */
+  public double linearDistance(final PlanetModel planetModel, final double x, final double y, final double z, final Membership... bounds) {
+    if (evaluateIsZero(x,y,z)) {
+      if (meetsAllBounds(x,y,z, bounds))
+        return 0.0;
+      return Double.MAX_VALUE;
+    }
+    
+    // First, compute the perpendicular plane.
+    final Plane perpPlane = new Plane(this.y * z - this.z * y, this.z * x - this.x * z, this.x * y - this.y * x, 0.0);
+
+    // We need to compute the intersection of two planes on the geo surface: this one, and its perpendicular.
+    // Then, we need to choose which of the two points we want to compute the distance to.  We pick the
+    // shorter distance always.
+    
+    final GeoPoint[] intersectionPoints = findIntersections(planetModel, perpPlane);
+    
+    // For each point, compute a linear distance, and take the minimum of them
+    double minDistance = Double.MAX_VALUE;
+    
+    for (final GeoPoint intersectionPoint : intersectionPoints) {
+      if (meetsAllBounds(intersectionPoint, bounds)) {
+        final double theDistance = intersectionPoint.linearDistance(x,y,z);
+        if (theDistance < minDistance) {
+          minDistance = theDistance;
+        }
+      }
+    }
+    return minDistance;
+  }
+      
+  /**
+   * Compute linear distance squared from plane to a vector.  This is defined
+   * as the distance from the given point to the nearest intersection of 
+   * this plane with the planet surface.
+   * @param v is the vector.
+   * @return the linear distance squared.
+   */
+  public double linearDistanceSquared(final PlanetModel planetModel, final GeoPoint v, final Membership... bounds) {
+    return linearDistanceSquared(planetModel, v.x, v.y, v.z, bounds);
+  }
+  
+  /**
+   * Compute linear distance squared from plane to a vector.  This is defined
+   * as the distance from the given point to the nearest intersection of 
+   * this plane with the planet surface.
+   * @param x is the vector x.
+   * @param y is the vector y.
+   * @param z is the vector z.
+   * @return the linear distance squared.
+   */
+  public double linearDistanceSquared(final PlanetModel planetModel, final double x, final double y, final double z, final Membership... bounds) {
+    final double linearDistance = linearDistance(planetModel, x, y, z, bounds);
+    return linearDistance * linearDistance;
+  }
+
   /**
    * Find points on the boundary of the intersection of a plane and the unit sphere,
    * given a starting point, and ending point, and a list of proportions of the arc (e.g. 0.25, 0.5, 0.75).
@@ -320,7 +492,8 @@ public class Plane extends Vector {
    */
   protected GeoPoint[] findIntersections(final PlanetModel planetModel, final Plane q, final Membership[] bounds, final Membership[] moreBounds) {
     //System.err.println("Looking for intersection between plane "+this+" and plane "+q+" within bounds");
-    final Vector lineVector = new Vector(this, q);
+    // Unnormalized, unchecked...
+    final Vector lineVector = new Vector(y * q.z - z * q.y, z * q.x - x * q.z, x * q.y - y * q.x);
     if (Math.abs(lineVector.x) < MINIMUM_RESOLUTION && Math.abs(lineVector.y) < MINIMUM_RESOLUTION && Math.abs(lineVector.z) < MINIMUM_RESOLUTION) {
       // Degenerate case: parallel planes
       //System.err.println(" planes are parallel - no intersection");
@@ -860,18 +1033,27 @@ public class Plane extends Vector {
     return evaluateIsZero(-p.x * p.D * denom, -p.y * p.D * denom, -p.z * p.D * denom);
   }
 
-  protected static boolean meetsAllBounds(final GeoPoint p, final Membership[] bounds, final Membership[] moreBounds) {
+  protected static boolean meetsAllBounds(final Vector p, final Membership[] bounds) {
+    return meetsAllBounds(p.x, p.y, p.z, bounds);
+  }
+
+  protected static boolean meetsAllBounds(final double x, final double y, final double z, final Membership[] bounds) {
     for (final Membership bound : bounds) {
-      if (!bound.isWithin(p))
-        return false;
-    }
-    for (final Membership bound : moreBounds) {
-      if (!bound.isWithin(p))
+      if (!bound.isWithin(x,y,z))
         return false;
     }
     return true;
   }
 
+  protected static boolean meetsAllBounds(final Vector p, final Membership[] bounds, final Membership[] moreBounds) {
+    return meetsAllBounds(p.x, p.y, p.z, bounds, moreBounds);
+  }
+
+  protected static boolean meetsAllBounds(final double x, final double y, final double z, final Membership[] bounds,
+                                          final Membership[] moreBounds) {
+    return meetsAllBounds(x,y,z, bounds) && meetsAllBounds(x,y,z, moreBounds);
+  }
+
   /**
    * Find a sample point on the intersection between two planes and the unit sphere.
    */

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java Wed Jul  1 02:03:43 2015
@@ -90,57 +90,58 @@ public class SidedPlane extends Plane im
   public static SidedPlane constructNormalizedPerpendicularSidedPlane(final Vector insidePoint,
     final Vector normalVector, final Vector point1, final Vector point2) {
     final Vector pointsVector = new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z);
-    final Vector newNormalVector = new Vector(normalVector, pointsVector).normalize();
-    if (newNormalVector == null)
+    final Vector newNormalVector = new Vector(normalVector, pointsVector);
+    try {
+      // To construct the plane, we now just need D, which is simply the negative of the evaluation of the circle normal vector at one of the points.
+      return new SidedPlane(insidePoint, newNormalVector, -newNormalVector.dotProduct(point1));
+    } catch (IllegalArgumentException e) {
       return null;
-    // To construct the plane, we now just need D, which is simply the negative of the evaluation of the circle normal vector at one of the points.
-    return new SidedPlane(insidePoint, newNormalVector, -newNormalVector.dotProduct(point1));
+    }
   }
   
   /** Construct a sided plane from three points.
    */
   public static SidedPlane constructNormalizedThreePointSidedPlane(final Vector insidePoint,
     final Vector point1, final Vector point2, final Vector point3) {
-    final Vector planeNormal = new Vector(
-      new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z),
-      new Vector(point2.x - point3.x, point2.y - point3.y, point2.z - point3.z)).normalize();
-    if (planeNormal == null)
+    try {
+      final Vector planeNormal = new Vector(
+        new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z),
+        new Vector(point2.x - point3.x, point2.y - point3.y, point2.z - point3.z));
+      return new SidedPlane(insidePoint, planeNormal, -planeNormal.dotProduct(point2));
+    } catch (IllegalArgumentException e) {
       return null;
-    return new SidedPlane(insidePoint, planeNormal, -planeNormal.dotProduct(point2));
+    }
   }
 
-  /**
-   * Check if a point is within this shape.
-   *
-   * @param point is the point to check.
-   * @return true if the point is within this shape
-   */
   @Override
-  public boolean isWithin(Vector point) {
-    double evalResult = evaluate(point);
+  public boolean isWithin(double x, double y, double z) {
+    double evalResult = evaluate(x, y, z);
     if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
       return true;
     double sigNum = Math.signum(evalResult);
     return sigNum == this.sigNum;
   }
 
-  /**
-   * Check if a point is within this shape.
-   *
-   * @param x is x coordinate of point to check.
-   * @param y is y coordinate of point to check.
-   * @param z is z coordinate of point to check.
-   * @return true if the point is within this shape
-   */
   @Override
-  public boolean isWithin(double x, double y, double z) {
-    double evalResult = evaluate(x, y, z);
-    if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
-      return true;
-    double sigNum = Math.signum(evalResult);
-    return sigNum == this.sigNum;
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof SidedPlane)) return false;
+    if (!super.equals(o)) return false;
+
+    SidedPlane that = (SidedPlane) o;
+
+    return Double.compare(that.sigNum, sigNum) == 0;
+
   }
 
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    long temp;
+    temp = Double.doubleToLongBits(sigNum);
+    result = 31 * result + (int) (temp ^ (temp >>> 32));
+    return result;
+  }
 
   @Override
   public String toString() {

Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java Wed Jul  1 02:03:43 2015
@@ -52,7 +52,8 @@ public class Vector {
   /**
    * Construct a vector that is perpendicular to
    * two other (non-zero) vectors.  If the vectors are parallel,
-   * the result vector will have magnitude 0.
+   * IllegalArgumentException will be thrown.
+   * Produces a normalized final vector.
    *
    * @param A is the first vector
    * @param B is the second
@@ -61,12 +62,25 @@ public class Vector {
     // x = u2v3 - u3v2
     // y = u3v1 - u1v3
     // z = u1v2 - u2v1
-
-    this(A.y * B.z - A.z * B.y,
-        A.z * B.x - A.x * B.z,
-        A.x * B.y - A.y * B.x);
+    final double thisX = A.y * B.z - A.z * B.y;
+    final double thisY = A.z * B.x - A.x * B.z;
+    final double thisZ = A.x * B.y - A.y * B.x;
+    final double magnitude = magnitude(thisX, thisY, thisZ);
+    if (Math.abs(magnitude) < MINIMUM_RESOLUTION) {
+      throw new IllegalArgumentException("Degenerate/parallel vector constructed");
+    }
+    final double inverseMagnitude = 1.0 / magnitude;
+    this.x = thisX * inverseMagnitude;
+    this.y = thisY * inverseMagnitude;
+    this.z = thisZ * inverseMagnitude;
   }
 
+  /** Compute a magnitude of an x,y,z value.
+   */
+  public static double magnitude(final double x, final double y, final double z) {
+    return Math.sqrt(x*x + y*y + z*z);
+  }
+  
   /**
    * Compute a normalized unit vector based on the current vector.
    *
@@ -304,7 +318,7 @@ public class Vector {
    * @return the magnitude.
    */
   public double magnitude() {
-    return Math.sqrt(x * x + y * y + z * z);
+    return magnitude(x,y,z);
   }
 
   /** Compute the desired magnitude of a unit vector projected to a given

Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java Wed Jul  1 02:03:43 2015
@@ -66,6 +66,9 @@ public class GeoBBoxTest {
     assertFalse(box.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.1);
     assertFalse(box.isWithin(gp));
+    assertEquals(0.1,box.computeOutsideDistance(DistanceStyle.ARC,gp),1e-2);
+    assertEquals(0.1,box.computeOutsideDistance(DistanceStyle.NORMAL,gp),1e-2);
+    assertEquals(0.1,box.computeOutsideDistance(DistanceStyle.NORMAL,gp),1e-2);
 
     // Standard normal Rect box, crossing dateline
     box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0);
@@ -77,8 +80,8 @@ public class GeoBBoxTest {
     assertFalse(box.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
     assertFalse(box.isWithin(gp));
-    //bad lon: gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI - 1.1);
-    //assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
+    assertFalse(box.isWithin(gp));
 
     // Latitude zone rectangle
     box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI, Math.PI);
@@ -90,8 +93,8 @@ public class GeoBBoxTest {
     assertFalse(box.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
     assertTrue(box.isWithin(gp));
-    //bad lon: gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI - 1.1);
-    //assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
+    assertTrue(box.isWithin(gp));
 
     // World
     box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, Math.PI);
@@ -103,8 +106,8 @@ public class GeoBBoxTest {
     assertTrue(box.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
     assertTrue(box.isWithin(gp));
-    //bad lat: gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI - 1.1);
-    //assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
+    assertTrue(box.isWithin(gp));
 
   }
 

Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java Wed Jul  1 02:03:43 2015
@@ -32,17 +32,17 @@ public class GeoCircleTest {
     GeoPoint gp;
     c = new GeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
-    assertEquals(Double.MAX_VALUE, c.computeArcDistance(gp), 0.0);
-    assertEquals(Double.MAX_VALUE, c.computeLinearDistance(gp), 0.0);
-    assertEquals(Double.MAX_VALUE, c.computeNormalDistance(gp), 0.0);
+    assertEquals(Double.MAX_VALUE, c.computeDistance(DistanceStyle.ARC,gp), 0.0);
+    assertEquals(Double.MAX_VALUE, c.computeDistance(DistanceStyle.NORMAL,gp), 0.0);
+    assertEquals(Double.MAX_VALUE, c.computeDistance(DistanceStyle.NORMAL,gp), 0.0);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
-    assertEquals(0.0, c.computeArcDistance(gp), 0.000001);
-    assertEquals(0.0, c.computeLinearDistance(gp), 0.000001);
-    assertEquals(0.0, c.computeNormalDistance(gp), 0.000001);
+    assertEquals(0.0, c.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    assertEquals(0.0, c.computeDistance(DistanceStyle.NORMAL,gp), 0.000001);
+    assertEquals(0.0, c.computeDistance(DistanceStyle.NORMAL,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
-    assertEquals(0.05, c.computeArcDistance(gp), 0.000001);
-    assertEquals(0.049995, c.computeLinearDistance(gp), 0.000001);
-    assertEquals(0.049979, c.computeNormalDistance(gp), 0.000001);
+    assertEquals(0.05, c.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    assertEquals(0.049995, c.computeDistance(DistanceStyle.LINEAR,gp), 0.000001);
+    assertEquals(0.049979, c.computeDistance(DistanceStyle.NORMAL,gp), 0.000001);
   }
 
   @Test
@@ -75,6 +75,9 @@ public class GeoCircleTest {
     c = new GeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
     assertFalse(c.isWithin(gp));
+    assertEquals(0.4,c.computeOutsideDistance(DistanceStyle.ARC,gp),1e-12);
+    assertEquals(0.12,c.computeOutsideDistance(DistanceStyle.NORMAL,gp),0.01);
+    assertEquals(0.4,c.computeOutsideDistance(DistanceStyle.LINEAR,gp),0.01);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
     assertTrue(c.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);

Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java Wed Jul  1 02:03:43 2015
@@ -46,9 +46,12 @@ public class GeoConvexPolygonTest {
     assertTrue(c.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
     assertTrue(c.isWithin(gp));
-    // Sample some nearby points outside
+    // Sample some nearby points outside, and compute distance-to-shape for them as well
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
     assertFalse(c.isWithin(gp));
+    assertEquals(0.05,c.computeOutsideDistance(DistanceStyle.ARC,gp),1e-12);
+    assertEquals(0.05,c.computeOutsideDistance(DistanceStyle.NORMAL,gp),1e-3);
+    assertEquals(0.05,c.computeOutsideDistance(DistanceStyle.LINEAR,gp),1e-3);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
     assertFalse(c.isWithin(gp));
     gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);

Modified: lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java?rev=1688545&r1=1688544&r2=1688545&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java Wed Jul  1 02:03:43 2015
@@ -37,17 +37,17 @@ public class GeoPathTest {
     p.addPoint(0.0, 0.2);
     p.done();
     gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.15);
-    assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15);
-    assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.15 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12);
-    assertEquals(0.12 + 0.0, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.12 + 0.0, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, -0.15, 0.05);
-    assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.000001);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.25);
-    assertEquals(0.20 + 0.05, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.20 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.05);
-    assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
 
     // Compute path distances now
     p = new GeoPath(PlanetModel.SPHERE, 0.1);
@@ -56,9 +56,9 @@ public class GeoPathTest {
     p.addPoint(0.0, 0.2);
     p.done();
     gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15);
-    assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.15 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12);
-    assertEquals(0.12, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.12, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
 
     // Now try a vertical path, and make sure distances are as expected
     p = new GeoPath(PlanetModel.SPHERE, 0.1);
@@ -66,13 +66,13 @@ public class GeoPathTest {
     p.addPoint(Math.PI * 0.25, -0.5);
     p.done();
     gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
-    assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
     gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.0);
-    assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
     gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.25 + 0.05, -0.5);
-    assertEquals(Math.PI * 0.5 + 0.05, p.computeArcDistance(gp), 0.000001);
+    assertEquals(Math.PI * 0.5 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
     gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, -0.5);
-    assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001);
+    assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
   }
 
   @Test