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 2016/04/05 12:12:34 UTC
[1/2] lucene-solr:branch_6x: LUCENE-7176: Hide GeoPath implementation
in a factory/interface.
Repository: lucene-solr
Updated Branches:
refs/heads/branch_6x 56a4b6af3 -> 784e3e386
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
----------------------------------------------------------------------
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
new file mode 100755
index 0000000..1546439
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
@@ -0,0 +1,797 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 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.internal
+ */
+class GeoStandardPath extends GeoBasePath {
+ /** The cutoff angle (width) */
+ protected final double cutoffAngle;
+
+ /** Sine of cutoff angle */
+ protected final double sinAngle;
+ /** Cosine of cutoff angle */
+ protected final double cosAngle;
+
+ /** The original list of path points */
+ protected final List<GeoPoint> points = new ArrayList<GeoPoint>();
+
+ /** A list of SegmentEndpoints */
+ protected List<SegmentEndpoint> endPoints;
+ /** A list of PathSegments */
+ protected List<PathSegment> segments;
+
+ /** A point on the edge */
+ protected GeoPoint[] edgePoints;
+
+ /** Set to true if path has been completely constructed */
+ protected boolean isDone = false;
+
+ /** Constructor.
+ *@param planetModel is the planet model.
+ *@param maxCutoffAngle is the width of the path, measured as an angle.
+ *@param pathPoints are the points in the path.
+ */
+ public GeoStandardPath(final PlanetModel planetModel, final double maxCutoffAngle, final GeoPoint[] pathPoints) {
+ this(planetModel, maxCutoffAngle);
+ Collections.addAll(points, pathPoints);
+ done();
+ }
+
+ /** Piece-wise constructor. Use in conjunction with addPoint() and done().
+ *@param planetModel is the planet model.
+ *@param maxCutoffAngle is the width of the path, measured as an angle.
+ */
+ public GeoStandardPath(final PlanetModel planetModel, final double maxCutoffAngle) {
+ super(planetModel);
+ if (maxCutoffAngle <= 0.0 || maxCutoffAngle > Math.PI * 0.5)
+ throw new IllegalArgumentException("Cutoff angle out of bounds");
+ this.cutoffAngle = maxCutoffAngle;
+ this.cosAngle = Math.cos(maxCutoffAngle);
+ this.sinAngle = Math.sin(maxCutoffAngle);
+ }
+
+ /** Add a point to the path.
+ *@param lat is the latitude of the point.
+ *@param lon is the longitude of the point.
+ */
+ public void addPoint(final double lat, final double lon) {
+ if (isDone)
+ throw new IllegalStateException("Can't call addPoint() if done() already called");
+ points.add(new GeoPoint(planetModel, lat, lon));
+ }
+
+ /** Complete the path.
+ */
+ public void done() {
+ if (isDone)
+ throw new IllegalStateException("Can't call done() twice");
+ if (points.size() == 0)
+ throw new IllegalArgumentException("Path must have at least one point");
+ isDone = true;
+
+ endPoints = new ArrayList<>(points.size());
+ 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();
+
+ // First, build all segments. We'll then go back and build corresponding segment endpoints.
+ GeoPoint lastPoint = null;
+ for (final GeoPoint end : points) {
+ if (lastPoint != null) {
+ final Plane normalizedConnectingPlane = new Plane(lastPoint, end);
+ if (normalizedConnectingPlane == null) {
+ continue;
+ }
+ segments.add(new PathSegment(planetModel, lastPoint, end, normalizedConnectingPlane, cutoffOffset));
+ }
+ lastPoint = end;
+ }
+
+ if (segments.size() == 0) {
+ // Simple circle
+ double lat = points.get(0).getLatitude();
+ double lon = points.get(0).getLongitude();
+ // Compute two points on the circle, with the right angle from the center. We'll use these
+ // to obtain the perpendicular plane to the circle.
+ double upperLat = lat + cutoffAngle;
+ double upperLon = lon;
+ if (upperLat > Math.PI * 0.5) {
+ upperLon += Math.PI;
+ if (upperLon > Math.PI)
+ upperLon -= 2.0 * Math.PI;
+ upperLat = Math.PI - upperLat;
+ }
+ double lowerLat = lat - cutoffAngle;
+ double lowerLon = lon;
+ if (lowerLat < -Math.PI * 0.5) {
+ lowerLon += Math.PI;
+ if (lowerLon > Math.PI)
+ lowerLon -= 2.0 * Math.PI;
+ lowerLat = -Math.PI - lowerLat;
+ }
+ final GeoPoint upperPoint = new GeoPoint(planetModel, upperLat, upperLon);
+ final GeoPoint lowerPoint = new GeoPoint(planetModel, lowerLat, lowerLon);
+ final GeoPoint point = points.get(0);
+
+ // Construct normal plane
+ final Plane normalPlane = Plane.constructNormalizedZPlane(upperPoint, lowerPoint, point);
+
+ final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(point, normalPlane, upperPoint, lowerPoint);
+ endPoints.add(onlyEndpoint);
+ this.edgePoints = new GeoPoint[]{onlyEndpoint.circlePlane.getSampleIntersectionPoint(planetModel, normalPlane)};
+ return;
+ }
+
+ // Create segment endpoints. Use an appropriate constructor for the start and end of the path.
+ for (int i = 0; i < segments.size(); i++) {
+ final PathSegment currentSegment = segments.get(i);
+
+ if (i == 0) {
+ // Starting endpoint
+ final SegmentEndpoint startEndpoint = new SegmentEndpoint(currentSegment.start,
+ currentSegment.startCutoffPlane, currentSegment.ULHC, currentSegment.LLHC);
+ endPoints.add(startEndpoint);
+ this.edgePoints = new GeoPoint[]{currentSegment.ULHC};
+ continue;
+ }
+
+ // General intersection case
+ final PathSegment prevSegment = segments.get(i-1);
+ // We construct four separate planes, and evaluate which one includes all interior points with least overlap
+ final SidedPlane candidate1 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, prevSegment.URHC, currentSegment.ULHC, currentSegment.LLHC);
+ final SidedPlane candidate2 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, currentSegment.ULHC, currentSegment.LLHC, prevSegment.LRHC);
+ final SidedPlane candidate3 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, currentSegment.LLHC, prevSegment.LRHC, prevSegment.URHC);
+ final SidedPlane candidate4 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, prevSegment.LRHC, prevSegment.URHC, currentSegment.ULHC);
+
+ if (candidate1 == null && candidate2 == null && candidate3 == null && candidate4 == null) {
+ // The planes are identical. We wouldn't need a circle at all except for the possibility of
+ // backing up, which is hard to detect here.
+ final SegmentEndpoint midEndpoint = new SegmentEndpoint(currentSegment.start,
+ prevSegment.endCutoffPlane, currentSegment.startCutoffPlane, currentSegment.ULHC, currentSegment.LLHC);
+ //don't need a circle at all. Special constructor...
+ endPoints.add(midEndpoint);
+ } else {
+ endPoints.add(new SegmentEndpoint(currentSegment.start,
+ prevSegment.endCutoffPlane, currentSegment.startCutoffPlane,
+ prevSegment.URHC, prevSegment.LRHC,
+ currentSegment.ULHC, currentSegment.LLHC,
+ candidate1, candidate2, candidate3, candidate4));
+ }
+ }
+ // Do final endpoint
+ final PathSegment lastSegment = segments.get(segments.size()-1);
+ endPoints.add(new SegmentEndpoint(lastSegment.end,
+ lastSegment.endCutoffPlane, lastSegment.URHC, lastSegment.LRHC));
+
+ }
+
+ @Override
+ 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(planetModel, distanceStyle, x,y,z);
+ if (distance != Double.MAX_VALUE)
+ return currentDistance + distance;
+ currentDistance += segment.fullPathDistance(distanceStyle);
+ }
+
+ int segmentIndex = 0;
+ currentDistance = 0.0;
+ for (SegmentEndpoint endpoint : endPoints) {
+ double distance = endpoint.pathDistance(distanceStyle, x, y, z);
+ if (distance != Double.MAX_VALUE)
+ return currentDistance + distance;
+ if (segmentIndex < segments.size())
+ currentDistance += segments.get(segmentIndex++).fullPathDistance(distanceStyle);
+ }
+
+ return Double.MAX_VALUE;
+ }
+
+ @Override
+ 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;
+ }
+ return minDistance;
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ for (SegmentEndpoint pathPoint : endPoints) {
+ if (pathPoint.isWithin(x, y, z))
+ return true;
+ }
+ for (PathSegment pathSegment : segments) {
+ if (pathSegment.isWithin(x, y, z))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) {
+ // We look for an intersection with any of the exterior edges of the path.
+ // We also have to look for intersections with the cones described by the endpoints.
+ // Return "true" if any such intersections are found.
+
+ // For plane intersections, the basic idea is to come up with an equation of the line that is
+ // the intersection (if any). Then, find the intersections with the unit sphere (if any). If
+ // any of the intersection points are within the bounds, then we've detected an intersection.
+ // Well, sort of. We can detect intersections also due to overlap of segments with each other.
+ // But that's an edge case and we won't be optimizing for it.
+ //System.err.println(" Looking for intersection of plane "+plane+" with path "+this);
+ for (final SegmentEndpoint pathPoint : endPoints) {
+ if (pathPoint.intersects(planetModel, plane, notablePoints, bounds)) {
+ return true;
+ }
+ }
+
+ for (final PathSegment pathSegment : segments) {
+ if (pathSegment.intersects(planetModel, plane, notablePoints, bounds)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ // For building bounds, order matters. We want to traverse
+ // never more than 180 degrees longitude at a pop or we risk having the
+ // bounds object get itself inverted. So do the edges first.
+ for (PathSegment pathSegment : segments) {
+ pathSegment.getBounds(planetModel, bounds);
+ }
+ for (SegmentEndpoint pathPoint : endPoints) {
+ pathPoint.getBounds(planetModel, bounds);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoStandardPath))
+ return false;
+ GeoStandardPath p = (GeoStandardPath) o;
+ if (!super.equals(p))
+ return false;
+ if (cutoffAngle != p.cutoffAngle)
+ return false;
+ return points.equals(p.points);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ long temp = Double.doubleToLongBits(cutoffAngle);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ result = 31 * result + points.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoStandardPath: {planetmodel=" + planetModel+", width=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + "), points={" + points + "}}";
+ }
+
+ /**
+ * This is precalculated data for segment endpoint.
+ * Note well: This is not necessarily a circle. There are four cases:
+ * (1) The path consists of a single endpoint. In this case, we build a simple circle with the proper cutoff offset.
+ * (2) This is the end of a path. The circle plane must be constructed to go through two supplied points and be perpendicular to a connecting plane.
+ * (2.5) Intersection, but the path on both sides is linear. We generate a circle, but we use the cutoff planes to limit its influence in the straight line case.
+ * (3) This is an intersection in a path. We are supplied FOUR planes. If there are intersections within bounds for both upper and lower, then
+ * we generate no circle at all. If there is one intersection only, then we generate a plane that includes that intersection, as well as the remaining
+ * cutoff plane/edge plane points.
+ */
+ public static class SegmentEndpoint {
+ /** The center point of the endpoint */
+ public final GeoPoint point;
+ /** A plane describing the circle */
+ public final SidedPlane circlePlane;
+ /** Pertinent cutoff planes from adjoining segments */
+ public final Membership[] cutoffPlanes;
+ /** Notable points for this segment endpoint */
+ public final GeoPoint[] notablePoints;
+ /** No notable points from the circle itself */
+ public final static GeoPoint[] circlePoints = new GeoPoint[0];
+ /** Null membership */
+ public final static Membership[] NO_MEMBERSHIP = new Membership[0];
+
+ /** Base case. Does nothing at all.
+ */
+ public SegmentEndpoint(final GeoPoint point) {
+ this.point = point;
+ this.circlePlane = null;
+ this.cutoffPlanes = null;
+ this.notablePoints = null;
+ }
+
+ /** Constructor for case (1).
+ * Generate a simple circle cutoff plane.
+ *@param point is the center point.
+ *@param upperPoint is a point that must be on the circle plane.
+ *@param lowerPoint is another point that must be on the circle plane.
+ */
+ public SegmentEndpoint(final GeoPoint point, final Plane normalPlane, final GeoPoint upperPoint, final GeoPoint lowerPoint) {
+ this.point = point;
+ // Construct a sided plane that goes through the two points and whose normal is in the normalPlane.
+ this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, normalPlane, upperPoint, lowerPoint);
+ this.cutoffPlanes = NO_MEMBERSHIP;
+ this.notablePoints = circlePoints;
+ }
+
+ /** Constructor for case (2).
+ * Generate an endpoint, given a single cutoff plane plus upper and lower edge points.
+ *@param point is the center point.
+ *@param cutoffPlane is the plane from the adjoining path segment marking the boundary between this endpoint and that segment.
+ *@param topEdgePoint is a point on the cutoffPlane that should be also on the circle plane.
+ *@param bottomEdgePoint is another point on the cutoffPlane that should be also on the circle plane.
+ */
+ public SegmentEndpoint(final GeoPoint point,
+ final SidedPlane cutoffPlane, final GeoPoint topEdgePoint, final GeoPoint bottomEdgePoint) {
+ this.point = point;
+ this.cutoffPlanes = new Membership[]{new SidedPlane(cutoffPlane)};
+ this.notablePoints = new GeoPoint[]{topEdgePoint, bottomEdgePoint};
+ // 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.
+ this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, cutoffPlane, topEdgePoint, bottomEdgePoint);
+ }
+
+ /** Constructor for case (2.5).
+ * Generate an endpoint, given two cutoff planes plus upper and lower edge points.
+ *@param point is the center.
+ *@param cutoffPlane1 is one adjoining path segment cutoff plane.
+ *@param cutoffPlane2 is another adjoining path segment cutoff plane.
+ *@param topEdgePoint is a point on the cutoffPlane that should be also on the circle plane.
+ *@param bottomEdgePoint is another point on the cutoffPlane that should be also on the circle plane.
+ */
+ public SegmentEndpoint(final GeoPoint point,
+ final SidedPlane cutoffPlane1, final SidedPlane cutoffPlane2, final GeoPoint topEdgePoint, final GeoPoint bottomEdgePoint) {
+ this.point = point;
+ this.cutoffPlanes = new Membership[]{new SidedPlane(cutoffPlane1), new SidedPlane(cutoffPlane2)};
+ this.notablePoints = new GeoPoint[]{topEdgePoint, bottomEdgePoint};
+ // 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.
+ this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, cutoffPlane1, topEdgePoint, bottomEdgePoint);
+ }
+
+ /** Constructor for case (3).
+ * Generate an endpoint for an intersection, given four points.
+ *@param point is the center.
+ *@param prevCutoffPlane is the previous adjoining segment cutoff plane.
+ *@param nextCutoffPlane is the next path segment cutoff plane.
+ *@param notCand2Point is a point NOT on candidate2.
+ *@param notCand1Point is a point NOT on candidate1.
+ *@param notCand3Point is a point NOT on candidate3.
+ *@param notCand4Point is a point NOT on candidate4.
+ *@param candidate1 one of four candidate circle planes.
+ *@param candidate2 one of four candidate circle planes.
+ *@param candidate3 one of four candidate circle planes.
+ *@param candidate4 one of four candidate circle planes.
+ */
+ public SegmentEndpoint(final GeoPoint point,
+ final SidedPlane prevCutoffPlane, final SidedPlane nextCutoffPlane,
+ final GeoPoint notCand2Point, final GeoPoint notCand1Point,
+ final GeoPoint notCand3Point, final GeoPoint notCand4Point,
+ final SidedPlane candidate1, final SidedPlane candidate2, final SidedPlane candidate3, final SidedPlane candidate4) {
+ // Note: What we really need is a single plane that goes through all four points.
+ // Since that's not possible in the ellipsoid case (because three points determine a plane, not four), we
+ // need an approximation that at least creates a boundary that has no interruptions.
+ // There are three obvious choices for the third point: either (a) one of the two remaining points, or (b) the top or bottom edge
+ // intersection point. (a) has no guarantee of continuity, while (b) is capable of producing something very far from a circle if
+ // the angle between segments is acute.
+ // The solution is to look for the side (top or bottom) that has an intersection within the shape. We use the two points from
+ // the opposite side to determine the plane, AND we pick the third to be either of the two points on the intersecting side
+ // PROVIDED that the other point is within the final circle we come up with.
+ this.point = point;
+
+ // We construct four separate planes, and evaluate which one includes all interior points with least overlap
+ // (Constructed beforehand because we need them for degeneracy check)
+
+ final boolean cand1IsOtherWithin = candidate1!=null?candidate1.isWithin(notCand1Point):false;
+ final boolean cand2IsOtherWithin = candidate2!=null?candidate2.isWithin(notCand2Point):false;
+ final boolean cand3IsOtherWithin = candidate3!=null?candidate3.isWithin(notCand3Point):false;
+ final boolean cand4IsOtherWithin = candidate4!=null?candidate4.isWithin(notCand4Point):false;
+
+ if (cand1IsOtherWithin && cand2IsOtherWithin && cand3IsOtherWithin && cand4IsOtherWithin) {
+ // The only way we should see both within is if all four points are coplanar. In that case, we default to the simplest treatment.
+ this.circlePlane = candidate1; // doesn't matter which
+ this.notablePoints = new GeoPoint[]{notCand2Point, notCand3Point, notCand1Point, notCand4Point};
+ this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane), new SidedPlane(nextCutoffPlane)};
+ } else if (cand1IsOtherWithin) {
+ // Use candidate1, and DON'T include prevCutoffPlane in the cutoff planes list
+ this.circlePlane = candidate1;
+ this.notablePoints = new GeoPoint[]{notCand2Point, notCand3Point, notCand4Point};
+ this.cutoffPlanes = new Membership[]{new SidedPlane(nextCutoffPlane)};
+ } else if (cand2IsOtherWithin) {
+ // Use candidate2
+ this.circlePlane = candidate2;
+ this.notablePoints = new GeoPoint[]{notCand3Point, notCand4Point, notCand1Point};
+ this.cutoffPlanes = new Membership[]{new SidedPlane(nextCutoffPlane)};
+ } else if (cand3IsOtherWithin) {
+ this.circlePlane = candidate3;
+ this.notablePoints = new GeoPoint[]{notCand4Point, notCand1Point, notCand2Point};
+ this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane)};
+ } else if (cand4IsOtherWithin) {
+ this.circlePlane = candidate4;
+ this.notablePoints = new GeoPoint[]{notCand1Point, notCand2Point, notCand3Point};
+ this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane)};
+ } else {
+ // dunno what happened
+ throw new RuntimeException("Couldn't come up with a plane through three points that included the fourth");
+ }
+ }
+
+ /** Check if point is within this endpoint.
+ *@param point is the point.
+ *@return true of within.
+ */
+ public boolean isWithin(final Vector point) {
+ if (circlePlane == null)
+ return false;
+ if (!circlePlane.isWithin(point))
+ return false;
+ for (final Membership m : cutoffPlanes) {
+ if (!m.isWithin(point)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** Check if point is within this endpoint.
+ *@param x is the point x.
+ *@param y is the point y.
+ *@param z is the point z.
+ *@return true of within.
+ */
+ public boolean isWithin(final double x, final double y, final double z) {
+ if (circlePlane == null)
+ return false;
+ if (!circlePlane.isWithin(x, y, z))
+ return false;
+ for (final Membership m : cutoffPlanes) {
+ if (!m.isWithin(x,y,z)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** Compute interior path 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.
+ */
+ 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 distanceStyle.computeDistance(this.point, x, y, z);
+ }
+
+ /** Compute external 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.
+ */
+ public double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ return distanceStyle.computeDistance(this.point, x, y, z);
+ }
+
+ /** 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) {
+ //System.err.println(" looking for intersection between plane "+p+" and circle "+circlePlane+" on proper side of "+cutoffPlanes+" within "+bounds);
+ if (circlePlane == null)
+ return false;
+ return circlePlane.intersects(planetModel, p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes);
+ }
+
+ /** Get the bounds for a segment endpoint.
+ *@param planetModel is the planet model.
+ *@param bounds are the bounds to be modified.
+ */
+ public void getBounds(final PlanetModel planetModel, Bounds bounds) {
+ bounds.addPoint(point);
+ if (circlePlane == null)
+ return;
+ bounds.addPlane(planetModel, circlePlane);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SegmentEndpoint))
+ return false;
+ SegmentEndpoint other = (SegmentEndpoint) o;
+ return point.equals(other.point);
+ }
+
+ @Override
+ public int hashCode() {
+ return point.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return point.toString();
+ }
+ }
+
+ /**
+ * This is the pre-calculated data for a path segment.
+ */
+ public static class PathSegment {
+ /** 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<DistanceStyle,Double>();
+ /** Normalized plane connecting the two points and going through world center */
+ public final Plane normalizedConnectingPlane;
+ /** Cutoff plane parallel to connecting plane representing one side of the path segment */
+ public final SidedPlane upperConnectingPlane;
+ /** Cutoff plane parallel to connecting plane representing the other side of the path segment */
+ public final SidedPlane lowerConnectingPlane;
+ /** Plane going through the center and start point, marking the start edge of the segment */
+ public final SidedPlane startCutoffPlane;
+ /** Plane going through the center and end point, marking the end edge of the segment */
+ public final SidedPlane endCutoffPlane;
+ /** Upper right hand corner of segment */
+ public final GeoPoint URHC;
+ /** Lower right hand corner of segment */
+ public final GeoPoint LRHC;
+ /** Upper left hand corner of segment */
+ public final GeoPoint ULHC;
+ /** Lower left hand corner of segment */
+ public final GeoPoint LLHC;
+ /** Notable points for the upper connecting plane */
+ public final GeoPoint[] upperConnectingPlanePoints;
+ /** Notable points for the lower connecting plane */
+ public final GeoPoint[] lowerConnectingPlanePoints;
+ /** Notable points for the start cutoff plane */
+ public final GeoPoint[] startCutoffPlanePoints;
+ /** Notable points for the end cutoff plane */
+ public final GeoPoint[] endCutoffPlanePoints;
+
+ /** Construct a path segment.
+ *@param planetModel is the planet model.
+ *@param start is the starting point.
+ *@param end is the ending point.
+ *@param normalizedConnectingPlane is the connecting plane.
+ *@param planeBoundingOffset is the linear offset from the connecting plane to either side.
+ */
+ public PathSegment(final PlanetModel planetModel, final GeoPoint start, final GeoPoint end,
+ final Plane normalizedConnectingPlane, final double planeBoundingOffset) {
+ this.start = start;
+ this.end = end;
+ this.normalizedConnectingPlane = normalizedConnectingPlane;
+
+ // Either start or end should be on the correct side
+ upperConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, -planeBoundingOffset);
+ lowerConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, planeBoundingOffset);
+ // Cutoff planes use opposite endpoints as correct side examples
+ startCutoffPlane = new SidedPlane(end, normalizedConnectingPlane, start);
+ endCutoffPlane = new SidedPlane(start, normalizedConnectingPlane, end);
+ final Membership[] upperSide = new Membership[]{upperConnectingPlane};
+ final Membership[] lowerSide = new Membership[]{lowerConnectingPlane};
+ final Membership[] startSide = new Membership[]{startCutoffPlane};
+ final Membership[] endSide = new Membership[]{endCutoffPlane};
+ GeoPoint[] points;
+ points = upperConnectingPlane.findIntersections(planetModel, startCutoffPlane, lowerSide, endSide);
+ if (points.length == 0) {
+ throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
+ }
+ this.ULHC = points[0];
+ points = upperConnectingPlane.findIntersections(planetModel, endCutoffPlane, lowerSide, startSide);
+ if (points.length == 0) {
+ throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
+ }
+ this.URHC = points[0];
+ points = lowerConnectingPlane.findIntersections(planetModel, startCutoffPlane, upperSide, endSide);
+ if (points.length == 0) {
+ throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
+ }
+ this.LLHC = points[0];
+ points = lowerConnectingPlane.findIntersections(planetModel, endCutoffPlane, upperSide, startSide);
+ if (points.length == 0) {
+ throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
+ }
+ this.LRHC = points[0];
+ upperConnectingPlanePoints = new GeoPoint[]{ULHC, URHC};
+ lowerConnectingPlanePoints = new GeoPoint[]{LLHC, LRHC};
+ startCutoffPlanePoints = new GeoPoint[]{ULHC, LLHC};
+ endCutoffPlanePoints = new GeoPoint[]{URHC, LRHC};
+ }
+
+ /** Compute the full distance along this path segment.
+ *@param distanceStyle is the distance style.
+ *@return the distance metric.
+ */
+ 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();
+ }
+ }
+
+ /** Check if point is within this segment.
+ *@param point is the point.
+ *@return true of within.
+ */
+ public boolean isWithin(final Vector point) {
+ return startCutoffPlane.isWithin(point) &&
+ endCutoffPlane.isWithin(point) &&
+ upperConnectingPlane.isWithin(point) &&
+ lowerConnectingPlane.isWithin(point);
+ }
+
+ /** Check if point is within this segment.
+ *@param x is the point x.
+ *@param y is the point y.
+ *@param z is the point z.
+ *@return true of within.
+ */
+ public boolean isWithin(final double x, final double y, final double z) {
+ return startCutoffPlane.isWithin(x, y, z) &&
+ endCutoffPlane.isWithin(x, y, z) &&
+ upperConnectingPlane.isWithin(x, y, z) &&
+ lowerConnectingPlane.isWithin(x, y, z);
+ }
+
+ /** Compute interior path 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.
+ */
+ 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;
+
+ // (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 * 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);
+ }
+
+ /** Compute external 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.
+ */
+ 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)));
+ }
+
+ /** 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) {
+ return upperConnectingPlane.intersects(planetModel, p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
+ lowerConnectingPlane.intersects(planetModel, p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
+ }
+
+ /** Get the bounds for a segment endpoint.
+ *@param planetModel is the planet model.
+ *@param bounds are the bounds to be modified.
+ */
+ public void getBounds(final PlanetModel planetModel, Bounds bounds) {
+ // We need to do all bounding planes as well as corner points
+ bounds.addPoint(start).addPoint(end).addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
+ bounds.addPlane(planetModel, upperConnectingPlane, lowerConnectingPlane, startCutoffPlane, endCutoffPlane);
+ bounds.addPlane(planetModel, lowerConnectingPlane, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
+ bounds.addPlane(planetModel, startCutoffPlane, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
+ bounds.addPlane(planetModel, endCutoffPlane, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
index 398458c..3caf039 100644
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
@@ -40,7 +40,7 @@ import org.apache.lucene.spatial3d.geom.GeoArea;
import org.apache.lucene.spatial3d.geom.GeoAreaFactory;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
-import org.apache.lucene.spatial3d.geom.GeoPath;
+import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoShape;
@@ -625,13 +625,12 @@ public class TestGeo3DPoint extends LuceneTestCase {
// Paths
final int pointCount = random().nextInt(5) + 1;
final double width = toRadians(random().nextInt(89)+1);
+ final GeoPoint[] points = new GeoPoint[pointCount];
+ for (int i = 0; i < pointCount; i++) {
+ points[i] = new GeoPoint(planetModel, toRadians(randomLat()), toRadians(randomLon()));
+ }
try {
- final GeoPath path = new GeoPath(planetModel, width);
- for (int i = 0; i < pointCount; i++) {
- path.addPoint(toRadians(randomLat()), toRadians(randomLon()));
- }
- path.done();
- return path;
+ return GeoPathFactory.makeGeoPath(planetModel, width, points);
} catch (IllegalArgumentException e) {
// This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where
// the exception is thrown incorrectly, we aren't going to be able to do that in this random test.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
index 3746069..e68e3f4 100755
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
@@ -28,9 +28,9 @@ public class GeoPathTest {
@Test
public void testPathDistance() {
// Start with a really simple case
- GeoPath p;
+ GeoStandardPath p;
GeoPoint gp;
- p = new GeoPath(PlanetModel.SPHERE, 0.1);
+ p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(0.0, 0.0);
p.addPoint(0.0, 0.1);
p.addPoint(0.0, 0.2);
@@ -49,7 +49,7 @@ public class GeoPathTest {
assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
// Compute path distances now
- p = new GeoPath(PlanetModel.SPHERE, 0.1);
+ p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(0.0, 0.0);
p.addPoint(0.0, 0.1);
p.addPoint(0.0, 0.2);
@@ -60,7 +60,7 @@ public class GeoPathTest {
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);
+ p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(-Math.PI * 0.25, -0.5);
p.addPoint(Math.PI * 0.25, -0.5);
p.done();
@@ -77,9 +77,9 @@ public class GeoPathTest {
@Test
public void testPathPointWithin() {
// Tests whether we can properly detect whether a point is within a path or not
- GeoPath p;
+ GeoStandardPath p;
GeoPoint gp;
- p = new GeoPath(PlanetModel.SPHERE, 0.1);
+ p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
// Build a diagonal path crossing the equator
p.addPoint(-0.2, -0.2);
p.addPoint(0.2, 0.2);
@@ -101,7 +101,7 @@ public class GeoPathTest {
gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
assertFalse(p.isWithin(gp));
// Repeat the test, but across the terminator
- p = new GeoPath(PlanetModel.SPHERE, 0.1);
+ p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
// Build a diagonal path crossing the equator
p.addPoint(-0.2, Math.PI - 0.2);
p.addPoint(0.2, -Math.PI + 0.2);
@@ -128,8 +128,8 @@ public class GeoPathTest {
@Test
public void testGetRelationship() {
GeoArea rect;
- GeoPath p;
- GeoPath c;
+ GeoStandardPath p;
+ GeoStandardPath c;
GeoPoint point;
GeoPoint pointApprox;
int relationship;
@@ -137,7 +137,7 @@ public class GeoPathTest {
PlanetModel planetModel;
planetModel = new PlanetModel(1.151145876105594, 0.8488541238944061);
- c = new GeoPath(planetModel, 0.008726646259971648);
+ c = new GeoStandardPath(planetModel, 0.008726646259971648);
c.addPoint(-0.6925658899376476, 0.6316613927914589);
c.addPoint(0.27828548161836364, 0.6785795524104564);
c.done();
@@ -148,7 +148,7 @@ public class GeoPathTest {
// Start by testing the basic kinds of relationship, increasing in order of difficulty.
- p = new GeoPath(PlanetModel.SPHERE, 0.1);
+ p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(-0.3, -0.3);
p.addPoint(0.3, 0.3);
p.done();
@@ -179,7 +179,7 @@ public class GeoPathTest {
@Test
public void testPathBounds() {
- GeoPath c;
+ GeoStandardPath c;
LatLonBounds b;
XYZBounds xyzb;
GeoPoint point;
@@ -188,7 +188,7 @@ public class GeoPathTest {
PlanetModel planetModel;
planetModel = new PlanetModel(0.751521665790406,1.248478334209594);
- c = new GeoPath(planetModel, 0.7504915783575618);
+ c = new GeoStandardPath(planetModel, 0.7504915783575618);
c.addPoint(0.10869761172400265, 0.08895880215465272);
c.addPoint(0.22467878641991612, 0.10972973084229565);
c.addPoint(-0.7398772468744732, -0.4465812941383364);
@@ -202,10 +202,10 @@ public class GeoPathTest {
relationship = area.getRelationship(c);
assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
assertTrue(area.isWithin(point));
- // No longer true due to fixed GeoPath waypoints.
+ // No longer true due to fixed GeoStandardPath waypoints.
//assertTrue(c.isWithin(point));
- c = new GeoPath(PlanetModel.WGS84, 0.6894050545377601);
+ c = new GeoStandardPath(PlanetModel.WGS84, 0.6894050545377601);
c.addPoint(-0.0788176065762948, 0.9431251741731624);
c.addPoint(0.510387871458147, 0.5327078872484678);
c.addPoint(-0.5624521609859962, 1.5398841746888388);
@@ -224,7 +224,7 @@ public class GeoPathTest {
assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
assertTrue(area.isWithin(point));
- c = new GeoPath(PlanetModel.WGS84, 0.7766715171374766);
+ c = new GeoStandardPath(PlanetModel.WGS84, 0.7766715171374766);
c.addPoint(-0.2751718361148076, -0.7786721269011477);
c.addPoint(0.5728375851539309, -1.2700115736820465);
c.done();
@@ -240,7 +240,7 @@ public class GeoPathTest {
assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
assertTrue(area.isWithin(point));
- c = new GeoPath(PlanetModel.SPHERE, 0.1);
+ c = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
c.addPoint(-0.3, -0.3);
c.addPoint(0.3, 0.3);
c.done();
@@ -260,7 +260,7 @@ public class GeoPathTest {
@Test
public void testCoLinear() {
// p1: (12,-90), p2: (11, -55), (129, -90)
- GeoPath p = new GeoPath(PlanetModel.SPHERE, 0.1);
+ GeoStandardPath p = new GeoStandardPath(PlanetModel.SPHERE, 0.1);
p.addPoint(toRadians(-90), toRadians(12));//south pole
p.addPoint(toRadians(-55), toRadians(11));
p.addPoint(toRadians(-90), toRadians(129));//south pole again
[2/2] lucene-solr:branch_6x: LUCENE-7176: Hide GeoPath implementation
in a factory/interface.
Posted by kw...@apache.org.
LUCENE-7176: Hide GeoPath implementation in a factory/interface.
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/784e3e38
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/784e3e38
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/784e3e38
Branch: refs/heads/branch_6x
Commit: 784e3e38631dce6a6f863390ca43e17836de86eb
Parents: 56a4b6a
Author: Karl Wright <Da...@gmail.com>
Authored: Tue Apr 5 03:41:48 2016 -0400
Committer: Karl Wright <Da...@gmail.com>
Committed: Tue Apr 5 06:10:48 2016 -0400
----------------------------------------------------------------------
.../lucene/spatial/spatial4j/Geo3dRptTest.java | 26 +-
.../Geo3dShapeRectRelationTestCase.java | 15 +-
.../Geo3dShapeWGS84ModelRectRelationTest.java | 15 +-
.../org/apache/lucene/spatial3d/Geo3DPoint.java | 4 +-
.../lucene/spatial3d/geom/GeoBaseCircle.java | 2 +-
.../lucene/spatial3d/geom/GeoBasePath.java | 34 +
.../apache/lucene/spatial3d/geom/GeoPath.java | 776 +-----------------
.../lucene/spatial3d/geom/GeoPathFactory.java | 39 +
.../lucene/spatial3d/geom/GeoStandardPath.java | 797 +++++++++++++++++++
.../apache/lucene/spatial3d/TestGeo3DPoint.java | 13 +-
.../lucene/spatial3d/geom/GeoPathTest.java | 36 +-
11 files changed, 928 insertions(+), 829 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
index aacea2c..004139e 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java
@@ -29,7 +29,7 @@ import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
-import org.apache.lucene.spatial3d.geom.GeoPath;
+import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoShape;
@@ -95,12 +95,12 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
points.add(new GeoPoint(PlanetModel.SPHERE, -57 * DEGREES_TO_RADIANS, 146 * DEGREES_TO_RADIANS));
points.add(new GeoPoint(PlanetModel.SPHERE, 14 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS));
points.add(new GeoPoint(PlanetModel.SPHERE, -15 * DEGREES_TO_RADIANS, 153 * DEGREES_TO_RADIANS));
- final GeoPath path = new GeoPath(PlanetModel.SPHERE, 29 * DEGREES_TO_RADIANS);
- path.addPoint(55.0 * DEGREES_TO_RADIANS, -26.0 * DEGREES_TO_RADIANS);
- path.addPoint(-90.0 * DEGREES_TO_RADIANS, 0.0);
- path.addPoint(54.0 * DEGREES_TO_RADIANS, 165.0 * DEGREES_TO_RADIANS);
- path.addPoint(-90.0 * DEGREES_TO_RADIANS, 0.0);
- path.done();
+ final GeoPoint[] pathPoints = new GeoPoint[] {
+ new GeoPoint(PlanetModel.SPHERE, 55.0 * DEGREES_TO_RADIANS, -26.0 * DEGREES_TO_RADIANS),
+ new GeoPoint(PlanetModel.SPHERE, -90.0 * DEGREES_TO_RADIANS, 0.0),
+ new GeoPoint(PlanetModel.SPHERE, 54.0 * DEGREES_TO_RADIANS, 165.0 * DEGREES_TO_RADIANS),
+ new GeoPoint(PlanetModel.SPHERE, -90.0 * DEGREES_TO_RADIANS, 0.0)};
+ final GeoShape path = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, 29 * DEGREES_TO_RADIANS, pathPoints);
final Shape shape = new Geo3dShape(path,ctx);
final Rectangle rect = ctx.makeRectangle(131, 143, 39, 54);
testOperation(rect,SpatialOperation.Intersects,shape,true);
@@ -199,14 +199,14 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
// Paths
final int pointCount = random().nextInt(5) + 1;
final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS;
+ final GeoPoint[] points = new GeoPoint[pointCount];
while (true) {
+ for (int i = 0; i < pointCount; i++) {
+ final Point nextPoint = randomPoint();
+ points[i] = new GeoPoint(PlanetModel.SPHERE, nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
+ }
try {
- final GeoPath path = new GeoPath(PlanetModel.SPHERE, width);
- for (int i = 0; i < pointCount; i++) {
- final Point nextPoint = randomPoint();
- path.addPoint(nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
- }
- path.done();
+ final GeoShape path = GeoPathFactory.makeGeoPath(PlanetModel.SPHERE, width, points);
return new Geo3dShape(path, ctx);
} catch (IllegalArgumentException e) {
// This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
index 2c41f79..f8e51d1 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTestCase.java
@@ -30,7 +30,7 @@ import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoCircle;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
-import org.apache.lucene.spatial3d.geom.GeoPath;
+import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
import org.apache.lucene.spatial3d.geom.GeoShape;
@@ -221,14 +221,15 @@ public abstract class Geo3dShapeRectRelationTestCase extends RandomizedShapeTest
final Circle pointZone = ctx.makeCircle(centerPoint, maxDistance);
final int pointCount = random().nextInt(5) + 1;
final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS;
+ final GeoPoint[] points = new GeoPoint[pointCount];
while (true) {
+ for (int i = 0; i < pointCount; i++) {
+ final Point nextPoint = randomPointIn(pointZone);
+ points[i] = new GeoPoint(planetModel, nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
+ }
+
try {
- final GeoPath path = new GeoPath(planetModel, width);
- for (int i = 0; i < pointCount; i++) {
- final Point nextPoint = randomPointIn(pointZone);
- path.addPoint(nextPoint.getY() * DEGREES_TO_RADIANS, nextPoint.getX() * DEGREES_TO_RADIANS);
- }
- path.done();
+ final GeoShape path = GeoPathFactory.makeGeoPath(planetModel, width, points);
return new Geo3dShape(planetModel, path, ctx);
} catch (IllegalArgumentException e) {
// This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
index 00a0903..43b88ca 100644
--- a/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
+++ b/lucene/spatial-extras/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeWGS84ModelRectRelationTest.java
@@ -21,6 +21,7 @@ import org.apache.lucene.spatial3d.geom.GeoBBox;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
import org.apache.lucene.spatial3d.geom.GeoCircle;
+import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoPath;
import org.apache.lucene.spatial3d.geom.GeoPoint;
import org.apache.lucene.spatial3d.geom.PlanetModel;
@@ -36,9 +37,9 @@ public class Geo3dShapeWGS84ModelRectRelationTest extends Geo3dShapeRectRelation
public void testFailure1() {
final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(planetModel, 90 * RADIANS_PER_DEGREE, 74 * RADIANS_PER_DEGREE,
40 * RADIANS_PER_DEGREE, 60 * RADIANS_PER_DEGREE);
- final GeoPath path = new GeoPath(planetModel, 4 * RADIANS_PER_DEGREE);
- path.addPoint(84.4987594274 * RADIANS_PER_DEGREE, -22.8345484402 * RADIANS_PER_DEGREE);
- path.done();
+ final GeoPoint[] pathPoints = new GeoPoint[] {
+ new GeoPoint(planetModel, 84.4987594274 * RADIANS_PER_DEGREE, -22.8345484402 * RADIANS_PER_DEGREE)};
+ final GeoPath path = GeoPathFactory.makeGeoPath(planetModel, 4 * RADIANS_PER_DEGREE, pathPoints);
assertTrue(GeoArea.DISJOINT == rect.getRelationship(path));
// This is what the test failure claimed...
//assertTrue(GeoArea.CONTAINS == rect.getRelationship(path));
@@ -75,10 +76,10 @@ public class Geo3dShapeWGS84ModelRectRelationTest extends Geo3dShapeRectRelation
*/
final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(planetModel, 16 * RADIANS_PER_DEGREE, 16 * RADIANS_PER_DEGREE, 4 * RADIANS_PER_DEGREE, 36 * RADIANS_PER_DEGREE);
final GeoPoint pt = new GeoPoint(planetModel, 16 * RADIANS_PER_DEGREE, 23.81626064835212 * RADIANS_PER_DEGREE);
- final GeoPath path = new GeoPath(planetModel, 88 * RADIANS_PER_DEGREE);
- path.addPoint(46.6369060853 * RADIANS_PER_DEGREE, -79.8452213228 * RADIANS_PER_DEGREE);
- path.addPoint(54.9779334519 * RADIANS_PER_DEGREE, 132.029177424 * RADIANS_PER_DEGREE);
- path.done();
+ final GeoPoint[] pathPoints = new GeoPoint[]{
+ new GeoPoint(planetModel, 46.6369060853 * RADIANS_PER_DEGREE, -79.8452213228 * RADIANS_PER_DEGREE),
+ new GeoPoint(planetModel, 54.9779334519 * RADIANS_PER_DEGREE, 132.029177424 * RADIANS_PER_DEGREE)};
+ final GeoPath path = GeoPathFactory.makeGeoPath(planetModel, 88 * RADIANS_PER_DEGREE, pathPoints);
System.out.println("rect=" + rect);
// Rectangle is within path (this is wrong; it's on the other side. Should be OVERLAPS)
assertTrue(GeoArea.OVERLAPS == rect.getRelationship(path));
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
index 944ce39..be8990f 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/Geo3DPoint.java
@@ -31,7 +31,7 @@ import org.apache.lucene.spatial3d.geom.PlanetModel;
import org.apache.lucene.spatial3d.geom.GeoCircleFactory;
import org.apache.lucene.spatial3d.geom.GeoBBoxFactory;
import org.apache.lucene.spatial3d.geom.GeoPolygonFactory;
-import org.apache.lucene.spatial3d.geom.GeoPath;
+import org.apache.lucene.spatial3d.geom.GeoPathFactory;
import org.apache.lucene.spatial3d.geom.GeoCompositePolygon;
import org.apache.lucene.spatial3d.geom.GeoPolygon;
import org.apache.lucene.search.Query;
@@ -172,7 +172,7 @@ public final class Geo3DPoint extends Field {
GeoUtils.checkLongitude(longitude);
points[i] = new GeoPoint(PlanetModel.WGS84, fromDegrees(latitude), fromDegrees(longitude));
}
- final GeoShape shape = new GeoPath(PlanetModel.WGS84, fromMeters(pathWidthMeters), points);
+ final GeoShape shape = GeoPathFactory.makeGeoPath(PlanetModel.WGS84, fromMeters(pathWidthMeters), points);
return newShapeQuery(field, shape);
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCircle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCircle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCircle.java
index b34313a..0599c91 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCircle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseCircle.java
@@ -19,7 +19,7 @@ package org.apache.lucene.spatial3d.geom;
/**
* GeoCircles have all the characteristics of GeoBaseDistanceShapes, plus GeoSizeable.
*
- * @lucene.experimental
+ * @lucene.internal
*/
abstract class GeoBaseCircle extends GeoBaseDistanceShape implements GeoCircle {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePath.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePath.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePath.java
new file mode 100644
index 0000000..c726b3a
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePath.java
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * GeoPaths have all the characteristics of GeoBaseDistanceShapes.
+ *
+ * @lucene.internal
+ */
+abstract class GeoBasePath extends GeoBaseDistanceShape implements GeoPath {
+
+ /** Constructor.
+ *@param planetModel is the planet model to use.
+ */
+ public GeoBasePath(final PlanetModel planetModel) {
+ super(planetModel);
+ }
+
+}
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPath.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPath.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPath.java
old mode 100755
new mode 100644
index a5b8b9b..48a613b
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPath.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPath.java
@@ -16,782 +16,10 @@
*/
package org.apache.lucene.spatial3d.geom;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
/**
- * 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.
+ * Interface describing a path.
*
* @lucene.experimental
*/
-public class GeoPath extends GeoBaseDistanceShape {
- /** The cutoff angle (width) */
- protected final double cutoffAngle;
-
- /** Sine of cutoff angle */
- protected final double sinAngle;
- /** Cosine of cutoff angle */
- protected final double cosAngle;
-
- /** The original list of path points */
- protected final List<GeoPoint> points = new ArrayList<GeoPoint>();
-
- /** A list of SegmentEndpoints */
- protected List<SegmentEndpoint> endPoints;
- /** A list of PathSegments */
- protected List<PathSegment> segments;
-
- /** A point on the edge */
- protected GeoPoint[] edgePoints;
-
- /** Set to true if path has been completely constructed */
- protected boolean isDone = false;
-
- /** Constructor.
- *@param planetModel is the planet model.
- *@param maxCutoffAngle is the width of the path, measured as an angle.
- *@param pathPoints are the points in the path.
- */
- public GeoPath(final PlanetModel planetModel, final double maxCutoffAngle, final GeoPoint[] pathPoints) {
- this(planetModel, maxCutoffAngle);
- Collections.addAll(points, pathPoints);
- done();
- }
-
- /** Piece-wise constructor. Use in conjunction with addPoint() and done().
- *@param planetModel is the planet model.
- *@param maxCutoffAngle is the width of the path, measured as an angle.
- */
- public GeoPath(final PlanetModel planetModel, final double maxCutoffAngle) {
- super(planetModel);
- if (maxCutoffAngle <= 0.0 || maxCutoffAngle > Math.PI * 0.5)
- throw new IllegalArgumentException("Cutoff angle out of bounds");
- this.cutoffAngle = maxCutoffAngle;
- this.cosAngle = Math.cos(maxCutoffAngle);
- this.sinAngle = Math.sin(maxCutoffAngle);
- }
-
- /** Add a point to the path.
- *@param lat is the latitude of the point.
- *@param lon is the longitude of the point.
- */
- public void addPoint(final double lat, final double lon) {
- if (isDone)
- throw new IllegalStateException("Can't call addPoint() if done() already called");
- points.add(new GeoPoint(planetModel, lat, lon));
- }
-
- /** Complete the path.
- */
- public void done() {
- if (isDone)
- throw new IllegalStateException("Can't call done() twice");
- if (points.size() == 0)
- throw new IllegalArgumentException("Path must have at least one point");
- isDone = true;
-
- endPoints = new ArrayList<>(points.size());
- 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();
-
- // First, build all segments. We'll then go back and build corresponding segment endpoints.
- GeoPoint lastPoint = null;
- for (final GeoPoint end : points) {
- if (lastPoint != null) {
- final Plane normalizedConnectingPlane = new Plane(lastPoint, end);
- if (normalizedConnectingPlane == null) {
- continue;
- }
- segments.add(new PathSegment(planetModel, lastPoint, end, normalizedConnectingPlane, cutoffOffset));
- }
- lastPoint = end;
- }
-
- if (segments.size() == 0) {
- // Simple circle
- double lat = points.get(0).getLatitude();
- double lon = points.get(0).getLongitude();
- // Compute two points on the circle, with the right angle from the center. We'll use these
- // to obtain the perpendicular plane to the circle.
- double upperLat = lat + cutoffAngle;
- double upperLon = lon;
- if (upperLat > Math.PI * 0.5) {
- upperLon += Math.PI;
- if (upperLon > Math.PI)
- upperLon -= 2.0 * Math.PI;
- upperLat = Math.PI - upperLat;
- }
- double lowerLat = lat - cutoffAngle;
- double lowerLon = lon;
- if (lowerLat < -Math.PI * 0.5) {
- lowerLon += Math.PI;
- if (lowerLon > Math.PI)
- lowerLon -= 2.0 * Math.PI;
- lowerLat = -Math.PI - lowerLat;
- }
- final GeoPoint upperPoint = new GeoPoint(planetModel, upperLat, upperLon);
- final GeoPoint lowerPoint = new GeoPoint(planetModel, lowerLat, lowerLon);
- final GeoPoint point = points.get(0);
-
- // Construct normal plane
- final Plane normalPlane = Plane.constructNormalizedZPlane(upperPoint, lowerPoint, point);
-
- final SegmentEndpoint onlyEndpoint = new SegmentEndpoint(point, normalPlane, upperPoint, lowerPoint);
- endPoints.add(onlyEndpoint);
- this.edgePoints = new GeoPoint[]{onlyEndpoint.circlePlane.getSampleIntersectionPoint(planetModel, normalPlane)};
- return;
- }
-
- // Create segment endpoints. Use an appropriate constructor for the start and end of the path.
- for (int i = 0; i < segments.size(); i++) {
- final PathSegment currentSegment = segments.get(i);
-
- if (i == 0) {
- // Starting endpoint
- final SegmentEndpoint startEndpoint = new SegmentEndpoint(currentSegment.start,
- currentSegment.startCutoffPlane, currentSegment.ULHC, currentSegment.LLHC);
- endPoints.add(startEndpoint);
- this.edgePoints = new GeoPoint[]{currentSegment.ULHC};
- continue;
- }
-
- // General intersection case
- final PathSegment prevSegment = segments.get(i-1);
- // We construct four separate planes, and evaluate which one includes all interior points with least overlap
- final SidedPlane candidate1 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, prevSegment.URHC, currentSegment.ULHC, currentSegment.LLHC);
- final SidedPlane candidate2 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, currentSegment.ULHC, currentSegment.LLHC, prevSegment.LRHC);
- final SidedPlane candidate3 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, currentSegment.LLHC, prevSegment.LRHC, prevSegment.URHC);
- final SidedPlane candidate4 = SidedPlane.constructNormalizedThreePointSidedPlane(currentSegment.start, prevSegment.LRHC, prevSegment.URHC, currentSegment.ULHC);
-
- if (candidate1 == null && candidate2 == null && candidate3 == null && candidate4 == null) {
- // The planes are identical. We wouldn't need a circle at all except for the possibility of
- // backing up, which is hard to detect here.
- final SegmentEndpoint midEndpoint = new SegmentEndpoint(currentSegment.start,
- prevSegment.endCutoffPlane, currentSegment.startCutoffPlane, currentSegment.ULHC, currentSegment.LLHC);
- //don't need a circle at all. Special constructor...
- endPoints.add(midEndpoint);
- } else {
- endPoints.add(new SegmentEndpoint(currentSegment.start,
- prevSegment.endCutoffPlane, currentSegment.startCutoffPlane,
- prevSegment.URHC, prevSegment.LRHC,
- currentSegment.ULHC, currentSegment.LLHC,
- candidate1, candidate2, candidate3, candidate4));
- }
- }
- // Do final endpoint
- final PathSegment lastSegment = segments.get(segments.size()-1);
- endPoints.add(new SegmentEndpoint(lastSegment.end,
- lastSegment.endCutoffPlane, lastSegment.URHC, lastSegment.LRHC));
-
- }
-
- @Override
- 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(planetModel, distanceStyle, x,y,z);
- if (distance != Double.MAX_VALUE)
- return currentDistance + distance;
- currentDistance += segment.fullPathDistance(distanceStyle);
- }
-
- int segmentIndex = 0;
- currentDistance = 0.0;
- for (SegmentEndpoint endpoint : endPoints) {
- double distance = endpoint.pathDistance(distanceStyle, x, y, z);
- if (distance != Double.MAX_VALUE)
- return currentDistance + distance;
- if (segmentIndex < segments.size())
- currentDistance += segments.get(segmentIndex++).fullPathDistance(distanceStyle);
- }
-
- return Double.MAX_VALUE;
- }
-
- @Override
- 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;
- }
- return minDistance;
- }
-
- @Override
- public boolean isWithin(final double x, final double y, final double z) {
- for (SegmentEndpoint pathPoint : endPoints) {
- if (pathPoint.isWithin(x, y, z))
- return true;
- }
- for (PathSegment pathSegment : segments) {
- if (pathSegment.isWithin(x, y, z))
- return true;
- }
- return false;
- }
-
- @Override
- public GeoPoint[] getEdgePoints() {
- return edgePoints;
- }
-
- @Override
- public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) {
- // We look for an intersection with any of the exterior edges of the path.
- // We also have to look for intersections with the cones described by the endpoints.
- // Return "true" if any such intersections are found.
-
- // For plane intersections, the basic idea is to come up with an equation of the line that is
- // the intersection (if any). Then, find the intersections with the unit sphere (if any). If
- // any of the intersection points are within the bounds, then we've detected an intersection.
- // Well, sort of. We can detect intersections also due to overlap of segments with each other.
- // But that's an edge case and we won't be optimizing for it.
- //System.err.println(" Looking for intersection of plane "+plane+" with path "+this);
- for (final SegmentEndpoint pathPoint : endPoints) {
- if (pathPoint.intersects(planetModel, plane, notablePoints, bounds)) {
- return true;
- }
- }
-
- for (final PathSegment pathSegment : segments) {
- if (pathSegment.intersects(planetModel, plane, notablePoints, bounds)) {
- return true;
- }
- }
-
- return false;
- }
-
- @Override
- public void getBounds(Bounds bounds) {
- super.getBounds(bounds);
- // For building bounds, order matters. We want to traverse
- // never more than 180 degrees longitude at a pop or we risk having the
- // bounds object get itself inverted. So do the edges first.
- for (PathSegment pathSegment : segments) {
- pathSegment.getBounds(planetModel, bounds);
- }
- for (SegmentEndpoint pathPoint : endPoints) {
- pathPoint.getBounds(planetModel, bounds);
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof GeoPath))
- return false;
- GeoPath p = (GeoPath) o;
- if (!super.equals(p))
- return false;
- if (cutoffAngle != p.cutoffAngle)
- return false;
- return points.equals(p.points);
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- long temp = Double.doubleToLongBits(cutoffAngle);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- result = 31 * result + points.hashCode();
- return result;
- }
-
- @Override
- public String toString() {
- return "GeoPath: {planetmodel=" + planetModel+", width=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + "), points={" + points + "}}";
- }
-
- /**
- * This is precalculated data for segment endpoint.
- * Note well: This is not necessarily a circle. There are four cases:
- * (1) The path consists of a single endpoint. In this case, we build a simple circle with the proper cutoff offset.
- * (2) This is the end of a path. The circle plane must be constructed to go through two supplied points and be perpendicular to a connecting plane.
- * (2.5) Intersection, but the path on both sides is linear. We generate a circle, but we use the cutoff planes to limit its influence in the straight line case.
- * (3) This is an intersection in a path. We are supplied FOUR planes. If there are intersections within bounds for both upper and lower, then
- * we generate no circle at all. If there is one intersection only, then we generate a plane that includes that intersection, as well as the remaining
- * cutoff plane/edge plane points.
- */
- public static class SegmentEndpoint {
- /** The center point of the endpoint */
- public final GeoPoint point;
- /** A plane describing the circle */
- public final SidedPlane circlePlane;
- /** Pertinent cutoff planes from adjoining segments */
- public final Membership[] cutoffPlanes;
- /** Notable points for this segment endpoint */
- public final GeoPoint[] notablePoints;
- /** No notable points from the circle itself */
- public final static GeoPoint[] circlePoints = new GeoPoint[0];
- /** Null membership */
- public final static Membership[] NO_MEMBERSHIP = new Membership[0];
-
- /** Base case. Does nothing at all.
- */
- public SegmentEndpoint(final GeoPoint point) {
- this.point = point;
- this.circlePlane = null;
- this.cutoffPlanes = null;
- this.notablePoints = null;
- }
-
- /** Constructor for case (1).
- * Generate a simple circle cutoff plane.
- *@param point is the center point.
- *@param upperPoint is a point that must be on the circle plane.
- *@param lowerPoint is another point that must be on the circle plane.
- */
- public SegmentEndpoint(final GeoPoint point, final Plane normalPlane, final GeoPoint upperPoint, final GeoPoint lowerPoint) {
- this.point = point;
- // Construct a sided plane that goes through the two points and whose normal is in the normalPlane.
- this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, normalPlane, upperPoint, lowerPoint);
- this.cutoffPlanes = NO_MEMBERSHIP;
- this.notablePoints = circlePoints;
- }
-
- /** Constructor for case (2).
- * Generate an endpoint, given a single cutoff plane plus upper and lower edge points.
- *@param point is the center point.
- *@param cutoffPlane is the plane from the adjoining path segment marking the boundary between this endpoint and that segment.
- *@param topEdgePoint is a point on the cutoffPlane that should be also on the circle plane.
- *@param bottomEdgePoint is another point on the cutoffPlane that should be also on the circle plane.
- */
- public SegmentEndpoint(final GeoPoint point,
- final SidedPlane cutoffPlane, final GeoPoint topEdgePoint, final GeoPoint bottomEdgePoint) {
- this.point = point;
- this.cutoffPlanes = new Membership[]{new SidedPlane(cutoffPlane)};
- this.notablePoints = new GeoPoint[]{topEdgePoint, bottomEdgePoint};
- // 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.
- this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, cutoffPlane, topEdgePoint, bottomEdgePoint);
- }
-
- /** Constructor for case (2.5).
- * Generate an endpoint, given two cutoff planes plus upper and lower edge points.
- *@param point is the center.
- *@param cutoffPlane1 is one adjoining path segment cutoff plane.
- *@param cutoffPlane2 is another adjoining path segment cutoff plane.
- *@param topEdgePoint is a point on the cutoffPlane that should be also on the circle plane.
- *@param bottomEdgePoint is another point on the cutoffPlane that should be also on the circle plane.
- */
- public SegmentEndpoint(final GeoPoint point,
- final SidedPlane cutoffPlane1, final SidedPlane cutoffPlane2, final GeoPoint topEdgePoint, final GeoPoint bottomEdgePoint) {
- this.point = point;
- this.cutoffPlanes = new Membership[]{new SidedPlane(cutoffPlane1), new SidedPlane(cutoffPlane2)};
- this.notablePoints = new GeoPoint[]{topEdgePoint, bottomEdgePoint};
- // 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.
- this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(point, cutoffPlane1, topEdgePoint, bottomEdgePoint);
- }
-
- /** Constructor for case (3).
- * Generate an endpoint for an intersection, given four points.
- *@param point is the center.
- *@param prevCutoffPlane is the previous adjoining segment cutoff plane.
- *@param nextCutoffPlane is the next path segment cutoff plane.
- *@param notCand2Point is a point NOT on candidate2.
- *@param notCand1Point is a point NOT on candidate1.
- *@param notCand3Point is a point NOT on candidate3.
- *@param notCand4Point is a point NOT on candidate4.
- *@param candidate1 one of four candidate circle planes.
- *@param candidate2 one of four candidate circle planes.
- *@param candidate3 one of four candidate circle planes.
- *@param candidate4 one of four candidate circle planes.
- */
- public SegmentEndpoint(final GeoPoint point,
- final SidedPlane prevCutoffPlane, final SidedPlane nextCutoffPlane,
- final GeoPoint notCand2Point, final GeoPoint notCand1Point,
- final GeoPoint notCand3Point, final GeoPoint notCand4Point,
- final SidedPlane candidate1, final SidedPlane candidate2, final SidedPlane candidate3, final SidedPlane candidate4) {
- // Note: What we really need is a single plane that goes through all four points.
- // Since that's not possible in the ellipsoid case (because three points determine a plane, not four), we
- // need an approximation that at least creates a boundary that has no interruptions.
- // There are three obvious choices for the third point: either (a) one of the two remaining points, or (b) the top or bottom edge
- // intersection point. (a) has no guarantee of continuity, while (b) is capable of producing something very far from a circle if
- // the angle between segments is acute.
- // The solution is to look for the side (top or bottom) that has an intersection within the shape. We use the two points from
- // the opposite side to determine the plane, AND we pick the third to be either of the two points on the intersecting side
- // PROVIDED that the other point is within the final circle we come up with.
- this.point = point;
-
- // We construct four separate planes, and evaluate which one includes all interior points with least overlap
- // (Constructed beforehand because we need them for degeneracy check)
-
- final boolean cand1IsOtherWithin = candidate1!=null?candidate1.isWithin(notCand1Point):false;
- final boolean cand2IsOtherWithin = candidate2!=null?candidate2.isWithin(notCand2Point):false;
- final boolean cand3IsOtherWithin = candidate3!=null?candidate3.isWithin(notCand3Point):false;
- final boolean cand4IsOtherWithin = candidate4!=null?candidate4.isWithin(notCand4Point):false;
-
- if (cand1IsOtherWithin && cand2IsOtherWithin && cand3IsOtherWithin && cand4IsOtherWithin) {
- // The only way we should see both within is if all four points are coplanar. In that case, we default to the simplest treatment.
- this.circlePlane = candidate1; // doesn't matter which
- this.notablePoints = new GeoPoint[]{notCand2Point, notCand3Point, notCand1Point, notCand4Point};
- this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane), new SidedPlane(nextCutoffPlane)};
- } else if (cand1IsOtherWithin) {
- // Use candidate1, and DON'T include prevCutoffPlane in the cutoff planes list
- this.circlePlane = candidate1;
- this.notablePoints = new GeoPoint[]{notCand2Point, notCand3Point, notCand4Point};
- this.cutoffPlanes = new Membership[]{new SidedPlane(nextCutoffPlane)};
- } else if (cand2IsOtherWithin) {
- // Use candidate2
- this.circlePlane = candidate2;
- this.notablePoints = new GeoPoint[]{notCand3Point, notCand4Point, notCand1Point};
- this.cutoffPlanes = new Membership[]{new SidedPlane(nextCutoffPlane)};
- } else if (cand3IsOtherWithin) {
- this.circlePlane = candidate3;
- this.notablePoints = new GeoPoint[]{notCand4Point, notCand1Point, notCand2Point};
- this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane)};
- } else if (cand4IsOtherWithin) {
- this.circlePlane = candidate4;
- this.notablePoints = new GeoPoint[]{notCand1Point, notCand2Point, notCand3Point};
- this.cutoffPlanes = new Membership[]{new SidedPlane(prevCutoffPlane)};
- } else {
- // dunno what happened
- throw new RuntimeException("Couldn't come up with a plane through three points that included the fourth");
- }
- }
-
- /** Check if point is within this endpoint.
- *@param point is the point.
- *@return true of within.
- */
- public boolean isWithin(final Vector point) {
- if (circlePlane == null)
- return false;
- if (!circlePlane.isWithin(point))
- return false;
- for (final Membership m : cutoffPlanes) {
- if (!m.isWithin(point)) {
- return false;
- }
- }
- return true;
- }
-
- /** Check if point is within this endpoint.
- *@param x is the point x.
- *@param y is the point y.
- *@param z is the point z.
- *@return true of within.
- */
- public boolean isWithin(final double x, final double y, final double z) {
- if (circlePlane == null)
- return false;
- if (!circlePlane.isWithin(x, y, z))
- return false;
- for (final Membership m : cutoffPlanes) {
- if (!m.isWithin(x,y,z)) {
- return false;
- }
- }
- return true;
- }
-
- /** Compute interior path 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.
- */
- 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 distanceStyle.computeDistance(this.point, x, y, z);
- }
-
- /** Compute external 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.
- */
- public double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
- return distanceStyle.computeDistance(this.point, x, y, z);
- }
-
- /** 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) {
- //System.err.println(" looking for intersection between plane "+p+" and circle "+circlePlane+" on proper side of "+cutoffPlanes+" within "+bounds);
- if (circlePlane == null)
- return false;
- return circlePlane.intersects(planetModel, p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes);
- }
-
- /** Get the bounds for a segment endpoint.
- *@param planetModel is the planet model.
- *@param bounds are the bounds to be modified.
- */
- public void getBounds(final PlanetModel planetModel, Bounds bounds) {
- bounds.addPoint(point);
- if (circlePlane == null)
- return;
- bounds.addPlane(planetModel, circlePlane);
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof SegmentEndpoint))
- return false;
- SegmentEndpoint other = (SegmentEndpoint) o;
- return point.equals(other.point);
- }
-
- @Override
- public int hashCode() {
- return point.hashCode();
- }
-
- @Override
- public String toString() {
- return point.toString();
- }
- }
-
- /**
- * This is the pre-calculated data for a path segment.
- */
- public static class PathSegment {
- /** 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<DistanceStyle,Double>();
- /** Normalized plane connecting the two points and going through world center */
- public final Plane normalizedConnectingPlane;
- /** Cutoff plane parallel to connecting plane representing one side of the path segment */
- public final SidedPlane upperConnectingPlane;
- /** Cutoff plane parallel to connecting plane representing the other side of the path segment */
- public final SidedPlane lowerConnectingPlane;
- /** Plane going through the center and start point, marking the start edge of the segment */
- public final SidedPlane startCutoffPlane;
- /** Plane going through the center and end point, marking the end edge of the segment */
- public final SidedPlane endCutoffPlane;
- /** Upper right hand corner of segment */
- public final GeoPoint URHC;
- /** Lower right hand corner of segment */
- public final GeoPoint LRHC;
- /** Upper left hand corner of segment */
- public final GeoPoint ULHC;
- /** Lower left hand corner of segment */
- public final GeoPoint LLHC;
- /** Notable points for the upper connecting plane */
- public final GeoPoint[] upperConnectingPlanePoints;
- /** Notable points for the lower connecting plane */
- public final GeoPoint[] lowerConnectingPlanePoints;
- /** Notable points for the start cutoff plane */
- public final GeoPoint[] startCutoffPlanePoints;
- /** Notable points for the end cutoff plane */
- public final GeoPoint[] endCutoffPlanePoints;
-
- /** Construct a path segment.
- *@param planetModel is the planet model.
- *@param start is the starting point.
- *@param end is the ending point.
- *@param normalizedConnectingPlane is the connecting plane.
- *@param planeBoundingOffset is the linear offset from the connecting plane to either side.
- */
- public PathSegment(final PlanetModel planetModel, final GeoPoint start, final GeoPoint end,
- final Plane normalizedConnectingPlane, final double planeBoundingOffset) {
- this.start = start;
- this.end = end;
- this.normalizedConnectingPlane = normalizedConnectingPlane;
-
- // Either start or end should be on the correct side
- upperConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, -planeBoundingOffset);
- lowerConnectingPlane = new SidedPlane(start, normalizedConnectingPlane, planeBoundingOffset);
- // Cutoff planes use opposite endpoints as correct side examples
- startCutoffPlane = new SidedPlane(end, normalizedConnectingPlane, start);
- endCutoffPlane = new SidedPlane(start, normalizedConnectingPlane, end);
- final Membership[] upperSide = new Membership[]{upperConnectingPlane};
- final Membership[] lowerSide = new Membership[]{lowerConnectingPlane};
- final Membership[] startSide = new Membership[]{startCutoffPlane};
- final Membership[] endSide = new Membership[]{endCutoffPlane};
- GeoPoint[] points;
- points = upperConnectingPlane.findIntersections(planetModel, startCutoffPlane, lowerSide, endSide);
- if (points.length == 0) {
- throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
- }
- this.ULHC = points[0];
- points = upperConnectingPlane.findIntersections(planetModel, endCutoffPlane, lowerSide, startSide);
- if (points.length == 0) {
- throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
- }
- this.URHC = points[0];
- points = lowerConnectingPlane.findIntersections(planetModel, startCutoffPlane, upperSide, endSide);
- if (points.length == 0) {
- throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
- }
- this.LLHC = points[0];
- points = lowerConnectingPlane.findIntersections(planetModel, endCutoffPlane, upperSide, startSide);
- if (points.length == 0) {
- throw new IllegalArgumentException("Some segment boundary points are off the ellipsoid; path too wide");
- }
- this.LRHC = points[0];
- upperConnectingPlanePoints = new GeoPoint[]{ULHC, URHC};
- lowerConnectingPlanePoints = new GeoPoint[]{LLHC, LRHC};
- startCutoffPlanePoints = new GeoPoint[]{ULHC, LLHC};
- endCutoffPlanePoints = new GeoPoint[]{URHC, LRHC};
- }
-
- /** Compute the full distance along this path segment.
- *@param distanceStyle is the distance style.
- *@return the distance metric.
- */
- 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();
- }
- }
-
- /** Check if point is within this segment.
- *@param point is the point.
- *@return true of within.
- */
- public boolean isWithin(final Vector point) {
- return startCutoffPlane.isWithin(point) &&
- endCutoffPlane.isWithin(point) &&
- upperConnectingPlane.isWithin(point) &&
- lowerConnectingPlane.isWithin(point);
- }
-
- /** Check if point is within this segment.
- *@param x is the point x.
- *@param y is the point y.
- *@param z is the point z.
- *@return true of within.
- */
- public boolean isWithin(final double x, final double y, final double z) {
- return startCutoffPlane.isWithin(x, y, z) &&
- endCutoffPlane.isWithin(x, y, z) &&
- upperConnectingPlane.isWithin(x, y, z) &&
- lowerConnectingPlane.isWithin(x, y, z);
- }
-
- /** Compute interior path 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.
- */
- 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;
-
- // (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 * 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);
- }
-
- /** Compute external 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.
- */
- 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)));
- }
-
- /** 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) {
- return upperConnectingPlane.intersects(planetModel, p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
- lowerConnectingPlane.intersects(planetModel, p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
- }
-
- /** Get the bounds for a segment endpoint.
- *@param planetModel is the planet model.
- *@param bounds are the bounds to be modified.
- */
- public void getBounds(final PlanetModel planetModel, Bounds bounds) {
- // We need to do all bounding planes as well as corner points
- bounds.addPoint(start).addPoint(end).addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
- bounds.addPlane(planetModel, upperConnectingPlane, lowerConnectingPlane, startCutoffPlane, endCutoffPlane);
- bounds.addPlane(planetModel, lowerConnectingPlane, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
- bounds.addPlane(planetModel, startCutoffPlane, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
- bounds.addPlane(planetModel, endCutoffPlane, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane);
- }
-
- }
-
+public interface GeoPath extends GeoDistanceShape {
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/784e3e38/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPathFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPathFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPathFactory.java
new file mode 100644
index 0000000..3fd7cf7
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPathFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Class which constructs a GeoPath representing an arbitrary path.
+ *
+ * @lucene.experimental
+ */
+public class GeoPathFactory {
+ private GeoPathFactory() {
+ }
+
+ /**
+ * Create a GeoPath of the right kind given the specified information.
+ * @param planetModel is the planet model.
+ * @param maxCutoffAngle is the width of the path, measured as an angle.
+ * @param pathPoints are the points in the path.
+ * @return a GeoPath corresponding to what was specified.
+ */
+ public static GeoPath makeGeoPath(final PlanetModel planetModel, final double maxCutoffAngle, final GeoPoint[] pathPoints) {
+ return new GeoStandardPath(planetModel, maxCutoffAngle, pathPoints);
+ }
+
+}