You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by kw...@apache.org on 2022/11/23 23:52:26 UTC

[lucene] 10/19: Fix nearestDistance for real this time

This is an automated email from the ASF dual-hosted git repository.

kwright pushed a commit to branch branch_9x
in repository https://gitbox.apache.org/repos/asf/lucene.git

commit fe62787386afcc76fd4ecb4429df2fe241724b30
Author: Karl David Wright <kw...@apache.org>
AuthorDate: Tue Nov 22 12:55:18 2022 -0500

    Fix nearestDistance for real this time
---
 .../lucene/spatial3d/geom/GeoDegeneratePath.java   | 114 ++++++-------
 .../lucene/spatial3d/geom/GeoStandardPath.java     | 182 ++++++++++++++++++---
 2 files changed, 206 insertions(+), 90 deletions(-)

diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePath.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePath.java
index b13bbfe50ff..472dedb158f 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePath.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePath.java
@@ -21,9 +21,9 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * GeoShape representing a path across the surface of the globe, with a specified half-width. Path
@@ -109,7 +109,7 @@ class GeoDegeneratePath extends GeoBasePath {
       // Simple circle
       final GeoPoint point = points.get(0);
 
-      final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(point);
+      final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(planetModel, point);
       endPoints.add(onlyEndpoint);
       this.edgePoints = new GeoPoint[] {point};
       return;
@@ -122,7 +122,7 @@ class GeoDegeneratePath extends GeoBasePath {
       if (i == 0) {
         // Starting endpoint
         final SegmentEndpoint startEndpoint =
-            new SegmentEndpoint(currentSegment.start, currentSegment.startCutoffPlane);
+            new SegmentEndpoint(planetModel, currentSegment.start, currentSegment.startCutoffPlane);
         endPoints.add(startEndpoint);
         this.edgePoints = new GeoPoint[] {currentSegment.start};
         continue;
@@ -130,13 +130,14 @@ class GeoDegeneratePath extends GeoBasePath {
 
       endPoints.add(
           new SegmentEndpoint(
+              planetModel,
               currentSegment.start,
               segments.get(i - 1).endCutoffPlane,
               currentSegment.startCutoffPlane));
     }
     // Do final endpoint
     final PathSegment lastSegment = segments.get(segments.size() - 1);
-    endPoints.add(new SegmentEndpoint(lastSegment.end, lastSegment.endCutoffPlane));
+    endPoints.add(new SegmentEndpoint(planetModel, lastSegment.end, lastSegment.endCutoffPlane));
   }
 
   /**
@@ -162,8 +163,7 @@ class GeoDegeneratePath extends GeoBasePath {
     double closestDistance = Double.POSITIVE_INFINITY;
     // Segments first
     for (PathSegment segment : segments) {
-      final double segmentDistance =
-          segment.pathCenterDistance(planetModel, distanceStyle, x, y, z);
+      final double segmentDistance = segment.pathCenterDistance(distanceStyle, x, y, z);
       if (segmentDistance < closestDistance) {
         closestDistance = segmentDistance;
       }
@@ -196,14 +196,12 @@ class GeoDegeneratePath extends GeoBasePath {
       // Look at the following segment, if any
       if (segmentIndex < segments.size()) {
         final PathSegment segment = segments.get(segmentIndex++);
-        final double segmentPathCenterDistance =
-            segment.pathCenterDistance(planetModel, distanceStyle, x, y, z);
+        final double segmentPathCenterDistance = segment.pathCenterDistance(distanceStyle, x, y, z);
         if (segmentPathCenterDistance < minPathCenterDistance) {
           minPathCenterDistance = segmentPathCenterDistance;
           bestDistance =
               distanceStyle.aggregateDistances(
-                  currentDistance,
-                  segment.nearestPathDistance(planetModel, distanceStyle, x, y, z));
+                  currentDistance, segment.nearestPathDistance(distanceStyle, x, y, z));
         }
         currentDistance =
             distanceStyle.aggregateDistances(
@@ -221,7 +219,7 @@ class GeoDegeneratePath extends GeoBasePath {
     // (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(planetModel, distanceStyle, x, y, z);
+      double distance = segment.pathDistance(distanceStyle, x, y, z);
       if (distance != Double.POSITIVE_INFINITY)
         return distanceStyle.fromAggregationForm(
             distanceStyle.aggregateDistances(currentDistance, distance));
@@ -272,7 +270,7 @@ class GeoDegeneratePath extends GeoBasePath {
       }
     }
     for (final PathSegment segment : segments) {
-      final double newDistance = segment.outsideDistance(planetModel, distanceStyle, x, y, z);
+      final double newDistance = segment.outsideDistance(distanceStyle, x, y, z);
       if (newDistance < minDistance) {
         minDistance = newDistance;
       }
@@ -317,11 +315,11 @@ class GeoDegeneratePath extends GeoBasePath {
     // Since the endpoints are included in the path segments, we only need to do this if there are
     // no path segments
     if (endPoints.size() == 1) {
-      return endPoints.get(0).intersects(planetModel, plane, notablePoints, bounds);
+      return endPoints.get(0).intersects(plane, notablePoints, bounds);
     }
 
     for (final PathSegment pathSegment : segments) {
-      if (pathSegment.intersects(planetModel, plane, notablePoints, bounds)) {
+      if (pathSegment.intersects(plane, notablePoints, bounds)) {
         return true;
       }
     }
@@ -353,10 +351,10 @@ class GeoDegeneratePath extends GeoBasePath {
     // never more than 180 degrees longitude at a pop or we risk having the
     // bounds object get itself inverted.  So do the edges first.
     for (PathSegment pathSegment : segments) {
-      pathSegment.getBounds(planetModel, bounds);
+      pathSegment.getBounds(bounds);
     }
     if (endPoints.size() == 1) {
-      endPoints.get(0).getBounds(planetModel, bounds);
+      endPoints.get(0).getBounds(bounds);
     }
   }
 
@@ -396,7 +394,7 @@ class GeoDegeneratePath extends GeoBasePath {
    *   <li>Intersection. There are two cutoff planes, one for each end of the intersection.
    * </ol>
    */
-  private static class SegmentEndpoint {
+  private static class SegmentEndpoint extends GeoBaseBounds {
     /** The center point of the endpoint */
     public final GeoPoint point;
     /** Pertinent cutoff planes from adjoining segments */
@@ -407,9 +405,11 @@ class GeoDegeneratePath extends GeoBasePath {
     /**
      * Constructor for case (1).
      *
+     * @param planetModel is the planet model.
      * @param point is the center point.
      */
-    public SegmentEndpoint(final GeoPoint point) {
+    public SegmentEndpoint(final PlanetModel planetModel, final GeoPoint point) {
+      super(planetModel);
       this.point = point;
       this.cutoffPlanes = NO_MEMBERSHIP;
     }
@@ -422,7 +422,9 @@ class GeoDegeneratePath extends GeoBasePath {
      * @param cutoffPlane is the plane from the adjoining path segment marking the boundary between
      *     this endpoint and that segment.
      */
-    public SegmentEndpoint(final GeoPoint point, final SidedPlane cutoffPlane) {
+    public SegmentEndpoint(
+        final PlanetModel planetModel, final GeoPoint point, final SidedPlane cutoffPlane) {
+      super(planetModel);
       this.point = point;
       this.cutoffPlanes = new Membership[] {new SidedPlane(cutoffPlane)};
     }
@@ -430,12 +432,17 @@ class GeoDegeneratePath extends GeoBasePath {
     /**
      * Constructor for case (3). Generate an endpoint, given two cutoff planes.
      *
+     * @param planetModel is the planet model.
      * @param point is the center.
      * @param cutoffPlane1 is one adjoining path segment cutoff plane.
      * @param cutoffPlane2 is another adjoining path segment cutoff plane.
      */
     public SegmentEndpoint(
-        final GeoPoint point, final SidedPlane cutoffPlane1, final SidedPlane cutoffPlane2) {
+        final PlanetModel planetModel,
+        final GeoPoint point,
+        final SidedPlane cutoffPlane1,
+        final SidedPlane cutoffPlane2) {
+      super(planetModel);
       this.point = point;
       this.cutoffPlanes =
           new Membership[] {new SidedPlane(cutoffPlane1), new SidedPlane(cutoffPlane2)};
@@ -507,17 +514,13 @@ class GeoDegeneratePath extends GeoBasePath {
     /**
      * Determine if this endpoint intersects a specified plane.
      *
-     * @param planetModel is the planet model.
      * @param p is the plane.
      * @param notablePoints are the points associated with the plane.
      * @param bounds are any bounds which the intersection must lie within.
      * @return true if there is a matching intersection.
      */
     public boolean intersects(
-        final PlanetModel planetModel,
-        final Plane p,
-        final GeoPoint[] notablePoints,
-        final Membership[] bounds) {
+        final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
       // If not on the plane, no intersection
       if (!p.evaluateIsZero(point)) {
         return false;
@@ -572,13 +575,13 @@ class GeoDegeneratePath extends GeoBasePath {
   }
 
   /** This is the pre-calculated data for a path segment. */
-  private static class PathSegment {
+  private static class PathSegment extends GeoBaseBounds {
     /** Starting point of the segment */
     public final GeoPoint start;
     /** End point of the segment */
     public final GeoPoint end;
     /** Place to keep any complete segment distances we've calculated so far */
-    public final Map<DistanceStyle, Double> fullDistanceCache = new HashMap<>();
+    public final Map<DistanceStyle, Double> fullDistanceCache = new ConcurrentHashMap<>(1);
     /** Normalized plane connecting the two points and going through world center */
     public final Plane normalizedConnectingPlane;
     /** Plane going through the center and start point, marking the start edge of the segment */
@@ -601,6 +604,7 @@ class GeoDegeneratePath extends GeoBasePath {
         final GeoPoint start,
         final GeoPoint end,
         final Plane normalizedConnectingPlane) {
+      super(planetModel);
       this.start = start;
       this.end = end;
       this.normalizedConnectingPlane = normalizedConnectingPlane;
@@ -618,16 +622,14 @@ class GeoDegeneratePath extends GeoBasePath {
      * @return the distance metric, in aggregation form.
      */
     public double fullPathDistance(final DistanceStyle distanceStyle) {
-      synchronized (fullDistanceCache) {
-        Double dist = fullDistanceCache.get(distanceStyle);
-        if (dist == null) {
-          dist =
-              distanceStyle.toAggregationForm(
-                  distanceStyle.computeDistance(start, end.x, end.y, end.z));
-          fullDistanceCache.put(distanceStyle, dist);
-        }
-        return dist.doubleValue();
+      Double dist = fullDistanceCache.get(distanceStyle);
+      if (dist == null) {
+        dist =
+            distanceStyle.toAggregationForm(
+                distanceStyle.computeDistance(start, end.x, end.y, end.z));
+        fullDistanceCache.put(distanceStyle, dist);
       }
+      return dist.doubleValue();
     }
 
     /**
@@ -638,6 +640,7 @@ class GeoDegeneratePath extends GeoBasePath {
      * @param z is the point z.
      * @return true of within.
      */
+    @Override
     public boolean isWithin(final double x, final double y, final double z) {
       return startCutoffPlane.isWithin(x, y, z)
           && endCutoffPlane.isWithin(x, y, z)
@@ -645,9 +648,8 @@ class GeoDegeneratePath extends GeoBasePath {
     }
 
     /**
-     * Compute path center distance.
+     * Compute path center distance (distance from path to current point).
      *
-     * @param planetModel is the planet model.
      * @param distanceStyle is the distance style.
      * @param x is the point x.
      * @param y is the point y.
@@ -655,11 +657,7 @@ class GeoDegeneratePath extends GeoBasePath {
      * @return the distance metric, or Double.POSITIVE_INFINITY if outside this segment
      */
     public double pathCenterDistance(
-        final PlanetModel planetModel,
-        final DistanceStyle distanceStyle,
-        final double x,
-        final double y,
-        final double z) {
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
       // First, if this point is outside the endplanes of the segment, return POSITIVE_INFINITY.
       if (!startCutoffPlane.isWithin(x, y, z) || !endCutoffPlane.isWithin(x, y, z)) {
         return Double.POSITIVE_INFINITY;
@@ -704,9 +702,8 @@ class GeoDegeneratePath extends GeoBasePath {
     }
 
     /**
-     * Compute nearest path distance.
+     * Compute nearest path distance (distance from start of segment to center line point adjacent).
      *
-     * @param planetModel is the planet model.
      * @param distanceStyle is the distance style.
      * @param x is the point x.
      * @param y is the point y.
@@ -715,11 +712,7 @@ class GeoDegeneratePath extends GeoBasePath {
      *     segment
      */
     public double nearestPathDistance(
-        final PlanetModel planetModel,
-        final DistanceStyle distanceStyle,
-        final double x,
-        final double y,
-        final double z) {
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
       // First, if this point is outside the endplanes of the segment, return POSITIVE_INFINITY.
       if (!startCutoffPlane.isWithin(x, y, z) || !endCutoffPlane.isWithin(x, y, z)) {
         return Double.POSITIVE_INFINITY;
@@ -775,11 +768,7 @@ class GeoDegeneratePath extends GeoBasePath {
      * @return the distance metric, in aggregation form.
      */
     public double pathDistance(
-        final PlanetModel planetModel,
-        final DistanceStyle distanceStyle,
-        final double x,
-        final double y,
-        final double z) {
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
       if (!isWithin(x, y, z)) return Double.POSITIVE_INFINITY;
 
       // (1) Compute normalizedPerpPlane.  If degenerate, then return point distance from start to
@@ -839,11 +828,7 @@ class GeoDegeneratePath extends GeoBasePath {
      * @return the distance metric.
      */
     public double outsideDistance(
-        final PlanetModel planetModel,
-        final DistanceStyle distanceStyle,
-        final double x,
-        final double y,
-        final double z) {
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
       final double distance =
           distanceStyle.computeDistance(
               planetModel, normalizedConnectingPlane, x, y, z, startCutoffPlane, endCutoffPlane);
@@ -862,10 +847,7 @@ class GeoDegeneratePath extends GeoBasePath {
      * @return true if there is a matching intersection.
      */
     public boolean intersects(
-        final PlanetModel planetModel,
-        final Plane p,
-        final GeoPoint[] notablePoints,
-        final Membership[] bounds) {
+        final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
       return normalizedConnectingPlane.intersects(
           planetModel,
           p,
@@ -893,7 +875,9 @@ class GeoDegeneratePath extends GeoBasePath {
      * @param planetModel is the planet model.
      * @param bounds are the bounds to be modified.
      */
-    public void getBounds(final PlanetModel planetModel, Bounds bounds) {
+    @Override
+    public void getBounds(final Bounds bounds) {
+      super.getBounds(bounds);
       // We need to do all bounding planes as well as corner points
       bounds
           .addPoint(start)
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
index 4d711a1efa3..072b276cd4a 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
@@ -108,7 +108,7 @@ class GeoStandardPath extends GeoBasePath {
 
     final List<SegmentEndpoint> endPoints = new ArrayList<>(points.size());
     final List<PathSegment> segments = new ArrayList<>(points.size());
-    
+
     // Compute an offset to use for all segments.  This will be based on the minimum magnitude of
     // the entire ellipsoid.
     final double cutoffOffset = this.sinAngle * planetModel.getMinimumMagnitude();
@@ -351,7 +351,11 @@ class GeoStandardPath extends GeoBasePath {
     if (rootComponent == null) {
       return false;
     }
-    return rootComponent.intersects(plane, notablePoints, bounds);
+    final boolean rval = rootComponent.intersects(plane, notablePoints, bounds);
+    if (rval) {
+      System.out.println("Plane " + plane + " within its bounds intersects " + rootComponent);
+    }
+    return rval;
   }
 
   @Override
@@ -359,7 +363,11 @@ class GeoStandardPath extends GeoBasePath {
     if (rootComponent == null) {
       return false;
     }
-    return rootComponent.intersects(geoShape);
+    final boolean rval = rootComponent.intersects(geoShape);
+    if (rval) {
+      System.out.println("Shape " + geoShape + " intersects " + rootComponent);
+    }
+    return rval;
   }
 
   @Override
@@ -430,6 +438,12 @@ class GeoStandardPath extends GeoBasePath {
      */
     boolean isWithin(final double x, final double y, final double z);
 
+    /** Check if point is within this section (within cutoff planes). */
+    boolean isWithinSection(final Vector point);
+
+    /** Check if point is within this section (within cutoff planes). */
+    boolean isWithinSection(final double x, final double y, final double z);
+
     /**
      * Retrieve the starting distance along the path for this path element.
      *
@@ -498,28 +512,28 @@ class GeoStandardPath extends GeoBasePath {
         final DistanceStyle distanceStyle, final double x, final double y, final double z);
 
     /**
-     * Compute nearest path distance.
+     * Compute nearest path distance (distance from start of segment to point adjacent the one
+     * specitied, if reachable by this segment).
      *
      * @param distanceStyle is the distance style.
      * @param x is the point x.
      * @param y is the point y.
      * @param z is the point z.
-     * @return the distance metric (always value zero), in aggregation form, or POSITIVE_INFINITY if
-     *     the point is not within the bounds of the endpoint.
+     * @return the distance metric, in aggregation form.
      */
     double nearestPathDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z);
 
     /**
-     * Compute path center distance. Returns POSITIVE_INFINITY if the point is outside of the
-     * bounds.
+     * Compute path center distance (distance from the point to center of the path, if reachable by
+     * this segment).
      *
      * @param distanceStyle is the distance style.
      * @param x is the point x.
      * @param y is the point y.
      * @param z is the point z.
      * @return the distance metric, or POSITIVE_INFINITY if the point is not within the bounds of
-     *     the endpoint.
+     *     the path segment.
      */
     double pathCenterDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z);
@@ -611,6 +625,16 @@ class GeoStandardPath extends GeoBasePath {
       return child1.isWithin(x, y, z) || child2.isWithin(x, y, z);
     }
 
+    @Override
+    public boolean isWithinSection(final Vector point) {
+      return child1.isWithinSection(point) || child2.isWithinSection(point);
+    }
+
+    @Override
+    public boolean isWithinSection(final double x, final double y, final double z) {
+      return child1.isWithinSection(x, y, z) || child2.isWithinSection(x, y, z);
+    }
+
     @Override
     public double getStartingDistance(final DistanceStyle distanceStyle) {
       return child1.getStartingDistance(distanceStyle);
@@ -632,7 +656,7 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double nearestDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       return Math.min(
@@ -672,7 +696,7 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double nearestPathDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       return Math.min(
@@ -683,7 +707,7 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double pathCenterDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       return Math.min(
@@ -703,8 +727,17 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public boolean intersects(
         final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
-      return child1.intersects(p, notablePoints, bounds)
-          || child2.intersects(p, notablePoints, bounds);
+      final boolean rval =
+          child1.intersects(p, notablePoints, bounds)
+              || child2.intersects(p, notablePoints, bounds);
+      if (rval) {
+        if (child1.intersects(p, notablePoints, bounds)) {
+          System.out.println("Plane " + p + " intersected " + child1);
+        } else if (child2.intersects(p, notablePoints, bounds)) {
+          System.out.println("Plane " + p + " intersected " + child2);
+        }
+      }
+      return rval;
     }
 
     @Override
@@ -769,6 +802,16 @@ class GeoStandardPath extends GeoBasePath {
       return false;
     }
 
+    @Override
+    public boolean isWithinSection(final double x, final double y, final double z) {
+      return true;
+    }
+
+    @Override
+    public boolean isWithinSection(final Vector point) {
+      return true;
+    }
+
     @Override
     public double getStartingDistance(DistanceStyle distanceStyle) {
       if (previous == null) {
@@ -793,7 +836,7 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double nearestDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       return distanceStyle.fromAggregationForm(getStartingDistance(distanceStyle));
@@ -827,16 +870,16 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double nearestPathDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
-      return distanceStyle.toAggregationForm(0.0);
+      return distanceStyle.toAggregationForm(distanceStyle.computeDistance(this.point, x, y, z));
     }
 
     @Override
     public double pathCenterDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       return distanceStyle.computeDistance(this.point, x, y, z);
@@ -1023,6 +1066,16 @@ class GeoStandardPath extends GeoBasePath {
       return cutoffPlane.isWithin(x, y, z) && super.isWithin(x, y, z);
     }
 
+    @Override
+    public boolean isWithinSection(final Vector point) {
+      return cutoffPlane.isWithin(point);
+    }
+
+    @Override
+    public boolean isWithinSection(final double x, final double y, final double z) {
+      return cutoffPlane.isWithin(x, y, z);
+    }
+
     @Override
     public boolean intersects(
         final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
@@ -1035,6 +1088,28 @@ class GeoStandardPath extends GeoBasePath {
       return geoShape.intersects(circlePlane, this.notablePoints, this.cutoffPlanes);
     }
 
+    @Override
+    public double nearestPathDistance(
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      for (final Membership cutoff : cutoffPlanes) {
+        if (!cutoff.isWithin(x, y, z)) {
+          return Double.POSITIVE_INFINITY;
+        }
+      }
+      return super.nearestPathDistance(distanceStyle, x, y, z);
+    }
+
+    @Override
+    public double pathCenterDistance(
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      for (final Membership cutoff : cutoffPlanes) {
+        if (!cutoff.isWithin(x, y, z)) {
+          return Double.POSITIVE_INFINITY;
+        }
+      }
+      return super.pathCenterDistance(distanceStyle, x, y, z);
+    }
+
     @Override
     public void getBounds(final Bounds bounds) {
       super.getBounds(bounds);
@@ -1144,6 +1219,26 @@ class GeoStandardPath extends GeoBasePath {
       return circlePlane1.isWithin(x, y, z) || circlePlane2.isWithin(x, y, z);
     }
 
+    @Override
+    public boolean isWithinSection(final Vector point) {
+      for (final Membership m : cutoffPlanes) {
+        if (!m.isWithin(point)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    @Override
+    public boolean isWithinSection(final double x, final double y, final double z) {
+      for (final Membership m : cutoffPlanes) {
+        if (!m.isWithin(x, y, z)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
     @Override
     public boolean intersects(
         final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) {
@@ -1159,6 +1254,28 @@ class GeoStandardPath extends GeoBasePath {
           || geoShape.intersects(circlePlane2, this.notablePoints2, this.cutoffPlanes);
     }
 
+    @Override
+    public double nearestPathDistance(
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      for (final Membership cutoff : cutoffPlanes) {
+        if (!cutoff.isWithin(x, y, z)) {
+          return Double.POSITIVE_INFINITY;
+        }
+      }
+      return super.nearestPathDistance(distanceStyle, x, y, z);
+    }
+
+    @Override
+    public double pathCenterDistance(
+        final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+      for (final Membership cutoff : cutoffPlanes) {
+        if (!cutoff.isWithin(x, y, z)) {
+          return Double.POSITIVE_INFINITY;
+        }
+      }
+      return super.pathCenterDistance(distanceStyle, x, y, z);
+    }
+
     @Override
     public void getBounds(final Bounds bounds) {
       super.getBounds(bounds);
@@ -1311,6 +1428,16 @@ class GeoStandardPath extends GeoBasePath {
       return isWithin(v.x, v.y, v.z);
     }
 
+    @Override
+    public boolean isWithinSection(final Vector point) {
+      return startCutoffPlane.isWithin(point) && endCutoffPlane.isWithin(point);
+    }
+
+    @Override
+    public boolean isWithinSection(final double x, final double y, final double z) {
+      return startCutoffPlane.isWithin(x, y, z) && endCutoffPlane.isWithin(x, y, z);
+    }
+
     @Override
     public double getStartingDistance(final DistanceStyle distanceStyle) {
       Double dist = startDistanceCache.get(distanceStyle);
@@ -1338,12 +1465,14 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double nearestDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      if (!isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       return distanceStyle.fromAggregationForm(
           distanceStyle.aggregateDistances(
-              getStartingDistance(distanceStyle), nearestPathDistance(distanceStyle, x, y, z)));
+              distanceStyle.aggregateDistances(
+                  getStartingDistance(distanceStyle), nearestPathDistance(distanceStyle, x, y, z)),
+              pathCenterDistance(distanceStyle, x, y, z)));
     }
 
     private double computeStartingDistance(final DistanceStyle distanceStyle) {
@@ -1358,10 +1487,10 @@ class GeoStandardPath extends GeoBasePath {
     public double pathCenterDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
       // Computes the distance to the path center from the specified point, or returns
-      // POSITIVE_INFINITY if the point is outside the path.
+      // POSITIVE_INFINITY if the point is outside the path section.
 
       // First, if this point is outside the endplanes of the segment, return POSITIVE_INFINITY.
-      if (!startCutoffPlane.isWithin(x, y, z) || !endCutoffPlane.isWithin(x, y, z)) {
+      if (!isWithinSection(x, y, z)) {
         return Double.POSITIVE_INFINITY;
       }
       // (1) Compute normalizedPerpPlane.  If degenerate, then there is no such plane, which means
@@ -1400,7 +1529,7 @@ class GeoStandardPath extends GeoBasePath {
               "Can't find world intersection for point x=" + x + " y=" + y + " z=" + z);
         }
       }
-      return distanceStyle.computeDistance(thePoint, x, y, z);
+      return distanceStyle.toAggregationForm(distanceStyle.computeDistance(thePoint, x, y, z));
     }
 
     @Override
@@ -1408,7 +1537,10 @@ class GeoStandardPath extends GeoBasePath {
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
       // Computes the distance along the path to a point on the path where a perpendicular plane
       // goes through the specified point.
-
+      // First, if this point is outside the endplanes of the segment, return POSITIVE_INFINITY.
+      if (!isWithinSection(x, y, z)) {
+        return Double.POSITIVE_INFINITY;
+      }
       // (1) Compute normalizedPerpPlane.  If degenerate, then there is no such plane, which means
       // that the point given is insufficient to distinguish between a family of such planes.
       // This can happen only if the point is one of the "poles", imagining the normalized plane
@@ -1452,7 +1584,7 @@ class GeoStandardPath extends GeoBasePath {
     @Override
     public double pathDeltaDistance(
         final DistanceStyle distanceStyle, final double x, final double y, final double z) {
-      // Returns 2x the pathCenterDistance.  Represents the cost of an "excursion" to and
+      // Represents the cost of an "excursion" to and
       // from the path.
 
       if (!isWithin(x, y, z)) {