mirror of https://github.com/apache/lucene.git
LUCENE-7962: Revamp how computeNearestDistance is done to make it return the distance where the point is truly nearest to the given path.
This commit is contained in:
parent
c317ad8c1f
commit
8cb64ed926
|
@ -152,31 +152,52 @@ class GeoDegeneratePath extends GeoBasePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double computeNearestDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
public double computePathCenterDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
// Algorithm:
|
// Walk along path and keep track of the closest distance we find
|
||||||
// (1) If the point is within any of the segments along the path, return that value.
|
double closestDistance = Double.POSITIVE_INFINITY;
|
||||||
// (2) If the point is within any of the segment end circles along the path, return that value.
|
// Segments first
|
||||||
double currentDistance = 0.0;
|
|
||||||
for (PathSegment segment : segments) {
|
for (PathSegment segment : segments) {
|
||||||
double distance = segment.nearestPathDistance(planetModel, distanceStyle, x,y,z);
|
final double segmentDistance = segment.pathCenterDistance(planetModel, distanceStyle, x, y, z);
|
||||||
if (distance != Double.POSITIVE_INFINITY)
|
if (segmentDistance < closestDistance) {
|
||||||
return distanceStyle.fromAggregationForm(distanceStyle.aggregateDistances(currentDistance, distance));
|
closestDistance = segmentDistance;
|
||||||
currentDistance = distanceStyle.aggregateDistances(currentDistance, segment.fullPathDistance(distanceStyle));
|
}
|
||||||
}
|
}
|
||||||
|
// Now, endpoints
|
||||||
int segmentIndex = 0;
|
|
||||||
currentDistance = 0.0;
|
|
||||||
for (SegmentEndpoint endpoint : endPoints) {
|
for (SegmentEndpoint endpoint : endPoints) {
|
||||||
double distance = endpoint.nearestPathDistance(distanceStyle, x, y, z);
|
final double endpointDistance = endpoint.pathCenterDistance(distanceStyle, x, y, z);
|
||||||
if (distance != Double.POSITIVE_INFINITY) {
|
if (endpointDistance < closestDistance) {
|
||||||
return distanceStyle.fromAggregationForm(distanceStyle.aggregateDistances(currentDistance, distance));
|
closestDistance = endpointDistance;
|
||||||
}
|
|
||||||
if (segmentIndex < segments.size()) {
|
|
||||||
currentDistance = distanceStyle.aggregateDistances(currentDistance, segments.get(segmentIndex++).fullPathDistance(distanceStyle));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return closestDistance;
|
||||||
|
}
|
||||||
|
|
||||||
return Double.POSITIVE_INFINITY;
|
@Override
|
||||||
|
public double computeNearestDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
|
double currentDistance = 0.0;
|
||||||
|
double minPathCenterDistance = Double.POSITIVE_INFINITY;
|
||||||
|
double bestDistance = Double.POSITIVE_INFINITY;
|
||||||
|
int segmentIndex = 0;
|
||||||
|
|
||||||
|
for (SegmentEndpoint endpoint : endPoints) {
|
||||||
|
final double endpointPathCenterDistance = endpoint.pathCenterDistance(distanceStyle, x, y, z);
|
||||||
|
if (endpointPathCenterDistance < minPathCenterDistance) {
|
||||||
|
// Use this endpoint
|
||||||
|
minPathCenterDistance = endpointPathCenterDistance;
|
||||||
|
bestDistance = currentDistance;
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
if (segmentPathCenterDistance < minPathCenterDistance) {
|
||||||
|
minPathCenterDistance = segmentPathCenterDistance;
|
||||||
|
bestDistance = distanceStyle.aggregateDistances(currentDistance, segment.nearestPathDistance(planetModel, distanceStyle, x, y, z));
|
||||||
|
}
|
||||||
|
currentDistance = distanceStyle.aggregateDistances(currentDistance, segment.fullPathDistance(distanceStyle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -428,6 +449,23 @@ class GeoDegeneratePath extends GeoBasePath {
|
||||||
return distanceStyle.toAggregationForm(0.0);
|
return distanceStyle.toAggregationForm(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute path center distance.
|
||||||
|
*@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.
|
||||||
|
*/
|
||||||
|
public double pathCenterDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
|
for (final Membership m : cutoffPlanes) {
|
||||||
|
if (!m.isWithin(x,y,z)) {
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distanceStyle.computeDistance(this.point, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute external distance.
|
/** Compute external distance.
|
||||||
*@param distanceStyle is the distance style.
|
*@param distanceStyle is the distance style.
|
||||||
*@param x is the point x.
|
*@param x is the point x.
|
||||||
|
@ -567,6 +605,49 @@ class GeoDegeneratePath extends GeoBasePath {
|
||||||
normalizedConnectingPlane.evaluateIsZero(x, y, z);
|
normalizedConnectingPlane.evaluateIsZero(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute path center distance.
|
||||||
|
*@param planetModel is the planet model.
|
||||||
|
*@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 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) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// (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 to be the "equator". In that case, the distance returned should be zero.
|
||||||
|
// Want no allocations or expensive operations! so we do this the hard way
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute nearest path distance.
|
/** Compute nearest path distance.
|
||||||
*@param planetModel is the planet model.
|
*@param planetModel is the planet model.
|
||||||
*@param distanceStyle is the distance style.
|
*@param distanceStyle is the distance style.
|
||||||
|
|
|
@ -52,4 +52,32 @@ public interface GeoPath extends GeoDistanceShape {
|
||||||
*/
|
*/
|
||||||
public double computeNearestDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z);
|
public double computeNearestDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z);
|
||||||
|
|
||||||
|
// The following methods compute the best distance from the path center to the point.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the shortest distance from the path center to the GeoPoint.
|
||||||
|
* The distance is meant to allow comparisons between different
|
||||||
|
* paths to find the one that goes closest to a point.
|
||||||
|
*
|
||||||
|
* @param distanceStyle is the distance style.
|
||||||
|
* @param point is the point to compute the distance to.
|
||||||
|
* @return the shortest distance from the path center to the point.
|
||||||
|
*/
|
||||||
|
public default double computePathCenterDistance(final DistanceStyle distanceStyle, final GeoPoint point) {
|
||||||
|
return computePathCenterDistance(distanceStyle, point.x, point.y, point.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the shortest distance from the path center to the GeoPoint.
|
||||||
|
* The distance is meant to allow comparisons between different
|
||||||
|
* paths to find the one that goes closest to a point.
|
||||||
|
*
|
||||||
|
* @param distanceStyle is the distance style.
|
||||||
|
* @param x is the point's unit x coordinate (using U.S. convention).
|
||||||
|
* @param y is the point's unit y coordinate (using U.S. convention).
|
||||||
|
* @param z is the point's unit z coordinate (using U.S. convention).
|
||||||
|
* @return the shortest distance from the path center to the point.
|
||||||
|
*/
|
||||||
|
public double computePathCenterDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -215,31 +215,52 @@ class GeoStandardPath extends GeoBasePath {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double computeNearestDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
public double computePathCenterDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
// Algorithm:
|
// Walk along path and keep track of the closest distance we find
|
||||||
// (1) If the point is within any of the segments along the path, return that value.
|
double closestDistance = Double.POSITIVE_INFINITY;
|
||||||
// (2) If the point is within any of the segment end circles along the path, return that value.
|
// Segments first
|
||||||
double currentDistance = 0.0;
|
|
||||||
for (PathSegment segment : segments) {
|
for (PathSegment segment : segments) {
|
||||||
double distance = segment.nearestPathDistance(planetModel, distanceStyle, x,y,z);
|
final double segmentDistance = segment.pathCenterDistance(planetModel, distanceStyle, x, y, z);
|
||||||
if (distance != Double.POSITIVE_INFINITY)
|
if (segmentDistance < closestDistance) {
|
||||||
return distanceStyle.fromAggregationForm(distanceStyle.aggregateDistances(currentDistance, distance));
|
closestDistance = segmentDistance;
|
||||||
currentDistance = distanceStyle.aggregateDistances(currentDistance, segment.fullPathDistance(distanceStyle));
|
}
|
||||||
}
|
}
|
||||||
|
// Now, endpoints
|
||||||
int segmentIndex = 0;
|
|
||||||
currentDistance = 0.0;
|
|
||||||
for (SegmentEndpoint endpoint : endPoints) {
|
for (SegmentEndpoint endpoint : endPoints) {
|
||||||
double distance = endpoint.nearestPathDistance(distanceStyle, x, y, z);
|
final double endpointDistance = endpoint.pathCenterDistance(distanceStyle, x, y, z);
|
||||||
if (distance != Double.POSITIVE_INFINITY) {
|
if (endpointDistance < closestDistance) {
|
||||||
return distanceStyle.fromAggregationForm(distanceStyle.aggregateDistances(currentDistance, distance));
|
closestDistance = endpointDistance;
|
||||||
}
|
|
||||||
if (segmentIndex < segments.size()) {
|
|
||||||
currentDistance = distanceStyle.aggregateDistances(currentDistance, segments.get(segmentIndex++).fullPathDistance(distanceStyle));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return closestDistance;
|
||||||
|
}
|
||||||
|
|
||||||
return Double.POSITIVE_INFINITY;
|
@Override
|
||||||
|
public double computeNearestDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
|
double currentDistance = 0.0;
|
||||||
|
double minPathCenterDistance = Double.POSITIVE_INFINITY;
|
||||||
|
double bestDistance = Double.POSITIVE_INFINITY;
|
||||||
|
int segmentIndex = 0;
|
||||||
|
|
||||||
|
for (SegmentEndpoint endpoint : endPoints) {
|
||||||
|
final double endpointPathCenterDistance = endpoint.pathCenterDistance(distanceStyle, x, y, z);
|
||||||
|
if (endpointPathCenterDistance < minPathCenterDistance) {
|
||||||
|
// Use this endpoint
|
||||||
|
minPathCenterDistance = endpointPathCenterDistance;
|
||||||
|
bestDistance = currentDistance;
|
||||||
|
}
|
||||||
|
// 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);
|
||||||
|
if (segmentPathCenterDistance < minPathCenterDistance) {
|
||||||
|
minPathCenterDistance = segmentPathCenterDistance;
|
||||||
|
bestDistance = distanceStyle.aggregateDistances(currentDistance, segment.nearestPathDistance(planetModel, distanceStyle, x, y, z));
|
||||||
|
}
|
||||||
|
currentDistance = distanceStyle.aggregateDistances(currentDistance, segment.fullPathDistance(distanceStyle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -607,6 +628,23 @@ class GeoStandardPath extends GeoBasePath {
|
||||||
return distanceStyle.toAggregationForm(0.0);
|
return distanceStyle.toAggregationForm(0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute path center distance.
|
||||||
|
*@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.
|
||||||
|
*/
|
||||||
|
public double pathCenterDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
|
||||||
|
for (final Membership m : cutoffPlanes) {
|
||||||
|
if (!m.isWithin(x,y,z)) {
|
||||||
|
return Double.POSITIVE_INFINITY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return distanceStyle.computeDistance(this.point, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute external distance.
|
/** Compute external distance.
|
||||||
*@param distanceStyle is the distance style.
|
*@param distanceStyle is the distance style.
|
||||||
*@param x is the point x.
|
*@param x is the point x.
|
||||||
|
@ -811,6 +849,49 @@ class GeoStandardPath extends GeoBasePath {
|
||||||
lowerConnectingPlane.isWithin(x, y, z);
|
lowerConnectingPlane.isWithin(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Compute path center distance.
|
||||||
|
*@param planetModel is the planet model.
|
||||||
|
*@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 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) {
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
// (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 to be the "equator". In that case, the distance returned should be zero.
|
||||||
|
// Want no allocations or expensive operations! so we do this the hard way
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute nearest path distance.
|
/** Compute nearest path distance.
|
||||||
*@param planetModel is the planet model.
|
*@param planetModel is the planet model.
|
||||||
*@param distanceStyle is the distance style.
|
*@param distanceStyle is the distance style.
|
||||||
|
|
Loading…
Reference in New Issue