diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java index 23778f19fa1..75b91c3ec58 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Bounds.java @@ -17,288 +17,288 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** An object for accumulating bounds information. -* The bounds object is initially empty. Bounding points -* are then applied by supplying (x,y,z) tuples. It is also -* possible to indicate the following edge cases: -* (1) No longitude bound possible -* (2) No upper latitude bound possible -* (3) No lower latitude bound possible -* When any of these have been applied, further application of -* points cannot override that decision. +/** + * An object for accumulating bounds information. + * The bounds object is initially empty. Bounding points + * are then applied by supplying (x,y,z) tuples. It is also + * possible to indicate the following edge cases: + * (1) No longitude bound possible + * (2) No upper latitude bound possible + * (3) No lower latitude bound possible + * When any of these have been applied, further application of + * points cannot override that decision. */ -public class Bounds -{ - protected boolean noLongitudeBound = false; - protected boolean noTopLatitudeBound = false; - protected boolean noBottomLatitudeBound = false; +public class Bounds { + protected boolean noLongitudeBound = false; + protected boolean noTopLatitudeBound = false; + protected boolean noBottomLatitudeBound = false; - protected Double minLatitude = null; - protected Double maxLatitude = null; + protected Double minLatitude = null; + protected Double maxLatitude = null; - // For longitude bounds, this class needs to worry about keeping track of the distinction - // between left-side bounds and right-side bounds. Points are always submitted in pairs - // which have a maximum longitude separation of Math.PI. It's therefore always possible - // to determine which point represents a left bound, and which point represents a right - // bound. - // - // The next problem is how to compare two of the same kind of bound, e.g. two left bounds. - // We need to keep track of the leftmost longitude of the shape, but since this is a circle, - // this is arbitrary. What we could try to do instead would be to find a pair of (left,right) bounds such - // that: - // (1) all other bounds are within, and - // (2) the left minus right distance is minimized - // Unfortunately, there are still shapes that cannot be summarized in this way correctly. - // For example. consider a spiral that entirely circles the globe; we might arbitrarily choose - // lat/lon bounds that do not in fact circle the globe. - // - // One way to handle the longitude issue correctly is therefore to stipulate that we - // walk the bounds of the shape in some kind of connected order. Each point or circle is therefore - // added in a sequence. We also need an interior point to make sure we have the right - // choice of longitude bounds. But even with this, we still can't always choose whether the actual shape - // goes right or left. - // - // We can make the specification truly general by submitting the following in order: - // addSide(PlaneSide side, Membership... constraints) - // ... - // This is unambiguous, but I still can't see yet how this would help compute the bounds. The plane - // solution would in general seem to boil down to the same logic that relies on points along the path - // to define the shape boundaries. I guess the one thing that you do know for a bounded edge is that - // the endpoints are actually connected. But it is not clear whether relationship helps in any way. - // - // In any case, if we specify shapes by a sequence of planes, we should stipulate that multiple sequences - // are allowed, provided they progressively tile an area of the sphere that is connected and sequential. - // For example, paths do alternating rectangles and circles, in sequence. Each sequence member is - // described by a sequence of planes. I think it would also be reasonable to insist that the first segment - // of a shape overlap or adjoin the previous shape. - // - // Here's a way to think about it that might help: Traversing every edge should grow the longitude bounds - // in the direction of the traversal. So if the traversal is always known to be less than PI in total longitude - // angle, then it is possible to use the endpoints to determine the unambiguous extension of the envelope. - // For example, say you are currently at longitude -0.5. The next point is at longitude PI-0.1. You could say - // that the difference in longitude going one way around would be beter than the distance the other way - // around, and therefore the longitude envelope should be extended accordingly. But in practice, when an - // edge goes near a pole and may be inclined as well, the longer longitude change might be the right path, even - // if the arc length is short. So this too doesn't work. - // - // Given we have a hard time making an exact match, here's the current proposal. The proposal is a - // heuristic, based on the idea that most areas are small compared to the circumference of the globe. - // We keep track of the last point we saw, and take each point as it arrives, and compute its longitude. - // Then, we have a choice as to which way to expand the envelope: we can expand by going to the left or - // to the right. We choose the direction with the least longitude difference. (If we aren't sure, - // and can recognize that, we can set "unconstrained in longitude".) - - protected Double leftLongitude = null; - protected Double rightLongitude = null; - - public Bounds() { - } + // For longitude bounds, this class needs to worry about keeping track of the distinction + // between left-side bounds and right-side bounds. Points are always submitted in pairs + // which have a maximum longitude separation of Math.PI. It's therefore always possible + // to determine which point represents a left bound, and which point represents a right + // bound. + // + // The next problem is how to compare two of the same kind of bound, e.g. two left bounds. + // We need to keep track of the leftmost longitude of the shape, but since this is a circle, + // this is arbitrary. What we could try to do instead would be to find a pair of (left,right) bounds such + // that: + // (1) all other bounds are within, and + // (2) the left minus right distance is minimized + // Unfortunately, there are still shapes that cannot be summarized in this way correctly. + // For example. consider a spiral that entirely circles the globe; we might arbitrarily choose + // lat/lon bounds that do not in fact circle the globe. + // + // One way to handle the longitude issue correctly is therefore to stipulate that we + // walk the bounds of the shape in some kind of connected order. Each point or circle is therefore + // added in a sequence. We also need an interior point to make sure we have the right + // choice of longitude bounds. But even with this, we still can't always choose whether the actual shape + // goes right or left. + // + // We can make the specification truly general by submitting the following in order: + // addSide(PlaneSide side, Membership... constraints) + // ... + // This is unambiguous, but I still can't see yet how this would help compute the bounds. The plane + // solution would in general seem to boil down to the same logic that relies on points along the path + // to define the shape boundaries. I guess the one thing that you do know for a bounded edge is that + // the endpoints are actually connected. But it is not clear whether relationship helps in any way. + // + // In any case, if we specify shapes by a sequence of planes, we should stipulate that multiple sequences + // are allowed, provided they progressively tile an area of the sphere that is connected and sequential. + // For example, paths do alternating rectangles and circles, in sequence. Each sequence member is + // described by a sequence of planes. I think it would also be reasonable to insist that the first segment + // of a shape overlap or adjoin the previous shape. + // + // Here's a way to think about it that might help: Traversing every edge should grow the longitude bounds + // in the direction of the traversal. So if the traversal is always known to be less than PI in total longitude + // angle, then it is possible to use the endpoints to determine the unambiguous extension of the envelope. + // For example, say you are currently at longitude -0.5. The next point is at longitude PI-0.1. You could say + // that the difference in longitude going one way around would be beter than the distance the other way + // around, and therefore the longitude envelope should be extended accordingly. But in practice, when an + // edge goes near a pole and may be inclined as well, the longer longitude change might be the right path, even + // if the arc length is short. So this too doesn't work. + // + // Given we have a hard time making an exact match, here's the current proposal. The proposal is a + // heuristic, based on the idea that most areas are small compared to the circumference of the globe. + // We keep track of the last point we saw, and take each point as it arrives, and compute its longitude. + // Then, we have a choice as to which way to expand the envelope: we can expand by going to the left or + // to the right. We choose the direction with the least longitude difference. (If we aren't sure, + // and can recognize that, we can set "unconstrained in longitude".) - public Double getMaxLatitude() { - return maxLatitude; + protected Double leftLongitude = null; + protected Double rightLongitude = null; + + public Bounds() { + } + + public Double getMaxLatitude() { + return maxLatitude; + } + + public Double getMinLatitude() { + return minLatitude; + } + + public Double getLeftLongitude() { + return leftLongitude; + } + + public Double getRightLongitude() { + return rightLongitude; + } + + public boolean checkNoLongitudeBound() { + return noLongitudeBound; + } + + public boolean checkNoTopLatitudeBound() { + return noTopLatitudeBound; + } + + public boolean checkNoBottomLatitudeBound() { + return noBottomLatitudeBound; + } + + public Bounds addHorizontalCircle(double z) { + if (!noTopLatitudeBound || !noBottomLatitudeBound) { + // Compute a latitude value + double latitude = Math.asin(z); + addLatitudeBound(latitude); } - - public Double getMinLatitude() { - return minLatitude; + return this; + } + + public Bounds addLatitudeZone(double latitude) { + if (!noTopLatitudeBound || !noBottomLatitudeBound) { + addLatitudeBound(latitude); } - - public Double getLeftLongitude() { - return leftLongitude; + return this; + } + + public Bounds addLongitudeSlice(double newLeftLongitude, double newRightLongitude) { + if (!noLongitudeBound) { + addLongitudeBound(newLeftLongitude, newRightLongitude); } - - public Double getRightLongitude() { - return rightLongitude; - } - - public boolean checkNoLongitudeBound() { - return noLongitudeBound; - } - - public boolean checkNoTopLatitudeBound() { - return noTopLatitudeBound; - } - - public boolean checkNoBottomLatitudeBound() { - return noBottomLatitudeBound; - } - - public Bounds addHorizontalCircle(double z) { - if (!noTopLatitudeBound || !noBottomLatitudeBound) { - // Compute a latitude value - double latitude = Math.asin(z); - addLatitudeBound(latitude); + return this; + } + + protected void addLatitudeBound(double latitude) { + if (!noTopLatitudeBound && (maxLatitude == null || latitude > maxLatitude)) + maxLatitude = latitude; + if (!noBottomLatitudeBound && (minLatitude == null || latitude < minLatitude)) + minLatitude = latitude; + } + + protected void addLongitudeBound(double newLeftLongitude, double newRightLongitude) { + if (leftLongitude == null && rightLongitude == null) { + leftLongitude = newLeftLongitude; + rightLongitude = newRightLongitude; + } else { + // Map the current range to something monotonically increasing + double currentLeftLongitude = leftLongitude; + double currentRightLongitude = rightLongitude; + if (currentRightLongitude < currentLeftLongitude) + currentRightLongitude += 2.0 * Math.PI; + double adjustedLeftLongitude = newLeftLongitude; + double adjustedRightLongitude = newRightLongitude; + if (adjustedRightLongitude < adjustedLeftLongitude) + adjustedRightLongitude += 2.0 * Math.PI; + // Compare to see what the relationship is + if (currentLeftLongitude <= adjustedLeftLongitude && currentRightLongitude >= adjustedRightLongitude) { + // No adjustment needed. + } else if (currentLeftLongitude >= adjustedLeftLongitude && currentRightLongitude <= adjustedRightLongitude) { + // New longitude entirely contains old one + leftLongitude = newLeftLongitude; + rightLongitude = newRightLongitude; + } else { + if (currentLeftLongitude > adjustedLeftLongitude) { + // New left longitude needed + leftLongitude = newLeftLongitude; } - return this; - } - - public Bounds addLatitudeZone(double latitude) { - if (!noTopLatitudeBound || !noBottomLatitudeBound) { - addLatitudeBound(latitude); + if (currentRightLongitude < adjustedRightLongitude) { + // New right longitude needed + rightLongitude = newRightLongitude; } - return this; + } } + double testRightLongitude = rightLongitude; + if (testRightLongitude < leftLongitude) + testRightLongitude += Math.PI * 2.0; + // If the bound exceeds 180 degrees, we know we could have screwed up. + if (testRightLongitude - leftLongitude >= Math.PI) { + noLongitudeBound = true; + leftLongitude = null; + rightLongitude = null; + } + } - public Bounds addLongitudeSlice(double newLeftLongitude, double newRightLongitude) { - if (!noLongitudeBound) { - addLongitudeBound(newLeftLongitude,newRightLongitude); - } - return this; - } - - protected void addLatitudeBound(double latitude) { - if (!noTopLatitudeBound && (maxLatitude == null || latitude > maxLatitude)) - maxLatitude = latitude; - if (!noBottomLatitudeBound && (minLatitude == null || latitude < minLatitude)) - minLatitude = latitude; - } - - protected void addLongitudeBound(double newLeftLongitude, double newRightLongitude) { - if (leftLongitude == null && rightLongitude == null) { - leftLongitude = newLeftLongitude; - rightLongitude = newRightLongitude; + protected void addLongitudeBound(double longitude) { + // If this point is within the current bounds, we're done; otherwise + // expand one side or the other. + if (leftLongitude == null && rightLongitude == null) { + leftLongitude = longitude; + rightLongitude = longitude; + } else { + // Compute whether we're to the right of the left value. But the left value may be greater than + // the right value. + double currentLeftLongitude = leftLongitude; + double currentRightLongitude = rightLongitude; + if (currentRightLongitude < currentLeftLongitude) + currentRightLongitude += 2.0 * Math.PI; + // We have a range to look at that's going in the right way. + // Now, do the same trick with the computed longitude. + if (longitude < currentLeftLongitude) + longitude += 2.0 * Math.PI; + + if (longitude < currentLeftLongitude || longitude > currentRightLongitude) { + // Outside of current bounds. Consider carefully how we'll expand. + double leftExtensionAmt; + double rightExtensionAmt; + if (longitude < currentLeftLongitude) { + leftExtensionAmt = currentLeftLongitude - longitude; } else { - // Map the current range to something monotonically increasing - double currentLeftLongitude = leftLongitude; - double currentRightLongitude = rightLongitude; - if (currentRightLongitude < currentLeftLongitude) - currentRightLongitude += 2.0 * Math.PI; - double adjustedLeftLongitude = newLeftLongitude; - double adjustedRightLongitude = newRightLongitude; - if (adjustedRightLongitude < adjustedLeftLongitude) - adjustedRightLongitude += 2.0 * Math.PI; - // Compare to see what the relationship is - if (currentLeftLongitude <= adjustedLeftLongitude && currentRightLongitude >= adjustedRightLongitude) { - // No adjustment needed. - } else if (currentLeftLongitude >= adjustedLeftLongitude && currentRightLongitude <= adjustedRightLongitude) { - // New longitude entirely contains old one - leftLongitude = newLeftLongitude; - rightLongitude = newRightLongitude; - } else { - if (currentLeftLongitude > adjustedLeftLongitude) { - // New left longitude needed - leftLongitude = newLeftLongitude; - } - if (currentRightLongitude < adjustedRightLongitude) { - // New right longitude needed - rightLongitude = newRightLongitude; - } - } + leftExtensionAmt = currentLeftLongitude + 2.0 * Math.PI - longitude; } - double testRightLongitude = rightLongitude; - if (testRightLongitude < leftLongitude) - testRightLongitude += Math.PI * 2.0; - // If the bound exceeds 180 degrees, we know we could have screwed up. - if (testRightLongitude - leftLongitude >= Math.PI) { - noLongitudeBound = true; - leftLongitude = null; - rightLongitude = null; - } - } - - protected void addLongitudeBound(double longitude) { - // If this point is within the current bounds, we're done; otherwise - // expand one side or the other. - if (leftLongitude == null && rightLongitude == null) { - leftLongitude = longitude; - rightLongitude = longitude; + if (longitude > currentRightLongitude) { + rightExtensionAmt = longitude - currentRightLongitude; } else { - // Compute whether we're to the right of the left value. But the left value may be greater than - // the right value. - double currentLeftLongitude = leftLongitude; - double currentRightLongitude = rightLongitude; - if (currentRightLongitude < currentLeftLongitude) - currentRightLongitude += 2.0 * Math.PI; - // We have a range to look at that's going in the right way. - // Now, do the same trick with the computed longitude. - if (longitude < currentLeftLongitude) - longitude += 2.0 * Math.PI; - - if (longitude < currentLeftLongitude || longitude > currentRightLongitude) { - // Outside of current bounds. Consider carefully how we'll expand. - double leftExtensionAmt; - double rightExtensionAmt; - if (longitude < currentLeftLongitude) { - leftExtensionAmt = currentLeftLongitude - longitude; - } else { - leftExtensionAmt = currentLeftLongitude + 2.0 * Math.PI - longitude; - } - if (longitude > currentRightLongitude) { - rightExtensionAmt = longitude - currentRightLongitude; - } else { - rightExtensionAmt = longitude + 2.0 * Math.PI - currentRightLongitude; - } - if (leftExtensionAmt < rightExtensionAmt) { - currentLeftLongitude = leftLongitude - leftExtensionAmt; - while (currentLeftLongitude <= -Math.PI) { - currentLeftLongitude += 2.0 * Math.PI; - } - leftLongitude = currentLeftLongitude; - } else { - currentRightLongitude = rightLongitude + rightExtensionAmt; - while (currentRightLongitude > Math.PI) { - currentRightLongitude -= 2.0 * Math.PI; - } - rightLongitude = currentRightLongitude; - } - } + rightExtensionAmt = longitude + 2.0 * Math.PI - currentRightLongitude; } - double testRightLongitude = rightLongitude; - if (testRightLongitude < leftLongitude) - testRightLongitude += Math.PI * 2.0; - if (testRightLongitude - leftLongitude >= Math.PI) { - noLongitudeBound = true; - leftLongitude = null; - rightLongitude = null; + if (leftExtensionAmt < rightExtensionAmt) { + currentLeftLongitude = leftLongitude - leftExtensionAmt; + while (currentLeftLongitude <= -Math.PI) { + currentLeftLongitude += 2.0 * Math.PI; + } + leftLongitude = currentLeftLongitude; + } else { + currentRightLongitude = rightLongitude + rightExtensionAmt; + while (currentRightLongitude > Math.PI) { + currentRightLongitude -= 2.0 * Math.PI; + } + rightLongitude = currentRightLongitude; } + } } - - public Bounds addPoint(Vector v) { - return addPoint(v.x, v.y, v.z); - } - - public Bounds addPoint(double x, double y, double z) { - if (!noLongitudeBound) { - // Get a longitude value - double longitude = Math.atan2(y,x); - //System.err.println(" add longitude bound at "+longitude * 180.0/Math.PI); - addLongitudeBound(longitude); - } - if (!noTopLatitudeBound || !noBottomLatitudeBound) { - // Compute a latitude value - double latitude = Math.asin(z); - addLatitudeBound(latitude); - } - return this; + double testRightLongitude = rightLongitude; + if (testRightLongitude < leftLongitude) + testRightLongitude += Math.PI * 2.0; + if (testRightLongitude - leftLongitude >= Math.PI) { + noLongitudeBound = true; + leftLongitude = null; + rightLongitude = null; } + } - public Bounds addPoint(double latitude, double longitude) { - if (!noLongitudeBound) { - // Get a longitude value - addLongitudeBound(longitude); - } - if (!noTopLatitudeBound || !noBottomLatitudeBound) { - // Compute a latitude value - addLatitudeBound(latitude); - } - return this; + public Bounds addPoint(Vector v) { + return addPoint(v.x, v.y, v.z); + } + + public Bounds addPoint(double x, double y, double z) { + if (!noLongitudeBound) { + // Get a longitude value + double longitude = Math.atan2(y, x); + //System.err.println(" add longitude bound at "+longitude * 180.0/Math.PI); + addLongitudeBound(longitude); } - - public Bounds noLongitudeBound() { - noLongitudeBound = true; - leftLongitude = null; - rightLongitude = null; - return this; + if (!noTopLatitudeBound || !noBottomLatitudeBound) { + // Compute a latitude value + double latitude = Math.asin(z); + addLatitudeBound(latitude); } - - public Bounds noTopLatitudeBound() { - noTopLatitudeBound = true; - maxLatitude = null; - return this; + return this; + } + + public Bounds addPoint(double latitude, double longitude) { + if (!noLongitudeBound) { + // Get a longitude value + addLongitudeBound(longitude); } - - public Bounds noBottomLatitudeBound() { - noBottomLatitudeBound = true; - minLatitude = null; - return this; + if (!noTopLatitudeBound || !noBottomLatitudeBound) { + // Compute a latitude value + addLatitudeBound(latitude); } + return this; + } + + public Bounds noLongitudeBound() { + noLongitudeBound = true; + leftLongitude = null; + rightLongitude = null; + return this; + } + + public Bounds noTopLatitudeBound() { + noTopLatitudeBound = true; + maxLatitude = null; + return this; + } + + public Bounds noBottomLatitudeBound() { + noBottomLatitudeBound = true; + minLatitude = null; + return this; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoArea.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoArea.java index cf3e6d187e8..4412e8b0eca 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoArea.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoArea.java @@ -17,33 +17,37 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** A GeoArea represents a standard 2-D breakdown of a part of sphere. It can -* be bounded in latitude, or bounded in both latitude and longitude, or not -* bounded at all. The purpose of the interface is to describe bounding shapes used for -* computation of geo hashes. */ +/** + * A GeoArea represents a standard 2-D breakdown of a part of sphere. It can + * be bounded in latitude, or bounded in both latitude and longitude, or not + * bounded at all. The purpose of the interface is to describe bounding shapes used for + * computation of geo hashes. + */ public interface GeoArea extends Membership { // Since we don't know what each GeoArea's constraints are, // we put the onus on the GeoArea implementation to do the right thing. // This will, of course, rely heavily on methods provided by // the underlying GeoShape class. - - public static final int CONTAINS = 0; - public static final int WITHIN = 1; - public static final int OVERLAPS = 2; - public static final int DISJOINT = 3; - /** Find the spatial relationship between a shape and the current geo area. - * Note: return value is how the GeoShape relates to the GeoArea, not the - * other way around. For example, if this GeoArea is entirely within the - * shape, then CONTAINS should be returned. If the shape is entirely enclosed - * by this GeoArea, then WITHIN should be returned. - * Note well: When a shape consists of multiple independent overlapping subshapes, - * it is sometimes impossible to determine the distinction between - * OVERLAPS and CONTAINS. In that case, OVERLAPS may be returned even - * though the proper result would in fact be CONTAINS. Code accordingly. - *@param shape is the shape to consider. - *@return the relationship, from the perspective of the shape. - */ - public int getRelationship(GeoShape shape); + public static final int CONTAINS = 0; + public static final int WITHIN = 1; + public static final int OVERLAPS = 2; + public static final int DISJOINT = 3; + + /** + * Find the spatial relationship between a shape and the current geo area. + * Note: return value is how the GeoShape relates to the GeoArea, not the + * other way around. For example, if this GeoArea is entirely within the + * shape, then CONTAINS should be returned. If the shape is entirely enclosed + * by this GeoArea, then WITHIN should be returned. + * Note well: When a shape consists of multiple independent overlapping subshapes, + * it is sometimes impossible to determine the distinction between + * OVERLAPS and CONTAINS. In that case, OVERLAPS may be returned even + * though the proper result would in fact be CONTAINS. Code accordingly. + * + * @param shape is the shape to consider. + * @return the relationship, from the perspective of the shape. + */ + public int getRelationship(GeoShape shape); } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java index 62c391e6567..d391da262de 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoAreaFactory.java @@ -17,20 +17,21 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -public class GeoAreaFactory -{ - private GeoAreaFactory() { - } - - /** Create a GeoArea of the right kind given the specified bounds. - *@param topLat is the top latitude - *@param bottomLat is the bottom latitude - *@param leftLon is the left longitude - *@param rightLon is the right longitude - *@return a GeoArea corresponding to what was specified. - */ - public static GeoArea makeGeoArea(double topLat, double bottomLat, double leftLon, double rightLon) { - return GeoBBoxFactory.makeGeoBBox(topLat, bottomLat, leftLon, rightLon); - } +public class GeoAreaFactory { + private GeoAreaFactory() { + } + + /** + * Create a GeoArea of the right kind given the specified bounds. + * + * @param topLat is the top latitude + * @param bottomLat is the bottom latitude + * @param leftLon is the left longitude + * @param rightLon is the right longitude + * @return a GeoArea corresponding to what was specified. + */ + public static GeoArea makeGeoArea(double topLat, double bottomLat, double leftLon, double rightLon) { + return GeoBBoxFactory.makeGeoBBox(topLat, bottomLat, leftLon, rightLon); + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBox.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBox.java index ea92c7457b1..5ff8739bc75 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBox.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBox.java @@ -17,16 +17,19 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** All bounding box shapes have this interface in common. -* This describes methods that bounding boxes have above and beyond -* GeoMembershipShape's. -*/ +/** + * All bounding box shapes have this interface in common. + * This describes methods that bounding boxes have above and beyond + * GeoMembershipShape's. + */ public interface GeoBBox extends GeoMembershipShape, GeoSizeable, GeoArea { - /** Expand box by specified angle. - *@param angle is the angle amount to expand the GeoBBox by. - *@return a new GeoBBox. - */ - public GeoBBox expand(double angle); - + /** + * Expand box by specified angle. + * + * @param angle is the angle amount to expand the GeoBBox by. + * @return a new GeoBBox. + */ + public GeoBBox expand(double angle); + } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java index d11f2cfa5a2..44c01555750 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxBase.java @@ -17,39 +17,40 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** All bounding box shapes can derive from this base class, which furnishes -* some common code -*/ +/** + * All bounding box shapes can derive from this base class, which furnishes + * some common code + */ public abstract class GeoBBoxBase implements GeoBBox { - protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0,0.0,1.0); - protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0,0.0,-1.0); + protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0, 0.0, 1.0); + protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0, 0.0, -1.0); - @Override - public abstract boolean isWithin(final Vector point); + @Override + public abstract boolean isWithin(final Vector point); - protected final static int ALL_INSIDE = 0; - protected final static int SOME_INSIDE = 1; - protected final static int NONE_INSIDE = 2; - - protected int isShapeInsideBBox(final GeoShape path) { - final GeoPoint[] pathPoints = path.getEdgePoints(); - boolean foundOutside = false; - boolean foundInside = false; - for (GeoPoint p : pathPoints) { - if (isWithin(p)) { - foundInside = true; - } else { - foundOutside = true; - } - } - if (!foundInside && !foundOutside) - return NONE_INSIDE; - if (foundInside && !foundOutside) - return ALL_INSIDE; - if (foundOutside && !foundInside) - return NONE_INSIDE; - return SOME_INSIDE; + protected final static int ALL_INSIDE = 0; + protected final static int SOME_INSIDE = 1; + protected final static int NONE_INSIDE = 2; + + protected int isShapeInsideBBox(final GeoShape path) { + final GeoPoint[] pathPoints = path.getEdgePoints(); + boolean foundOutside = false; + boolean foundInside = false; + for (GeoPoint p : pathPoints) { + if (isWithin(p)) { + foundInside = true; + } else { + foundOutside = true; + } } + if (!foundInside && !foundOutside) + return NONE_INSIDE; + if (foundInside && !foundOutside) + return ALL_INSIDE; + if (foundOutside && !foundInside) + return NONE_INSIDE; + return SOME_INSIDE; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java index 5fae6bb8f85..2f295fa4184 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxFactory.java @@ -17,86 +17,87 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -public class GeoBBoxFactory -{ - private GeoBBoxFactory() { - } - - /** Create a geobbox of the right kind given the specified bounds. - *@param topLat is the top latitude - *@param bottomLat is the bottom latitude - *@param leftLon is the left longitude - *@param rightLon is the right longitude - *@return a GeoBBox corresponding to what was specified. - */ - public static GeoBBox makeGeoBBox(double topLat, double bottomLat, double leftLon, double rightLon) { - //System.err.println("Making rectangle for topLat="+topLat*180.0/Math.PI+", bottomLat="+bottomLat*180.0/Math.PI+", leftLon="+leftLon*180.0/Math.PI+", rightlon="+rightLon*180.0/Math.PI); - if (topLat > Math.PI * 0.5) - topLat = Math.PI * 0.5; - if (bottomLat < -Math.PI * 0.5) - bottomLat = -Math.PI * 0.5; - if (leftLon < -Math.PI) - leftLon = -Math.PI; - if (rightLon > Math.PI) - rightLon = Math.PI; - if (leftLon == -Math.PI && rightLon == Math.PI) { - if (topLat == Math.PI * 0.5 && bottomLat == -Math.PI * 0.5) - return new GeoWorld(); - if (topLat == bottomLat) { - if (topLat == Math.PI * 0.5 || topLat == -Math.PI * 0.5) - return new GeoDegeneratePoint(topLat,0.0); - return new GeoDegenerateLatitudeZone(topLat); - } - if (topLat == Math.PI * 0.5) - return new GeoNorthLatitudeZone(bottomLat); - else if (bottomLat == -Math.PI * 0.5) - return new GeoSouthLatitudeZone(topLat); - return new GeoLatitudeZone(topLat, bottomLat); - } - //System.err.println(" not latitude zone"); - double extent = rightLon - leftLon; - if (extent < 0.0) - extent += Math.PI * 2.0; - if (topLat == Math.PI * 0.5 && bottomLat == -Math.PI * 0.5) { - if (leftLon == rightLon) - return new GeoDegenerateLongitudeSlice(leftLon); +public class GeoBBoxFactory { + private GeoBBoxFactory() { + } - if (extent >= Math.PI) - return new GeoWideLongitudeSlice(leftLon, rightLon); - - return new GeoLongitudeSlice(leftLon, rightLon); - } - //System.err.println(" not longitude slice"); - if (leftLon == rightLon) { - if (topLat == bottomLat) - return new GeoDegeneratePoint(topLat, leftLon); - return new GeoDegenerateVerticalLine(topLat, bottomLat, leftLon); - } - //System.err.println(" not vertical line"); - if (extent >= Math.PI) { - if (topLat == bottomLat) { - //System.err.println(" wide degenerate line"); - return new GeoWideDegenerateHorizontalLine(topLat, leftLon, rightLon); - } - if (topLat == Math.PI * 0.5) { - return new GeoWideNorthRectangle(bottomLat, leftLon, rightLon); - } else if (bottomLat == -Math.PI * 0.5) { - return new GeoWideSouthRectangle(topLat, leftLon, rightLon); - } - //System.err.println(" wide rect"); - return new GeoWideRectangle(topLat, bottomLat, leftLon, rightLon); - } - if (topLat == bottomLat) { - //System.err.println(" horizontal line"); - return new GeoDegenerateHorizontalLine(topLat, leftLon, rightLon); - } - if (topLat == Math.PI * 0.5) { - return new GeoNorthRectangle(bottomLat, leftLon, rightLon); - } else if (bottomLat == -Math.PI * 0.5) { - return new GeoSouthRectangle(topLat, leftLon, rightLon); - } - //System.err.println(" rectangle"); - return new GeoRectangle(topLat, bottomLat, leftLon, rightLon); + /** + * Create a geobbox of the right kind given the specified bounds. + * + * @param topLat is the top latitude + * @param bottomLat is the bottom latitude + * @param leftLon is the left longitude + * @param rightLon is the right longitude + * @return a GeoBBox corresponding to what was specified. + */ + public static GeoBBox makeGeoBBox(double topLat, double bottomLat, double leftLon, double rightLon) { + //System.err.println("Making rectangle for topLat="+topLat*180.0/Math.PI+", bottomLat="+bottomLat*180.0/Math.PI+", leftLon="+leftLon*180.0/Math.PI+", rightlon="+rightLon*180.0/Math.PI); + if (topLat > Math.PI * 0.5) + topLat = Math.PI * 0.5; + if (bottomLat < -Math.PI * 0.5) + bottomLat = -Math.PI * 0.5; + if (leftLon < -Math.PI) + leftLon = -Math.PI; + if (rightLon > Math.PI) + rightLon = Math.PI; + if (leftLon == -Math.PI && rightLon == Math.PI) { + if (topLat == Math.PI * 0.5 && bottomLat == -Math.PI * 0.5) + return new GeoWorld(); + if (topLat == bottomLat) { + if (topLat == Math.PI * 0.5 || topLat == -Math.PI * 0.5) + return new GeoDegeneratePoint(topLat, 0.0); + return new GeoDegenerateLatitudeZone(topLat); + } + if (topLat == Math.PI * 0.5) + return new GeoNorthLatitudeZone(bottomLat); + else if (bottomLat == -Math.PI * 0.5) + return new GeoSouthLatitudeZone(topLat); + return new GeoLatitudeZone(topLat, bottomLat); } + //System.err.println(" not latitude zone"); + double extent = rightLon - leftLon; + if (extent < 0.0) + extent += Math.PI * 2.0; + if (topLat == Math.PI * 0.5 && bottomLat == -Math.PI * 0.5) { + if (leftLon == rightLon) + return new GeoDegenerateLongitudeSlice(leftLon); + + if (extent >= Math.PI) + return new GeoWideLongitudeSlice(leftLon, rightLon); + + return new GeoLongitudeSlice(leftLon, rightLon); + } + //System.err.println(" not longitude slice"); + if (leftLon == rightLon) { + if (topLat == bottomLat) + return new GeoDegeneratePoint(topLat, leftLon); + return new GeoDegenerateVerticalLine(topLat, bottomLat, leftLon); + } + //System.err.println(" not vertical line"); + if (extent >= Math.PI) { + if (topLat == bottomLat) { + //System.err.println(" wide degenerate line"); + return new GeoWideDegenerateHorizontalLine(topLat, leftLon, rightLon); + } + if (topLat == Math.PI * 0.5) { + return new GeoWideNorthRectangle(bottomLat, leftLon, rightLon); + } else if (bottomLat == -Math.PI * 0.5) { + return new GeoWideSouthRectangle(topLat, leftLon, rightLon); + } + //System.err.println(" wide rect"); + return new GeoWideRectangle(topLat, bottomLat, leftLon, rightLon); + } + if (topLat == bottomLat) { + //System.err.println(" horizontal line"); + return new GeoDegenerateHorizontalLine(topLat, leftLon, rightLon); + } + if (topLat == Math.PI * 0.5) { + return new GeoNorthRectangle(bottomLat, leftLon, rightLon); + } else if (bottomLat == -Math.PI * 0.5) { + return new GeoSouthRectangle(topLat, leftLon, rightLon); + } + //System.err.println(" rectangle"); + return new GeoRectangle(topLat, bottomLat, leftLon, rightLon); + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java index 407aa6110ac..020794b5cce 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoBaseExtendedShape.java @@ -17,68 +17,76 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Base extended shape object. -*/ -public abstract class GeoBaseExtendedShape implements GeoShape -{ - protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0,0.0,1.0); - protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0,0.0,-1.0); - - public GeoBaseExtendedShape() - { +/** + * Base extended shape object. + */ +public abstract class GeoBaseExtendedShape implements GeoShape { + protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0, 0.0, 1.0); + protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0, 0.0, -1.0); + + public GeoBaseExtendedShape() { + } + + /** + * Check if a point is within this shape. + * + * @param point is the point to check. + * @return true if the point is within this shape + */ + @Override + public abstract boolean isWithin(final Vector point); + + /** + * Check if a point is within this shape. + * + * @param x is x coordinate of point to check. + * @param y is y coordinate of point to check. + * @param z is z coordinate of point to check. + * @return true if the point is within this shape + */ + @Override + public abstract boolean isWithin(final double x, final double y, final double z); + + /** + * Return a sample point that is on the edge of the shape. + * + * @return a number of edge points, one for each disconnected edge. + */ + @Override + public abstract GeoPoint[] getEdgePoints(); + + /** + * Assess whether a plane, within the provided bounds, intersects + * with the shape. + * + * @param plane is the plane to assess for intersection with the shape's edges or + * bounding curves. + * @param bounds are a set of bounds that define an area that an + * intersection must be within in order to qualify (provided by a GeoArea). + * @return true if there's such an intersection, false if not. + */ + @Override + public abstract boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds); + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + if (isWithin(NORTH_POLE)) { + bounds.noTopLatitudeBound().noLongitudeBound(); } - - /** Check if a point is within this shape. - *@param point is the point to check. - *@return true if the point is within this shape - */ - @Override - public abstract boolean isWithin(final Vector point); - - /** Check if a point is within this shape. - *@param x is x coordinate of point to check. - *@param y is y coordinate of point to check. - *@param z is z coordinate of point to check. - *@return true if the point is within this shape - */ - @Override - public abstract boolean isWithin(final double x, final double y, final double z); - - /** Return a sample point that is on the edge of the shape. - *@return a number of edge points, one for each disconnected edge. - */ - @Override - public abstract GeoPoint[] getEdgePoints(); - - /** Assess whether a plane, within the provided bounds, intersects - * with the shape. - *@param plane is the plane to assess for intersection with the shape's edges or - * bounding curves. - *@param bounds are a set of bounds that define an area that an - * intersection must be within in order to qualify (provided by a GeoArea). - *@return true if there's such an intersection, false if not. - */ - @Override - public abstract boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds); - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - if (isWithin(NORTH_POLE)) { - bounds.noTopLatitudeBound().noLongitudeBound(); - } - if (isWithin(SOUTH_POLE)) { - bounds.noBottomLatitudeBound().noLongitudeBound(); - } - return bounds; + if (isWithin(SOUTH_POLE)) { + bounds.noBottomLatitudeBound().noLongitudeBound(); } + return bounds; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java index a0e97076e53..3803e833791 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircle.java @@ -17,244 +17,241 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Circular area with a center and radius. -*/ -public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, GeoSizeable -{ - public final GeoPoint center; - public final double cutoffAngle; - public final double cutoffNormalDistance; - public final double cutoffLinearDistance; - public final SidedPlane circlePlane; - public final GeoPoint[] edgePoints; - public static final GeoPoint[] circlePoints = new GeoPoint[0]; - - public GeoCircle(final double lat, final double lon, final double cutoffAngle) - { - super(); - if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5) - throw new IllegalArgumentException("Latitude out of bounds"); - if (lon < -Math.PI || lon > Math.PI) - throw new IllegalArgumentException("Longitude out of bounds"); - if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI) - throw new IllegalArgumentException("Cutoff angle out of bounds"); - final double sinAngle = Math.sin(cutoffAngle); - final double cosAngle = Math.cos(cutoffAngle); - this.center = new GeoPoint(lat,lon); - this.cutoffNormalDistance = sinAngle; - // Need the chord distance. This is just the chord distance: sqrt((1 - cos(angle))^2 + (sin(angle))^2). - final double xDiff = 1.0 - cosAngle; - this.cutoffLinearDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle); - this.cutoffAngle = cutoffAngle; - this.circlePlane = new SidedPlane(center, center, -cosAngle); - - // Compute a point on the circle boundary. - if (cutoffAngle == Math.PI) - this.edgePoints = new GeoPoint[0]; - else { - // Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also. - double newLat = lat + cutoffAngle; - double newLon = lon; - if (newLat > Math.PI * 0.5) { - newLat = Math.PI - newLat; - newLon += Math.PI; - } - while (newLon > Math.PI) { - newLon -= Math.PI * 2.0; - } - final GeoPoint edgePoint = new GeoPoint(newLat,newLon); - //if (Math.abs(circlePlane.evaluate(edgePoint)) > 1e-10) - // throw new RuntimeException("Computed an edge point that does not satisfy circlePlane equation! "+circlePlane.evaluate(edgePoint)); - this.edgePoints = new GeoPoint[]{edgePoint}; - } - } - - @Override - public double getRadius() { - return cutoffAngle; - } +/** + * Circular area with a center and radius. + */ +public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, GeoSizeable { + public final GeoPoint center; + public final double cutoffAngle; + public final double cutoffNormalDistance; + public final double cutoffLinearDistance; + public final SidedPlane circlePlane; + public final GeoPoint[] edgePoints; + public static final GeoPoint[] circlePoints = new GeoPoint[0]; - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return center; - } + public GeoCircle(final double lat, final double lon, final double cutoffAngle) { + super(); + if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5) + throw new IllegalArgumentException("Latitude out of bounds"); + if (lon < -Math.PI || lon > Math.PI) + throw new IllegalArgumentException("Longitude out of bounds"); + if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI) + throw new IllegalArgumentException("Cutoff angle out of bounds"); + final double sinAngle = Math.sin(cutoffAngle); + final double cosAngle = Math.cos(cutoffAngle); + this.center = new GeoPoint(lat, lon); + this.cutoffNormalDistance = sinAngle; + // Need the chord distance. This is just the chord distance: sqrt((1 - cos(angle))^2 + (sin(angle))^2). + final double xDiff = 1.0 - cosAngle; + this.cutoffLinearDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle); + this.cutoffAngle = cutoffAngle; + this.circlePlane = new SidedPlane(center, center, -cosAngle); - /** Compute an estimate of "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - */ - @Override - public double computeNormalDistance(final GeoPoint point) - { - double normalDistance = this.center.normalDistance(point); - if (normalDistance > cutoffNormalDistance) - return Double.MAX_VALUE; - return normalDistance; + // Compute a point on the circle boundary. + if (cutoffAngle == Math.PI) + this.edgePoints = new GeoPoint[0]; + else { + // Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also. + double newLat = lat + cutoffAngle; + double newLon = lon; + if (newLat > Math.PI * 0.5) { + newLat = Math.PI - newLat; + newLon += Math.PI; + } + while (newLon > Math.PI) { + newLon -= Math.PI * 2.0; + } + final GeoPoint edgePoint = new GeoPoint(newLat, newLon); + //if (Math.abs(circlePlane.evaluate(edgePoint)) > 1e-10) + // throw new RuntimeException("Computed an edge point that does not satisfy circlePlane equation! "+circlePlane.evaluate(edgePoint)); + this.edgePoints = new GeoPoint[]{edgePoint}; } + } - /** Compute an estimate of "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - */ - @Override - public double computeNormalDistance(final double x, final double y, final double z) - { - double normalDistance = this.center.normalDistance(x,y,z); - if (normalDistance > cutoffNormalDistance) - return Double.MAX_VALUE; - return normalDistance; - } - - /** Compute a squared estimate of the "distance" to the - * GeoPoint. Double.MAX_VALUE indicates a point outside of the - * shape. - */ - @Override - public double computeSquaredNormalDistance(final GeoPoint point) - { - double normalDistanceSquared = this.center.normalDistanceSquared(point); - if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance) - return Double.MAX_VALUE; - return normalDistanceSquared; - } - - /** Compute a squared estimate of the "distance" to the - * GeoPoint. Double.MAX_VALUE indicates a point outside of the - * shape. - */ - @Override - public double computeSquaredNormalDistance(final double x, final double y, final double z) - { - double normalDistanceSquared = this.center.normalDistanceSquared(x,y,z); - if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance) - return Double.MAX_VALUE; - return normalDistanceSquared; - } - - /** Compute a linear distance to the vector. - * return Double.MAX_VALUE for points outside the shape. - */ - @Override - public double computeLinearDistance(final GeoPoint point) - { - double linearDistance = this.center.linearDistance(point); - if (linearDistance > cutoffLinearDistance) - return Double.MAX_VALUE; - return linearDistance; - } - - /** Compute a linear distance to the vector. - * return Double.MAX_VALUE for points outside the shape. - */ - @Override - public double computeLinearDistance(final double x, final double y, final double z) - { - double linearDistance = this.center.linearDistance(x,y,z); - if (linearDistance > cutoffLinearDistance) - return Double.MAX_VALUE; - return linearDistance; - } - - /** Compute a squared linear distance to the vector. - */ - @Override - public double computeSquaredLinearDistance(final GeoPoint point) - { - double linearDistanceSquared = this.center.linearDistanceSquared(point); - if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance) - return Double.MAX_VALUE; - return linearDistanceSquared; - } - - /** Compute a squared linear distance to the vector. - */ - @Override - public double computeSquaredLinearDistance(final double x, final double y, final double z) - { - double linearDistanceSquared = this.center.linearDistanceSquared(x,y,z); - if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance) - return Double.MAX_VALUE; - return linearDistanceSquared; - } - - /** Compute a true, accurate, great-circle distance. - * Double.MAX_VALUE indicates a point is outside of the shape. - */ - @Override - public double computeArcDistance(final GeoPoint point) - { - double dist = this.center.arcDistance(point); - if (dist > cutoffAngle) - return Double.MAX_VALUE; - return dist; - } + @Override + public double getRadius() { + return cutoffAngle; + } - @Override - public boolean isWithin(final Vector point) - { - // Fastest way of determining membership - return circlePlane.isWithin(point); - } + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return center; + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - // Fastest way of determining membership - return circlePlane.isWithin(x,y,z); - } + /** + * Compute an estimate of "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + */ + @Override + public double computeNormalDistance(final GeoPoint point) { + double normalDistance = this.center.normalDistance(point); + if (normalDistance > cutoffNormalDistance) + return Double.MAX_VALUE; + return normalDistance; + } - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return circlePlane.intersects(p, notablePoints, circlePoints, bounds); - } + /** + * Compute an estimate of "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + */ + @Override + public double computeNormalDistance(final double x, final double y, final double z) { + double normalDistance = this.center.normalDistance(x, y, z); + if (normalDistance > cutoffNormalDistance) + return Double.MAX_VALUE; + return normalDistance; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - bounds = super.getBounds(bounds); - bounds.addPoint(center); - circlePlane.recordBounds(bounds); - return bounds; - } + /** + * Compute a squared estimate of the "distance" to the + * GeoPoint. Double.MAX_VALUE indicates a point outside of the + * shape. + */ + @Override + public double computeSquaredNormalDistance(final GeoPoint point) { + double normalDistanceSquared = this.center.normalDistanceSquared(point); + if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance) + return Double.MAX_VALUE; + return normalDistanceSquared; + } - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoCircle)) - return false; - GeoCircle other = (GeoCircle)o; - return other.center.equals(center) && other.cutoffAngle == cutoffAngle; - } + /** + * Compute a squared estimate of the "distance" to the + * GeoPoint. Double.MAX_VALUE indicates a point outside of the + * shape. + */ + @Override + public double computeSquaredNormalDistance(final double x, final double y, final double z) { + double normalDistanceSquared = this.center.normalDistanceSquared(x, y, z); + if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance) + return Double.MAX_VALUE; + return normalDistanceSquared; + } - @Override - public int hashCode() { - int result; - long temp; - result = center.hashCode(); - temp = Double.doubleToLongBits(cutoffAngle); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "GeoCircle: {center="+center+", radius="+cutoffAngle+"("+cutoffAngle*180.0/Math.PI+")}"; - } + /** + * Compute a linear distance to the vector. + * return Double.MAX_VALUE for points outside the shape. + */ + @Override + public double computeLinearDistance(final GeoPoint point) { + double linearDistance = this.center.linearDistance(point); + if (linearDistance > cutoffLinearDistance) + return Double.MAX_VALUE; + return linearDistance; + } + + /** + * Compute a linear distance to the vector. + * return Double.MAX_VALUE for points outside the shape. + */ + @Override + public double computeLinearDistance(final double x, final double y, final double z) { + double linearDistance = this.center.linearDistance(x, y, z); + if (linearDistance > cutoffLinearDistance) + return Double.MAX_VALUE; + return linearDistance; + } + + /** + * Compute a squared linear distance to the vector. + */ + @Override + public double computeSquaredLinearDistance(final GeoPoint point) { + double linearDistanceSquared = this.center.linearDistanceSquared(point); + if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance) + return Double.MAX_VALUE; + return linearDistanceSquared; + } + + /** + * Compute a squared linear distance to the vector. + */ + @Override + public double computeSquaredLinearDistance(final double x, final double y, final double z) { + double linearDistanceSquared = this.center.linearDistanceSquared(x, y, z); + if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance) + return Double.MAX_VALUE; + return linearDistanceSquared; + } + + /** + * Compute a true, accurate, great-circle distance. + * Double.MAX_VALUE indicates a point is outside of the shape. + */ + @Override + public double computeArcDistance(final GeoPoint point) { + double dist = this.center.arcDistance(point); + if (dist > cutoffAngle) + return Double.MAX_VALUE; + return dist; + } + + @Override + public boolean isWithin(final Vector point) { + // Fastest way of determining membership + return circlePlane.isWithin(point); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + // Fastest way of determining membership + return circlePlane.isWithin(x, y, z); + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return circlePlane.intersects(p, notablePoints, circlePoints, bounds); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + bounds = super.getBounds(bounds); + bounds.addPoint(center); + circlePlane.recordBounds(bounds); + return bounds; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoCircle)) + return false; + GeoCircle other = (GeoCircle) o; + return other.center.equals(center) && other.cutoffAngle == cutoffAngle; + } + + @Override + public int hashCode() { + int result; + long temp; + result = center.hashCode(); + temp = Double.doubleToLongBits(cutoffAngle); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public String toString() { + return "GeoCircle: {center=" + center + ", radius=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCompositeMembershipShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCompositeMembershipShape.java index 9b1764e098f..7201b86b433 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCompositeMembershipShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoCompositeMembershipShape.java @@ -20,103 +20,99 @@ package org.apache.lucene.spatial.spatial4j.geo3d; import java.util.ArrayList; import java.util.List; -/** GeoComposite is a set of GeoMembershipShape's, treated as a unit. -*/ -public class GeoCompositeMembershipShape implements GeoMembershipShape -{ - protected final List shapes = new ArrayList(); - - public GeoCompositeMembershipShape() - { - } - - /** Add a shape to the composite. - */ - public void addShape(final GeoMembershipShape shape) { - shapes.add(shape); - } +/** + * GeoComposite is a set of GeoMembershipShape's, treated as a unit. + */ +public class GeoCompositeMembershipShape implements GeoMembershipShape { + protected final List shapes = new ArrayList(); - @Override - public boolean isWithin(final Vector point) - { - //System.err.println("Checking whether point "+point+" is within Composite"); - for (GeoMembershipShape shape : shapes) { - if (shape.isWithin(point)) { - //System.err.println(" Point is within "+shape); - return true; - } - } - return false; - } + public GeoCompositeMembershipShape() { + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - for (GeoMembershipShape shape : shapes) { - if (shape.isWithin(x,y,z)) - return true; - } - return false; - } + /** + * Add a shape to the composite. + */ + public void addShape(final GeoMembershipShape shape) { + shapes.add(shape); + } - @Override - public GeoPoint[] getEdgePoints() - { - return shapes.get(0).getEdgePoints(); - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - for (GeoMembershipShape shape : shapes) { - if (shape.intersects(p,notablePoints,bounds)) - return true; - } - return false; + @Override + public boolean isWithin(final Vector point) { + //System.err.println("Checking whether point "+point+" is within Composite"); + for (GeoMembershipShape shape : shapes) { + if (shape.isWithin(point)) { + //System.err.println(" Point is within "+shape); + return true; + } } + return false; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - for (GeoMembershipShape shape : shapes) { - bounds = shape.getBounds(bounds); - } - return bounds; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoCompositeMembershipShape)) - return false; - GeoCompositeMembershipShape other = (GeoCompositeMembershipShape)o; - if (other.shapes.size() != shapes.size()) - return false; - - for (int i = 0; i < shapes.size(); i++) { - if (!other.shapes.get(i).equals(shapes.get(i))) - return false; - } + @Override + public boolean isWithin(final double x, final double y, final double z) { + for (GeoMembershipShape shape : shapes) { + if (shape.isWithin(x, y, z)) return true; } + return false; + } - @Override - public int hashCode() { - return shapes.hashCode();//TODO cache - } + @Override + public GeoPoint[] getEdgePoints() { + return shapes.get(0).getEdgePoints(); + } - @Override - public String toString() { - return "GeoCompositeMembershipShape: {" + shapes + '}'; + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + for (GeoMembershipShape shape : shapes) { + if (shape.intersects(p, notablePoints, bounds)) + return true; } + return false; + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + for (GeoMembershipShape shape : shapes) { + bounds = shape.getBounds(bounds); + } + return bounds; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoCompositeMembershipShape)) + return false; + GeoCompositeMembershipShape other = (GeoCompositeMembershipShape) o; + if (other.shapes.size() != shapes.size()) + return false; + + for (int i = 0; i < shapes.size(); i++) { + if (!other.shapes.get(i).equals(shapes.get(i))) + return false; + } + return true; + } + + @Override + public int hashCode() { + return shapes.hashCode();//TODO cache + } + + @Override + public String toString() { + return "GeoCompositeMembershipShape: {" + shapes + '}'; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java index 20575b52071..8c4b8ed5fa9 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygon.java @@ -21,250 +21,251 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.List; -/** GeoConvexPolygon objects are generic building blocks of more complex structures. -* The only restrictions on these objects are: (1) they must be convex; (2) they must have -* a maximum extent no larger than PI. Violating either one of these limits will -* cause the logic to fail. -*/ -public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembershipShape -{ - protected final List points; - protected final BitSet isInternalEdges; +/** + * GeoConvexPolygon objects are generic building blocks of more complex structures. + * The only restrictions on these objects are: (1) they must be convex; (2) they must have + * a maximum extent no larger than PI. Violating either one of these limits will + * cause the logic to fail. + */ +public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembershipShape { + protected final List points; + protected final BitSet isInternalEdges; - protected SidedPlane[] edges = null; - protected boolean[] internalEdges = null; - protected GeoPoint[][] notableEdgePoints = null; - - protected GeoPoint[] edgePoints = null; - - protected double fullDistance = 0.0; - - /** Create a convex polygon from a list of points. The first point must be on the - * external edge. - */ - public GeoConvexPolygon(final List pointList) { - this.points = pointList; - this.isInternalEdges = null; - donePoints(false); - } + protected SidedPlane[] edges = null; + protected boolean[] internalEdges = null; + protected GeoPoint[][] notableEdgePoints = null; - /** Create a convex polygon from a list of points, keeping track of which boundaries - * are internal. This is used when creating a polygon as a building block for another shape. - */ - public GeoConvexPolygon(final List pointList, final BitSet internalEdgeFlags, final boolean returnEdgeInternal) { - this.points = pointList; - this.isInternalEdges = internalEdgeFlags; - donePoints(returnEdgeInternal); - } - - /** Create a convex polygon, with a starting latitude and longitude. - * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} - */ - public GeoConvexPolygon(final double startLatitude, final double startLongitude) - { - points = new ArrayList(); - isInternalEdges = new BitSet(); - // Argument checking - if (startLatitude > Math.PI * 0.5 || startLatitude < -Math.PI * 0.5) - throw new IllegalArgumentException("Latitude out of range"); - if (startLongitude < -Math.PI || startLongitude > Math.PI) - throw new IllegalArgumentException("Longitude out of range"); - - final GeoPoint p = new GeoPoint(startLatitude, startLongitude); - points.add(p); - } - - /** Add a point to the polygon. - * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} - *@param latitude is the latitude of the next point. - *@param longitude is the longitude of the next point. - *@param isInternalEdge is true if the edge just added should be considered "internal", and not - * intersected as part of the intersects() operation. - */ - public void addPoint(final double latitude, final double longitude, final boolean isInternalEdge) { - // Argument checking - if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) - throw new IllegalArgumentException("Latitude out of range"); - if (longitude < -Math.PI || longitude > Math.PI) - throw new IllegalArgumentException("Longitude out of range"); - - final GeoPoint p = new GeoPoint(latitude, longitude); - isInternalEdges.set(points.size(),isInternalEdge); - points.add(p); - } + protected GeoPoint[] edgePoints = null; - /** Finish the polygon, by connecting the last added point with the starting point. - */ - public void donePoints(final boolean isInternalReturnEdge) { - // If fewer than 3 points, can't do it. - if (points.size() < 3) - throw new IllegalArgumentException("Polygon needs at least three points."); - // Time to construct the planes. If the polygon is truly convex, then any adjacent point - // to a segment can provide an interior measurement. - edges = new SidedPlane[points.size()]; - notableEdgePoints = new GeoPoint[points.size()][]; - internalEdges = new boolean[points.size()]; - for (int i = 0; i < points.size(); i++) { - final GeoPoint start = points.get(i); - final boolean isInternalEdge = (isInternalEdges!=null?(i == isInternalEdges.size()?isInternalReturnEdge:isInternalEdges.get(i)):false); - final GeoPoint end = points.get(legalIndex(i+1)); - final double distance = start.arcDistance(end); - if (distance > fullDistance) - fullDistance = distance; - final GeoPoint check = points.get(legalIndex(i+2)); - final SidedPlane sp = new SidedPlane(check,start,end); - //System.out.println("Created edge "+sp+" using start="+start+" end="+end+" check="+check); - edges[i] = sp; - notableEdgePoints[i] = new GeoPoint[]{start,end}; - internalEdges[i] = isInternalEdge; - } - createCenterPoint(); - } - - protected void createCenterPoint() { - // In order to naively confirm that the polygon is convex, I would need to - // check every edge, and verify that every point (other than the edge endpoints) - // is within the edge's sided plane. This is an order n^2 operation. That's still - // not wrong, though, because everything else about polygons has a similar cost. - for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) { - final SidedPlane edge = edges[edgeIndex]; - for (int pointIndex =0; pointIndex < points.size(); pointIndex++) { - if (pointIndex != edgeIndex && pointIndex != legalIndex(edgeIndex+1)) { - if (!edge.isWithin(points.get(pointIndex))) - throw new IllegalArgumentException("Polygon is not convex: Point "+points.get(pointIndex)+" Edge "+edge); - } - } - } - edgePoints = new GeoPoint[]{points.get(0)}; - } - - protected int legalIndex(int index) { - while (index >= points.size()) - index -= points.size(); - return index; - } - - @Override - public boolean isWithin(final Vector point) - { - for (final SidedPlane edge : edges) { - if (!edge.isWithin(point)) - return false; - } - return true; - } + protected double fullDistance = 0.0; - @Override - public boolean isWithin(final double x, final double y, final double z) - { - for (final SidedPlane edge : edges) { - if (!edge.isWithin(x,y,z)) - return false; - } - return true; - } + /** + * Create a convex polygon from a list of points. The first point must be on the + * external edge. + */ + public GeoConvexPolygon(final List pointList) { + this.points = pointList; + this.isInternalEdges = null; + donePoints(false); + } - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; + /** + * Create a convex polygon from a list of points, keeping track of which boundaries + * are internal. This is used when creating a polygon as a building block for another shape. + */ + public GeoConvexPolygon(final List pointList, final BitSet internalEdgeFlags, final boolean returnEdgeInternal) { + this.points = pointList; + this.isInternalEdges = internalEdgeFlags; + donePoints(returnEdgeInternal); + } + + /** + * Create a convex polygon, with a starting latitude and longitude. + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} + */ + public GeoConvexPolygon(final double startLatitude, final double startLongitude) { + points = new ArrayList(); + isInternalEdges = new BitSet(); + // Argument checking + if (startLatitude > Math.PI * 0.5 || startLatitude < -Math.PI * 0.5) + throw new IllegalArgumentException("Latitude out of range"); + if (startLongitude < -Math.PI || startLongitude > Math.PI) + throw new IllegalArgumentException("Longitude out of range"); + + final GeoPoint p = new GeoPoint(startLatitude, startLongitude); + points.add(p); + } + + /** + * Add a point to the polygon. + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} + * + * @param latitude is the latitude of the next point. + * @param longitude is the longitude of the next point. + * @param isInternalEdge is true if the edge just added should be considered "internal", and not + * intersected as part of the intersects() operation. + */ + public void addPoint(final double latitude, final double longitude, final boolean isInternalEdge) { + // Argument checking + if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) + throw new IllegalArgumentException("Latitude out of range"); + if (longitude < -Math.PI || longitude > Math.PI) + throw new IllegalArgumentException("Longitude out of range"); + + final GeoPoint p = new GeoPoint(latitude, longitude); + isInternalEdges.set(points.size(), isInternalEdge); + points.add(p); + } + + /** + * Finish the polygon, by connecting the last added point with the starting point. + */ + public void donePoints(final boolean isInternalReturnEdge) { + // If fewer than 3 points, can't do it. + if (points.size() < 3) + throw new IllegalArgumentException("Polygon needs at least three points."); + // Time to construct the planes. If the polygon is truly convex, then any adjacent point + // to a segment can provide an interior measurement. + edges = new SidedPlane[points.size()]; + notableEdgePoints = new GeoPoint[points.size()][]; + internalEdges = new boolean[points.size()]; + for (int i = 0; i < points.size(); i++) { + final GeoPoint start = points.get(i); + final boolean isInternalEdge = (isInternalEdges != null ? (i == isInternalEdges.size() ? isInternalReturnEdge : isInternalEdges.get(i)) : false); + final GeoPoint end = points.get(legalIndex(i + 1)); + final double distance = start.arcDistance(end); + if (distance > fullDistance) + fullDistance = distance; + final GeoPoint check = points.get(legalIndex(i + 2)); + final SidedPlane sp = new SidedPlane(check, start, end); + //System.out.println("Created edge "+sp+" using start="+start+" end="+end+" check="+check); + edges[i] = sp; + notableEdgePoints[i] = new GeoPoint[]{start, end}; + internalEdges[i] = isInternalEdge; } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - //System.err.println("Checking for polygon intersection with plane "+p+"..."); - for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) { - final SidedPlane edge = edges[edgeIndex]; - final GeoPoint[] points = this.notableEdgePoints[edgeIndex]; - if (!internalEdges[edgeIndex]) { - //System.err.println(" non-internal edge "+edge); - // Edges flagged as 'internal only' are excluded from the matching - // Construct boundaries - final Membership[] membershipBounds = new Membership[edges.length-1]; - int count = 0; - for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) { - if (otherIndex != edgeIndex) { - membershipBounds[count++] = edges[otherIndex]; - } - } - if (edge.intersects(p,notablePoints, points, bounds,membershipBounds)) { - //System.err.println(" intersects!"); - return true; - } - } + createCenterPoint(); + } + + protected void createCenterPoint() { + // In order to naively confirm that the polygon is convex, I would need to + // check every edge, and verify that every point (other than the edge endpoints) + // is within the edge's sided plane. This is an order n^2 operation. That's still + // not wrong, though, because everything else about polygons has a similar cost. + for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) { + final SidedPlane edge = edges[edgeIndex]; + for (int pointIndex = 0; pointIndex < points.size(); pointIndex++) { + if (pointIndex != edgeIndex && pointIndex != legalIndex(edgeIndex + 1)) { + if (!edge.isWithin(points.get(pointIndex))) + throw new IllegalArgumentException("Polygon is not convex: Point " + points.get(pointIndex) + " Edge " + edge); } - //System.err.println(" no intersection"); + } + } + edgePoints = new GeoPoint[]{points.get(0)}; + } + + protected int legalIndex(int index) { + while (index >= points.size()) + index -= points.size(); + return index; + } + + @Override + public boolean isWithin(final Vector point) { + for (final SidedPlane edge : edges) { + if (!edge.isWithin(point)) return false; } + return true; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - bounds = super.getBounds(bounds); + @Override + public boolean isWithin(final double x, final double y, final double z) { + for (final SidedPlane edge : edges) { + if (!edge.isWithin(x, y, z)) + return false; + } + return true; + } - // Add all the points - for (final GeoPoint point : points) { - bounds.addPoint(point); + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + //System.err.println("Checking for polygon intersection with plane "+p+"..."); + for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) { + final SidedPlane edge = edges[edgeIndex]; + final GeoPoint[] points = this.notableEdgePoints[edgeIndex]; + if (!internalEdges[edgeIndex]) { + //System.err.println(" non-internal edge "+edge); + // Edges flagged as 'internal only' are excluded from the matching + // Construct boundaries + final Membership[] membershipBounds = new Membership[edges.length - 1]; + int count = 0; + for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) { + if (otherIndex != edgeIndex) { + membershipBounds[count++] = edges[otherIndex]; + } } - - // Add planes with membership. - for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) { - final SidedPlane edge = edges[edgeIndex]; - // Construct boundaries - final Membership[] membershipBounds = new Membership[edges.length-1]; - int count = 0; - for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) { - if (otherIndex != edgeIndex) { - membershipBounds[count++] = edges[otherIndex]; - } - } - edge.recordBounds(bounds,membershipBounds); + if (edge.intersects(p, notablePoints, points, bounds, membershipBounds)) { + //System.err.println(" intersects!"); + return true; } + } + } + //System.err.println(" no intersection"); + return false; + } - if (fullDistance >= Math.PI) { - // We can't reliably assume that bounds did its longitude calculation right, so we force it to be unbounded. - bounds.noLongitudeBound(); - } - return bounds; + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + bounds = super.getBounds(bounds); + + // Add all the points + for (final GeoPoint point : points) { + bounds.addPoint(point); } - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoConvexPolygon)) - return false; - GeoConvexPolygon other = (GeoConvexPolygon)o; - if (other.points.size() != points.size()) - return false; - - for (int i = 0; i < points.size(); i++) { - if (!other.points.get(i).equals(points.get(i))) - return false; + // Add planes with membership. + for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) { + final SidedPlane edge = edges[edgeIndex]; + // Construct boundaries + final Membership[] membershipBounds = new Membership[edges.length - 1]; + int count = 0; + for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) { + if (otherIndex != edgeIndex) { + membershipBounds[count++] = edges[otherIndex]; } - return true; + } + edge.recordBounds(bounds, membershipBounds); } - @Override - public int hashCode() { - return points.hashCode(); + if (fullDistance >= Math.PI) { + // We can't reliably assume that bounds did its longitude calculation right, so we force it to be unbounded. + bounds.noLongitudeBound(); } + return bounds; + } - @Override - public String toString() { - StringBuilder edgeString = new StringBuilder("{"); - for (int i = 0; i < edges.length; i++) { - edgeString.append(edges[i]).append(" internal? ").append(internalEdges[i]).append("; "); - } - edgeString.append("}"); - return "GeoConvexPolygon: {points=" + points + " edges="+edgeString+"}"; + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoConvexPolygon)) + return false; + GeoConvexPolygon other = (GeoConvexPolygon) o; + if (other.points.size() != points.size()) + return false; + + for (int i = 0; i < points.size(); i++) { + if (!other.points.get(i).equals(points.get(i))) + return false; } + return true; + } + + @Override + public int hashCode() { + return points.hashCode(); + } + + @Override + public String toString() { + StringBuilder edgeString = new StringBuilder("{"); + for (int i = 0; i < edges.length; i++) { + edgeString.append(edges[i]).append(" internal? ").append(internalEdges[i]).append("; "); + } + edgeString.append("}"); + return "GeoConvexPolygon: {points=" + points + " edges=" + edgeString + "}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java index d69f37ed8dc..a12cb8a6c3e 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateHorizontalLine.java @@ -17,188 +17,185 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Degenerate bounding box limited on two sides (left lon, right lon). -* The left-right maximum extent for this shape is PI; for anything larger, use -* GeoWideDegenerateHorizontalLine. -*/ -public class GeoDegenerateHorizontalLine extends GeoBBoxBase -{ - public final double latitude; - public final double leftLon; - public final double rightLon; - - public final GeoPoint LHC; - public final GeoPoint RHC; - - public final Plane plane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; +/** + * Degenerate bounding box limited on two sides (left lon, right lon). + * The left-right maximum extent for this shape is PI; for anything larger, use + * GeoWideDegenerateHorizontalLine. + */ +public class GeoDegenerateHorizontalLine extends GeoBBoxBase { + public final double latitude; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] planePoints; + public final GeoPoint LHC; + public final GeoPoint RHC; - public final GeoPoint centerPoint; - public final GeoPoint[] edgePoints; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon) - { - // Argument checking - if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) - throw new IllegalArgumentException("Latitude out of range"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent > Math.PI) - throw new IllegalArgumentException("Width of rectangle too great"); + public final Plane plane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.latitude = latitude; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinLatitude = Math.sin(latitude); - final double cosLatitude = Math.cos(latitude); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the two points - this.LHC = new GeoPoint(sinLatitude,sinLeftLon,cosLatitude,cosLeftLon); - this.RHC = new GeoPoint(sinLatitude,sinRightLon,cosLatitude,cosRightLon); - - this.plane = new Plane(sinLatitude); + public final GeoPoint[] planePoints; - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinLatitude,sinMiddleLon,cosLatitude,cosMiddleLon); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final GeoPoint centerPoint; + public final GeoPoint[] edgePoints; - this.planePoints = new GeoPoint[]{LHC,RHC}; - - this.edgePoints = new GeoPoint[]{centerPoint}; + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} + */ + public GeoDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon) { + // Argument checking + if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) + throw new IllegalArgumentException("Latitude out of range"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; } + if (extent > Math.PI) + throw new IllegalArgumentException("Width of rectangle too great"); - @Override - public GeoBBox expand(final double angle) - { - double newTopLat = latitude + angle; - double newBottomLat = latitude - angle; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); - } + this.latitude = latitude; + this.leftLon = leftLon; + this.rightLon = rightLon; - @Override - public boolean isWithin(final Vector point) - { - return plane.evaluateIsZero(point) && - leftPlane.isWithin(point) && - rightPlane.isWithin(point); - } + final double sinLatitude = Math.sin(latitude); + final double cosLatitude = Math.cos(latitude); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return plane.evaluateIsZero(x,y,z) && - leftPlane.isWithin(x,y,z) && - rightPlane.isWithin(x,y,z); - } + // Now build the two points + this.LHC = new GeoPoint(sinLatitude, sinLeftLon, cosLatitude, cosLeftLon); + this.RHC = new GeoPoint(sinLatitude, sinRightLon, cosLatitude, cosRightLon); - @Override - public double getRadius() - { - double topAngle = centerPoint.arcDistance(RHC); - double bottomAngle = centerPoint.arcDistance(LHC); - return Math.max(topAngle,bottomAngle); - } + this.plane = new Plane(sinLatitude); - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(plane,notablePoints,planePoints,bounds,leftPlane,rightPlane); - } + this.centerPoint = new GeoPoint(sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(latitude).addLongitudeSlice(leftLon,rightLon); - return bounds; - } + this.planePoints = new GeoPoint[]{LHC, RHC}; - @Override - public int getRelationship(final GeoShape path) { - if (path.intersects(plane,planePoints,leftPlane,rightPlane)) - return OVERLAPS; + this.edgePoints = new GeoPoint[]{centerPoint}; + } - if (path.isWithin(centerPoint)) - return CONTAINS; + @Override + public GeoBBox expand(final double angle) { + double newTopLat = latitude + angle; + double newBottomLat = latitude - angle; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } - return DISJOINT; - } + @Override + public boolean isWithin(final Vector point) { + return plane.evaluateIsZero(point) && + leftPlane.isWithin(point) && + rightPlane.isWithin(point); + } - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoDegenerateHorizontalLine)) - return false; - GeoDegenerateHorizontalLine other = (GeoDegenerateHorizontalLine)o; - return other.LHC.equals(LHC) && other.RHC.equals(RHC); - } + @Override + public boolean isWithin(final double x, final double y, final double z) { + return plane.evaluateIsZero(x, y, z) && + leftPlane.isWithin(x, y, z) && + rightPlane.isWithin(x, y, z); + } - @Override - public int hashCode() { - int result = LHC.hashCode(); - result = 31 * result + RHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoDegenerateHorizontalLine: {latitude="+latitude+"("+latitude*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightLon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } + @Override + public double getRadius() { + double topAngle = centerPoint.arcDistance(RHC); + double bottomAngle = centerPoint.arcDistance(LHC); + return Math.max(topAngle, bottomAngle); + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(plane, notablePoints, planePoints, bounds, leftPlane, rightPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(latitude).addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + if (path.intersects(plane, planePoints, leftPlane, rightPlane)) + return OVERLAPS; + + if (path.isWithin(centerPoint)) + return CONTAINS; + + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoDegenerateHorizontalLine)) + return false; + GeoDegenerateHorizontalLine other = (GeoDegenerateHorizontalLine) o; + return other.LHC.equals(LHC) && other.RHC.equals(RHC); + } + + @Override + public int hashCode() { + int result = LHC.hashCode(); + result = 31 * result + RHC.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoDegenerateHorizontalLine: {latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java index 3041ed74a00..fddfa0f2353 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLatitudeZone.java @@ -17,130 +17,125 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** This GeoBBox represents an area rectangle of one specific latitude with -* no longitude bounds. -*/ -public class GeoDegenerateLatitudeZone extends GeoBBoxBase -{ - public final double latitude; - - public final double sinLatitude; - public final Plane plane; - public final GeoPoint interiorPoint; - public final GeoPoint[] edgePoints; - public final static GeoPoint[] planePoints = new GeoPoint[0]; - - public GeoDegenerateLatitudeZone(final double latitude) - { - this.latitude = latitude; - - this.sinLatitude = Math.sin(latitude); - double cosLatitude = Math.cos(latitude); - this.plane = new Plane(sinLatitude); - // Compute an interior point. - interiorPoint = new GeoPoint(cosLatitude,0.0,sinLatitude); - edgePoints = new GeoPoint[]{interiorPoint}; +/** + * This GeoBBox represents an area rectangle of one specific latitude with + * no longitude bounds. + */ +public class GeoDegenerateLatitudeZone extends GeoBBoxBase { + public final double latitude; + + public final double sinLatitude; + public final Plane plane; + public final GeoPoint interiorPoint; + public final GeoPoint[] edgePoints; + public final static GeoPoint[] planePoints = new GeoPoint[0]; + + public GeoDegenerateLatitudeZone(final double latitude) { + this.latitude = latitude; + + this.sinLatitude = Math.sin(latitude); + double cosLatitude = Math.cos(latitude); + this.plane = new Plane(sinLatitude); + // Compute an interior point. + interiorPoint = new GeoPoint(cosLatitude, 0.0, sinLatitude); + edgePoints = new GeoPoint[]{interiorPoint}; + } + + @Override + public GeoBBox expand(final double angle) { + double newTopLat = latitude + angle; + double newBottomLat = latitude - angle; + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + } + + @Override + public boolean isWithin(final Vector point) { + return Math.abs(point.z - this.sinLatitude) < 1e-10; + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return Math.abs(z - this.sinLatitude) < 1e-10; + } + + @Override + public double getRadius() { + return Math.PI; + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + // Totally arbitrary + return interiorPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(plane, notablePoints, planePoints, bounds); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noLongitudeBound().addLatitudeZone(latitude); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + // Second, the shortcut of seeing whether endpoints are in/out is not going to + // work with no area endpoints. So we rely entirely on intersections. + //System.out.println("Got here! latitude="+latitude+" path="+path); + + if (path.intersects(plane, planePoints)) { + return OVERLAPS; } - @Override - public GeoBBox expand(final double angle) - { - double newTopLat = latitude + angle; - double newBottomLat = latitude - angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + if (path.isWithin(interiorPoint)) { + return CONTAINS; } - @Override - public boolean isWithin(final Vector point) - { - return Math.abs(point.z - this.sinLatitude) < 1e-10; - } + return DISJOINT; + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return Math.abs(z - this.sinLatitude) < 1e-10; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoDegenerateLatitudeZone)) + return false; + GeoDegenerateLatitudeZone other = (GeoDegenerateLatitudeZone) o; + return other.latitude == latitude; + } - @Override - public double getRadius() - { - return Math.PI; - } + @Override + public int hashCode() { + long temp = Double.doubleToLongBits(latitude); + int result = (int) (temp ^ (temp >>> 32)); + return result; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - // Totally arbitrary - return interiorPoint; - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(plane,notablePoints,planePoints,bounds); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noLongitudeBound().addLatitudeZone(latitude); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - // Second, the shortcut of seeing whether endpoints are in/out is not going to - // work with no area endpoints. So we rely entirely on intersections. - //System.out.println("Got here! latitude="+latitude+" path="+path); - - if (path.intersects(plane,planePoints)) { - return OVERLAPS; - } - - if (path.isWithin(interiorPoint)) { - return CONTAINS; - } - - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoDegenerateLatitudeZone)) - return false; - GeoDegenerateLatitudeZone other = (GeoDegenerateLatitudeZone)o; - return other.latitude == latitude; - } - - @Override - public int hashCode() { - long temp = Double.doubleToLongBits(latitude); - int result = (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "GeoDegenerateLatitudeZone: {lat="+latitude+"("+latitude*180.0/Math.PI+")}"; - } + @Override + public String toString() { + return "GeoDegenerateLatitudeZone: {lat=" + latitude + "(" + latitude * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java index 377099e907f..393f0f16565 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateLongitudeSlice.java @@ -17,144 +17,141 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Degenerate longitude slice. -*/ -public class GeoDegenerateLongitudeSlice extends GeoBBoxBase -{ - public final double longitude; - - public final double sinLongitude; - public final double cosLongitude; - public final SidedPlane boundingPlane; - public final Plane plane; - public final GeoPoint interiorPoint; - public final GeoPoint[] edgePoints; +/** + * Degenerate longitude slice. + */ +public class GeoDegenerateLongitudeSlice extends GeoBBoxBase { + public final double longitude; - public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE,SOUTH_POLE}; + public final double sinLongitude; + public final double cosLongitude; + public final SidedPlane boundingPlane; + public final Plane plane; + public final GeoPoint interiorPoint; + public final GeoPoint[] edgePoints; - /** Accepts only values in the following ranges: lon: {@code -PI -> PI} */ - public GeoDegenerateLongitudeSlice(final double longitude) - { - // Argument checking - if (longitude < -Math.PI || longitude > Math.PI) - throw new IllegalArgumentException("Longitude out of range"); - this.longitude = longitude; - - this.sinLongitude = Math.sin(longitude); - this.cosLongitude = Math.cos(longitude); + public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE}; - this.plane = new Plane(cosLongitude, sinLongitude); - // We need a bounding plane too, which is perpendicular to the longitude plane and sided so that the point (0.0, longitude) is inside. - this.interiorPoint = new GeoPoint(cosLongitude, sinLongitude, 0.0); - this.boundingPlane = new SidedPlane(interiorPoint, -sinLongitude, cosLongitude); - this.edgePoints = new GeoPoint[]{interiorPoint}; + /** + * Accepts only values in the following ranges: lon: {@code -PI -> PI} + */ + public GeoDegenerateLongitudeSlice(final double longitude) { + // Argument checking + if (longitude < -Math.PI || longitude > Math.PI) + throw new IllegalArgumentException("Longitude out of range"); + this.longitude = longitude; + + this.sinLongitude = Math.sin(longitude); + this.cosLongitude = Math.cos(longitude); + + this.plane = new Plane(cosLongitude, sinLongitude); + // We need a bounding plane too, which is perpendicular to the longitude plane and sided so that the point (0.0, longitude) is inside. + this.interiorPoint = new GeoPoint(cosLongitude, sinLongitude, 0.0); + this.boundingPlane = new SidedPlane(interiorPoint, -sinLongitude, cosLongitude); + this.edgePoints = new GeoPoint[]{interiorPoint}; + } + + @Override + public GeoBBox expand(final double angle) { + // Figuring out when we escalate to a special case requires some prefiguring + double newLeftLon = longitude - angle; + double newRightLon = longitude + angle; + double currentLonSpan = 2.0 * angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; } + return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); + } - @Override - public GeoBBox expand(final double angle) - { - // Figuring out when we escalate to a special case requires some prefiguring - double newLeftLon = longitude - angle; - double newRightLon = longitude + angle; - double currentLonSpan = 2.0 * angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5,-Math.PI * 0.5,newLeftLon,newRightLon); - } + @Override + public boolean isWithin(final Vector point) { + return plane.evaluateIsZero(point) && + boundingPlane.isWithin(point); + } - @Override - public boolean isWithin(final Vector point) - { - return plane.evaluateIsZero(point) && - boundingPlane.isWithin(point); - } + @Override + public boolean isWithin(final double x, final double y, final double z) { + return plane.evaluateIsZero(x, y, z) && + boundingPlane.isWithin(x, y, z); + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return plane.evaluateIsZero(x,y,z) && - boundingPlane.isWithin(x,y,z); - } + @Override + public double getRadius() { + return Math.PI * 0.5; + } - @Override - public double getRadius() - { - return Math.PI * 0.5; - } + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return interiorPoint; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return interiorPoint; - } + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(plane,notablePoints,planePoints,bounds,boundingPlane); - } + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(plane, notablePoints, planePoints, bounds, boundingPlane); + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noTopLatitudeBound().noBottomLatitudeBound(); - bounds.addLongitudeSlice(longitude,longitude); - return bounds; - } + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noTopLatitudeBound().noBottomLatitudeBound(); + bounds.addLongitudeSlice(longitude, longitude); + return bounds; + } - @Override - public int getRelationship(final GeoShape path) { - // Look for intersections. - if (path.intersects(plane,planePoints,boundingPlane)) - return OVERLAPS; + @Override + public int getRelationship(final GeoShape path) { + // Look for intersections. + if (path.intersects(plane, planePoints, boundingPlane)) + return OVERLAPS; - if (path.isWithin(interiorPoint)) - return CONTAINS; + if (path.isWithin(interiorPoint)) + return CONTAINS; - return DISJOINT; - } + return DISJOINT; + } - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoDegenerateLongitudeSlice)) - return false; - GeoDegenerateLongitudeSlice other = (GeoDegenerateLongitudeSlice)o; - return other.longitude == longitude; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoDegenerateLongitudeSlice)) + return false; + GeoDegenerateLongitudeSlice other = (GeoDegenerateLongitudeSlice) o; + return other.longitude == longitude; + } - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(longitude); - result = (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "GeoDegenerateLongitudeSlice: {longitude="+longitude+"("+longitude*180.0/Math.PI+")}"; - } + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(longitude); + result = (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public String toString() { + return "GeoDegenerateLongitudeSlice: {longitude=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java index 9606bccdd4d..9e96adbf818 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegeneratePoint.java @@ -17,156 +17,175 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** This class represents a degenerate point bounding box. -* It is not a simple GeoPoint because we must have the latitude and longitude. -*/ -public class GeoDegeneratePoint extends GeoPoint implements GeoBBox -{ - public final double latitude; - public final double longitude; - public final GeoPoint[] edgePoints; - - public GeoDegeneratePoint(final double lat, final double lon) - { - super(lat,lon); - this.latitude = lat; - this.longitude = lon; - this.edgePoints = new GeoPoint[]{this}; - } +/** + * This class represents a degenerate point bounding box. + * It is not a simple GeoPoint because we must have the latitude and longitude. + */ +public class GeoDegeneratePoint extends GeoPoint implements GeoBBox { + public final double latitude; + public final double longitude; + public final GeoPoint[] edgePoints; - /** Expand box by specified angle. - *@param angle is the angle amount to expand the GeoBBox by. - *@return a new GeoBBox. - */ - @Override - public GeoBBox expand(final double angle) { - final double newTopLat = latitude + angle; - final double newBottomLat = latitude - angle; - final double newLeftLon = longitude - angle; - final double newRightLon = longitude + angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); - } + public GeoDegeneratePoint(final double lat, final double lon) { + super(lat, lon); + this.latitude = lat; + this.longitude = lon; + this.edgePoints = new GeoPoint[]{this}; + } - /** Return a sample point that is on the edge of the shape. - *@return an interior point. - */ - @Override - public GeoPoint[] getEdgePoints() { - return edgePoints; - } - - /** Assess whether a plane, within the provided bounds, intersects - * with the shape. - *@param plane is the plane to assess for intersection with the shape's edges or - * bounding curves. - *@param bounds are a set of bounds that define an area that an - * intersection must be within in order to qualify (provided by a GeoArea). - *@return true if there's such an intersection, false if not. - */ - @Override - public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) { - if (plane.evaluate(this) == 0.0) - return false; - - for (Membership m : bounds) { - if (!m.isWithin(this)) - return false; - } - return true; - } + /** + * Expand box by specified angle. + * + * @param angle is the angle amount to expand the GeoBBox by. + * @return a new GeoBBox. + */ + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = latitude + angle; + final double newBottomLat = latitude - angle; + final double newLeftLon = longitude - angle; + final double newRightLon = longitude + angle; + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) { - if (bounds == null) - bounds = new Bounds(); - bounds.addPoint(latitude,longitude); - return bounds; - } + /** + * Return a sample point that is on the edge of the shape. + * + * @return an interior point. + */ + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - /** Equals */ - @Override - public boolean equals(Object o) { - if (!(o instanceof GeoDegeneratePoint)) - return false; - GeoDegeneratePoint other = (GeoDegeneratePoint)o; - return other.latitude == latitude && other.longitude == longitude; - } + /** + * Assess whether a plane, within the provided bounds, intersects + * with the shape. + * + * @param plane is the plane to assess for intersection with the shape's edges or + * bounding curves. + * @param bounds are a set of bounds that define an area that an + * intersection must be within in order to qualify (provided by a GeoArea). + * @return true if there's such an intersection, false if not. + */ + @Override + public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) { + if (plane.evaluate(this) == 0.0) + return false; - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(latitude); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(longitude); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "GeoDegeneratePoint: {lat="+latitude+"("+latitude*180.0/Math.PI+"), lon="+longitude+"("+longitude*180.0/Math.PI+")}"; - } - - /** Check if a point is within this shape. - *@param point is the point to check. - *@return true if the point is within this shape - */ - @Override - public boolean isWithin(final Vector point) { - return isWithin(point.x,point.y,point.z); + for (Membership m : bounds) { + if (!m.isWithin(this)) + return false; } + return true; + } - /** Check if a point is within this shape. - *@param x is x coordinate of point to check. - *@param y is y coordinate of point to check. - *@param z is z coordinate of point to check. - *@return true if the point is within this shape - */ - @Override - public boolean isWithin(final double x, final double y, final double z) { - return x == this.x && y == this.y && z == this.z; - } + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addPoint(latitude, longitude); + return bounds; + } - /** Returns the radius of a circle into which the GeoSizeable area can - * be inscribed. - *@return the radius. - */ - @Override - public double getRadius() { - return 0.0; - } + /** + * Equals + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoDegeneratePoint)) + return false; + GeoDegeneratePoint other = (GeoDegeneratePoint) o; + return other.latitude == latitude && other.longitude == longitude; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return this; - } + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(latitude); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(longitude); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } - /** Find the spatial relationship between a shape and the current geo area. - * Note: return value is how the GeoShape relates to the GeoArea, not the - * other way around. For example, if this GeoArea is entirely within the - * shape, then CONTAINS should be returned. If the shape is entirely enclosed - * by this GeoArea, then WITHIN should be returned. - *@param shape is the shape to consider. - *@return the relationship, from the perspective of the shape. - */ - @Override - public int getRelationship(final GeoShape shape) { - if (shape.isWithin(this)) - return CONTAINS; + @Override + public String toString() { + return "GeoDegeneratePoint: {lat=" + latitude + "(" + latitude * 180.0 / Math.PI + "), lon=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}"; + } - return DISJOINT; - } + /** + * Check if a point is within this shape. + * + * @param point is the point to check. + * @return true if the point is within this shape + */ + @Override + public boolean isWithin(final Vector point) { + return isWithin(point.x, point.y, point.z); + } + + /** + * Check if a point is within this shape. + * + * @param x is x coordinate of point to check. + * @param y is y coordinate of point to check. + * @param z is z coordinate of point to check. + * @return true if the point is within this shape + */ + @Override + public boolean isWithin(final double x, final double y, final double z) { + return x == this.x && y == this.y && z == this.z; + } + + /** + * Returns the radius of a circle into which the GeoSizeable area can + * be inscribed. + * + * @return the radius. + */ + @Override + public double getRadius() { + return 0.0; + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return this; + } + + /** + * Find the spatial relationship between a shape and the current geo area. + * Note: return value is how the GeoShape relates to the GeoArea, not the + * other way around. For example, if this GeoArea is entirely within the + * shape, then CONTAINS should be returned. If the shape is entirely enclosed + * by this GeoArea, then WITHIN should be returned. + * + * @param shape is the shape to consider. + * @return the relationship, from the perspective of the shape. + */ + @Override + public int getRelationship(final GeoShape shape) { + if (shape.isWithin(this)) + return CONTAINS; + + return DISJOINT; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java index 293708b2947..4b2bb13163b 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDegenerateVerticalLine.java @@ -17,191 +17,188 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Degenerate bounding box limited on two sides (top lat, bottom lat). -*/ -public class GeoDegenerateVerticalLine extends GeoBBoxBase -{ - public final double topLat; - public final double bottomLat; - public final double longitude; - - public final GeoPoint UHC; - public final GeoPoint LHC; - - public final SidedPlane topPlane; - public final SidedPlane bottomPlane; - public final SidedPlane boundingPlane; - public final Plane plane; +/** + * Degenerate bounding box limited on two sides (top lat, bottom lat). + */ +public class GeoDegenerateVerticalLine extends GeoBBoxBase { + public final double topLat; + public final double bottomLat; + public final double longitude; - public final GeoPoint[] planePoints; - - public final GeoPoint centerPoint; - public final GeoPoint[] edgePoints; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, longitude: {@code -PI -> PI} */ - public GeoDegenerateVerticalLine(final double topLat, final double bottomLat, final double longitude) - { - // Argument checking - if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Top latitude out of range"); - if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Bottom latitude out of range"); - if (topLat < bottomLat) - throw new IllegalArgumentException("Top latitude less than bottom latitude"); - if (longitude < -Math.PI || longitude > Math.PI) - throw new IllegalArgumentException("Longitude out of range"); + public final GeoPoint UHC; + public final GeoPoint LHC; - this.topLat = topLat; - this.bottomLat = bottomLat; - this.longitude = longitude; - - final double sinTopLat = Math.sin(topLat); - final double cosTopLat = Math.cos(topLat); - final double sinBottomLat = Math.sin(bottomLat); - final double cosBottomLat = Math.cos(bottomLat); - final double sinLongitude = Math.sin(longitude); - final double cosLongitude = Math.cos(longitude); - - // Now build the two points - this.UHC = new GeoPoint(sinTopLat,sinLongitude,cosTopLat,cosLongitude); - this.LHC = new GeoPoint(sinBottomLat,sinLongitude,cosBottomLat,cosLongitude); - - this.plane = new Plane(cosLongitude,sinLongitude); - - final double middleLat = (topLat + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - final double cosMiddleLat = Math.cos(middleLat); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinLongitude,cosMiddleLat,cosLongitude); + public final SidedPlane topPlane; + public final SidedPlane bottomPlane; + public final SidedPlane boundingPlane; + public final Plane plane; - this.topPlane = new SidedPlane(centerPoint,sinTopLat); - this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat); + public final GeoPoint[] planePoints; - this.boundingPlane = new SidedPlane(centerPoint,-sinLongitude,cosLongitude); + public final GeoPoint centerPoint; + public final GeoPoint[] edgePoints; - this.planePoints = new GeoPoint[]{UHC,LHC}; + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, longitude: {@code -PI -> PI} + */ + public GeoDegenerateVerticalLine(final double topLat, final double bottomLat, final double longitude) { + // Argument checking + if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Top latitude out of range"); + if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Bottom latitude out of range"); + if (topLat < bottomLat) + throw new IllegalArgumentException("Top latitude less than bottom latitude"); + if (longitude < -Math.PI || longitude > Math.PI) + throw new IllegalArgumentException("Longitude out of range"); - this.edgePoints = new GeoPoint[]{centerPoint}; + this.topLat = topLat; + this.bottomLat = bottomLat; + this.longitude = longitude; + + final double sinTopLat = Math.sin(topLat); + final double cosTopLat = Math.cos(topLat); + final double sinBottomLat = Math.sin(bottomLat); + final double cosBottomLat = Math.cos(bottomLat); + final double sinLongitude = Math.sin(longitude); + final double cosLongitude = Math.cos(longitude); + + // Now build the two points + this.UHC = new GeoPoint(sinTopLat, sinLongitude, cosTopLat, cosLongitude); + this.LHC = new GeoPoint(sinBottomLat, sinLongitude, cosBottomLat, cosLongitude); + + this.plane = new Plane(cosLongitude, sinLongitude); + + final double middleLat = (topLat + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + final double cosMiddleLat = Math.cos(middleLat); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinLongitude, cosMiddleLat, cosLongitude); + + this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + + this.boundingPlane = new SidedPlane(centerPoint, -sinLongitude, cosLongitude); + + this.planePoints = new GeoPoint[]{UHC, LHC}; + + this.edgePoints = new GeoPoint[]{centerPoint}; + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = bottomLat - angle; + double newLeftLon = longitude - angle; + double newRightLon = longitude + angle; + double currentLonSpan = 2.0 * angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return plane.evaluateIsZero(point) && + boundingPlane.isWithin(point) && + topPlane.isWithin(point) && + bottomPlane.isWithin(point); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return plane.evaluateIsZero(x, y, z) && + boundingPlane.isWithin(x, y, z) && + topPlane.isWithin(x, y, z) && + bottomPlane.isWithin(x, y, z); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double topAngle = centerPoint.arcDistance(UHC); + final double bottomAngle = centerPoint.arcDistance(LHC); + return Math.max(topAngle, bottomAngle); + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(plane, notablePoints, planePoints, bounds, boundingPlane, topPlane, bottomPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat) + .addLongitudeSlice(longitude, longitude); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" relationship to "+path); + if (path.intersects(plane, planePoints, boundingPlane, topPlane, bottomPlane)) { + //System.err.println(" overlaps"); + return OVERLAPS; } - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = bottomLat - angle; - double newLeftLon = longitude - angle; - double newRightLon = longitude + angle; - double currentLonSpan = 2.0 * angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + if (path.isWithin(centerPoint)) { + //System.err.println(" contains"); + return CONTAINS; } - @Override - public boolean isWithin(final Vector point) - { - return plane.evaluateIsZero(point) && - boundingPlane.isWithin(point) && - topPlane.isWithin(point) && - bottomPlane.isWithin(point); - } + //System.err.println(" disjoint"); + return DISJOINT; + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return plane.evaluateIsZero(x,y,z) && - boundingPlane.isWithin(x,y,z) && - topPlane.isWithin(x,y,z) && - bottomPlane.isWithin(x,y,z); - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoDegenerateVerticalLine)) + return false; + GeoDegenerateVerticalLine other = (GeoDegenerateVerticalLine) o; + return other.UHC.equals(UHC) && other.LHC.equals(LHC); + } - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double topAngle = centerPoint.arcDistance(UHC); - final double bottomAngle = centerPoint.arcDistance(LHC); - return Math.max(topAngle,bottomAngle); - } + @Override + public int hashCode() { + int result = UHC.hashCode(); + result = 31 * result + LHC.hashCode(); + return result; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(plane,notablePoints,planePoints,bounds,boundingPlane,topPlane,bottomPlane); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat) - .addLongitudeSlice(longitude,longitude); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" relationship to "+path); - if (path.intersects(plane,planePoints,boundingPlane,topPlane,bottomPlane)) { - //System.err.println(" overlaps"); - return OVERLAPS; - } - - if (path.isWithin(centerPoint)) { - //System.err.println(" contains"); - return CONTAINS; - } - - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoDegenerateVerticalLine)) - return false; - GeoDegenerateVerticalLine other = (GeoDegenerateVerticalLine)o; - return other.UHC.equals(UHC) && other.LHC.equals(LHC); - } - - @Override - public int hashCode() { - int result = UHC.hashCode(); - result = 31 * result + LHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoDegenerateVerticalLine: {longitude="+longitude+"("+longitude*180.0/Math.PI+"), toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}"; - } + @Override + public String toString() { + return "GeoDegenerateVerticalLine: {longitude=" + longitude + "(" + longitude * 180.0 / Math.PI + "), toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistance.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistance.java index 4d73b3a32dc..c7ef1ba5670 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistance.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistance.java @@ -17,116 +17,135 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Generic geo-distance-capable shape class description. An implementer -* of this interface is capable of computing the described "distance" values, -* which are meant to provide both actual distance values, as well as -* distance estimates that can be computed more cheaply. -*/ +/** + * Generic geo-distance-capable shape class description. An implementer + * of this interface is capable of computing the described "distance" values, + * which are meant to provide both actual distance values, as well as + * distance estimates that can be computed more cheaply. + */ public interface GeoDistance extends Membership { - /** Compute this shape's normal "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param point is the point to compute the distance to. - *@return the normal distance, defined as the perpendicular distance from - * from the point to one of the shape's bounding plane. Normal - * distances can therefore typically only go up to PI/2, except - * when they represent the sum of a sequence of normal distances. - */ - public double computeNormalDistance(GeoPoint point); + /** + * Compute this shape's normal "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param point is the point to compute the distance to. + * @return the normal distance, defined as the perpendicular distance from + * from the point to one of the shape's bounding plane. Normal + * distances can therefore typically only go up to PI/2, except + * when they represent the sum of a sequence of normal distances. + */ + public double computeNormalDistance(GeoPoint point); - /** Compute this shape's normal "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param x is the point's unit x coordinate (using U.S. convention). - *@param y is the point's unit y coordinate (using U.S. convention). - *@param z is the point's unit z coordinate (using U.S. convention). - *@return the normal distance, defined as the perpendicular distance from - * from the point to one of the shape's bounding plane. Normal - * distances can therefore typically only go up to PI/2, except - * when they represent the sum of a sequence of normal distances. - */ - public double computeNormalDistance(double x, double y, double z); - - /** Compute the square of this shape's normal "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param point is the point to compute the distance to. - *@return the square of the normal distance, defined as the perpendicular - * distance from - * from the point to one of the shape's bounding plane. Normal - * distances can therefore typically only go up to PI/2, except - * when they represent the sum of a sequence of normal distances. - */ - public double computeSquaredNormalDistance(GeoPoint point); - - /** Compute the square of this shape's normal "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param x is the point's unit x coordinate (using U.S. convention). - *@param y is the point's unit y coordinate (using U.S. convention). - *@param z is the point's unit z coordinate (using U.S. convention). - *@return the square of the normal distance, defined as the perpendicular - * distance from - * from the point to one of the shape's bounding plane. Normal - * distances can therefore typically only go up to PI/2, except - * when they represent the sum of a sequence of normal distances. - */ - public double computeSquaredNormalDistance(double x, double y, double z); + /** + * Compute this shape's normal "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param x is the point's unit x coordinate (using U.S. convention). + * @param y is the point's unit y coordinate (using U.S. convention). + * @param z is the point's unit z coordinate (using U.S. convention). + * @return the normal distance, defined as the perpendicular distance from + * from the point to one of the shape's bounding plane. Normal + * distances can therefore typically only go up to PI/2, except + * when they represent the sum of a sequence of normal distances. + */ + public double computeNormalDistance(double x, double y, double z); - /** Compute this shape's linear "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param point is the point to compute the distance to. - *@return the linear (or chord) distance, defined as the distance from - * from the point to the nearest point on the unit sphere and on one of the shape's - * bounding planes. Linear distances can therefore typically go up to PI, - * except when they represent the sum of a sequence of linear distances. - */ - public double computeLinearDistance(GeoPoint point); + /** + * Compute the square of this shape's normal "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param point is the point to compute the distance to. + * @return the square of the normal distance, defined as the perpendicular + * distance from + * from the point to one of the shape's bounding plane. Normal + * distances can therefore typically only go up to PI/2, except + * when they represent the sum of a sequence of normal distances. + */ + public double computeSquaredNormalDistance(GeoPoint point); - /** Compute this shape's linear "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param x is the point's unit x coordinate (using U.S. convention). - *@param y is the point's unit y coordinate (using U.S. convention). - *@param z is the point's unit z coordinate (using U.S. convention). - *@return the linear (or chord) distance, defined as the distance from - * from the point to the nearest point on the unit sphere and on one of the shape's - * bounding planes. Linear distances can therefore typically go up to PI, - * except when they represent the sum of a sequence of linear distances. - */ - public double computeLinearDistance(double x, double y, double z); + /** + * Compute the square of this shape's normal "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param x is the point's unit x coordinate (using U.S. convention). + * @param y is the point's unit y coordinate (using U.S. convention). + * @param z is the point's unit z coordinate (using U.S. convention). + * @return the square of the normal distance, defined as the perpendicular + * distance from + * from the point to one of the shape's bounding plane. Normal + * distances can therefore typically only go up to PI/2, except + * when they represent the sum of a sequence of normal distances. + */ + public double computeSquaredNormalDistance(double x, double y, double z); - /** Compute the square of this shape's linear "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param point is the point to compute the distance to. - *@return the square of the linear (or chord) distance, defined as the - * distance from - * from the point to the nearest point on the unit sphere and on one of the shape's - * bounding planes. Linear distances can therefore typically go up to PI, - * except when they represent the sum of a sequence of linear distances. - */ - public double computeSquaredLinearDistance(GeoPoint point); + /** + * Compute this shape's linear "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param point is the point to compute the distance to. + * @return the linear (or chord) distance, defined as the distance from + * from the point to the nearest point on the unit sphere and on one of the shape's + * bounding planes. Linear distances can therefore typically go up to PI, + * except when they represent the sum of a sequence of linear distances. + */ + public double computeLinearDistance(GeoPoint point); - /** Compute the square of this shape's linear "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - *@param x is the point's unit x coordinate (using U.S. convention). - *@param y is the point's unit y coordinate (using U.S. convention). - *@param z is the point's unit z coordinate (using U.S. convention). - *@return the square of the linear (or chord) distance, defined as the distance from - * from the point to the nearest point on the unit sphere and on one of the shape's - * bounding planes. Linear distances can therefore typically go up to PI, - * except when they represent the sum of a sequence of linear distances. - */ - public double computeSquaredLinearDistance(double x, double y, double z); + /** + * Compute this shape's linear "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param x is the point's unit x coordinate (using U.S. convention). + * @param y is the point's unit y coordinate (using U.S. convention). + * @param z is the point's unit z coordinate (using U.S. convention). + * @return the linear (or chord) distance, defined as the distance from + * from the point to the nearest point on the unit sphere and on one of the shape's + * bounding planes. Linear distances can therefore typically go up to PI, + * except when they represent the sum of a sequence of linear distances. + */ + public double computeLinearDistance(double x, double y, double z); + + /** + * Compute the square of this shape's linear "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param point is the point to compute the distance to. + * @return the square of the linear (or chord) distance, defined as the + * distance from + * from the point to the nearest point on the unit sphere and on one of the shape's + * bounding planes. Linear distances can therefore typically go up to PI, + * except when they represent the sum of a sequence of linear distances. + */ + public double computeSquaredLinearDistance(GeoPoint point); + + /** + * Compute the square of this shape's linear "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + * + * @param x is the point's unit x coordinate (using U.S. convention). + * @param y is the point's unit y coordinate (using U.S. convention). + * @param z is the point's unit z coordinate (using U.S. convention). + * @return the square of the linear (or chord) distance, defined as the distance from + * from the point to the nearest point on the unit sphere and on one of the shape's + * bounding planes. Linear distances can therefore typically go up to PI, + * except when they represent the sum of a sequence of linear distances. + */ + public double computeSquaredLinearDistance(double x, double y, double z); + + /** + * Compute a true, accurate, great-circle distance to a point. + * Double.MAX_VALUE indicates a point is outside of the shape. + * + * @param point is the point. + * @return the distance. + */ + public double computeArcDistance(GeoPoint point); - /** Compute a true, accurate, great-circle distance to a point. - * Double.MAX_VALUE indicates a point is outside of the shape. - *@param point is the point. - *@return the distance. - */ - public double computeArcDistance(GeoPoint point); - } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistanceShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistanceShape.java index 85f50093b69..0e41c8e9992 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistanceShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoDistanceShape.java @@ -17,7 +17,8 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Distance shapes have capabilities of both geohashing and distance +/** + * Distance shapes have capabilities of both geohashing and distance * computation (which also includes point membership determination). */ public interface GeoDistanceShape extends GeoMembershipShape, GeoDistance { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java index d2362d253ab..28eadac7a2a 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLatitudeZone.java @@ -17,185 +17,180 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** This GeoBBox represents an area rectangle limited only in latitude. -*/ -public class GeoLatitudeZone extends GeoBBoxBase -{ - public final double topLat; - public final double bottomLat; - public final double cosTopLat; - public final double cosBottomLat; - public final SidedPlane topPlane; - public final SidedPlane bottomPlane; - public final GeoPoint interiorPoint; - public final static GeoPoint[] planePoints = new GeoPoint[0]; +/** + * This GeoBBox represents an area rectangle limited only in latitude. + */ +public class GeoLatitudeZone extends GeoBBoxBase { + public final double topLat; + public final double bottomLat; + public final double cosTopLat; + public final double cosBottomLat; + public final SidedPlane topPlane; + public final SidedPlane bottomPlane; + public final GeoPoint interiorPoint; + public final static GeoPoint[] planePoints = new GeoPoint[0]; - // We need two additional points because a latitude zone's boundaries don't intersect. This is a very - // special case that most GeoBBox's do not have. - public final GeoPoint topBoundaryPoint; - public final GeoPoint bottomBoundaryPoint; - - // Edge points - public final GeoPoint[] edgePoints; - - public GeoLatitudeZone(final double topLat, final double bottomLat) - { - this.topLat = topLat; - this.bottomLat = bottomLat; - - final double sinTopLat = Math.sin(topLat); - final double sinBottomLat = Math.sin(bottomLat); - this.cosTopLat = Math.cos(topLat); - this.cosBottomLat = Math.cos(bottomLat); - - // Construct sample points, so we get our sidedness right - final Vector topPoint = new Vector(0.0,0.0,sinTopLat); - final Vector bottomPoint = new Vector(0.0,0.0,sinBottomLat); + // We need two additional points because a latitude zone's boundaries don't intersect. This is a very + // special case that most GeoBBox's do not have. + public final GeoPoint topBoundaryPoint; + public final GeoPoint bottomBoundaryPoint; - // Compute an interior point. Pick one whose lat is between top and bottom. - final double middleLat = (topLat + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat); - this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat),0.0,sinTopLat); - this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat),0.0,sinBottomLat); - - this.topPlane = new SidedPlane(interiorPoint,sinTopLat); - this.bottomPlane = new SidedPlane(interiorPoint,sinBottomLat); - - this.edgePoints = new GeoPoint[]{topBoundaryPoint,bottomBoundaryPoint}; - } + // Edge points + public final GeoPoint[] edgePoints; - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = bottomLat - angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); - } + public GeoLatitudeZone(final double topLat, final double bottomLat) { + this.topLat = topLat; + this.bottomLat = bottomLat; - @Override - public boolean isWithin(final Vector point) - { - return topPlane.isWithin(point) && - bottomPlane.isWithin(point); - } + final double sinTopLat = Math.sin(topLat); + final double sinBottomLat = Math.sin(bottomLat); + this.cosTopLat = Math.cos(topLat); + this.cosBottomLat = Math.cos(bottomLat); - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return topPlane.isWithin(x,y,z) && - bottomPlane.isWithin(x,y,z); - } + // Construct sample points, so we get our sidedness right + final Vector topPoint = new Vector(0.0, 0.0, sinTopLat); + final Vector bottomPoint = new Vector(0.0, 0.0, sinBottomLat); - @Override - public double getRadius() - { - // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that - // would contain all the bounding box points, when starting in the "center". - if (topLat > 0.0 && bottomLat < 0.0) - return Math.PI; - double maxCosLat = cosTopLat; - if (maxCosLat < cosBottomLat) - maxCosLat = cosBottomLat; - return maxCosLat * Math.PI; - } + // Compute an interior point. Pick one whose lat is between top and bottom. + final double middleLat = (topLat + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat); + this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat), 0.0, sinTopLat); + this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 0.0, sinBottomLat); - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - // This is totally arbitrary and only a cartesian could agree with it. - return interiorPoint; - } + this.topPlane = new SidedPlane(interiorPoint, sinTopLat); + this.bottomPlane = new SidedPlane(interiorPoint, sinBottomLat); - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(topPlane,notablePoints,planePoints,bounds,bottomPlane) || - p.intersects(bottomPlane,notablePoints,planePoints,bounds,topPlane); - } + this.edgePoints = new GeoPoint[]{topBoundaryPoint, bottomBoundaryPoint}; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noLongitudeBound().addLatitudeZone(topLat).addLatitudeZone(bottomLat); - return bounds; - } + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = bottomLat - angle; + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + } - @Override - public int getRelationship(final GeoShape path) { - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - return OVERLAPS; + @Override + public boolean isWithin(final Vector point) { + return topPlane.isWithin(point) && + bottomPlane.isWithin(point); + } - final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint); - final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint); - - if (topBoundaryInsideShape && !bottomBoundaryInsideShape || - !topBoundaryInsideShape && bottomBoundaryInsideShape) - return OVERLAPS; - - final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape; + @Override + public boolean isWithin(final double x, final double y, final double z) { + return topPlane.isWithin(x, y, z) && + bottomPlane.isWithin(x, y, z); + } - if (insideRectangle == ALL_INSIDE && insideShape) - return OVERLAPS; + @Override + public double getRadius() { + // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that + // would contain all the bounding box points, when starting in the "center". + if (topLat > 0.0 && bottomLat < 0.0) + return Math.PI; + double maxCosLat = cosTopLat; + if (maxCosLat < cosBottomLat) + maxCosLat = cosBottomLat; + return maxCosLat * Math.PI; + } - // Second, the shortcut of seeing whether endpoints are in/out is not going to - // work with no area endpoints. So we rely entirely on intersections. + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + // This is totally arbitrary and only a cartesian could agree with it. + return interiorPoint; + } - if (path.intersects(topPlane,planePoints,bottomPlane) || - path.intersects(bottomPlane,planePoints,topPlane)) - return OVERLAPS; + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - // There is another case for latitude zones only. This is when the boundaries of the shape all fit - // within the zone, but the shape includes areas outside the zone crossing a pole. - // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary - // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If - // one such point is within, then OVERLAPS is the right answer. - - if (insideShape) - return CONTAINS; + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(topPlane, notablePoints, planePoints, bounds, bottomPlane) || + p.intersects(bottomPlane, notablePoints, planePoints, bounds, topPlane); + } - if (insideRectangle == ALL_INSIDE) - return WITHIN; + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noLongitudeBound().addLatitudeZone(topLat).addLatitudeZone(bottomLat); + return bounds; + } - return DISJOINT; - } + @Override + public int getRelationship(final GeoShape path) { + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) + return OVERLAPS; - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoLatitudeZone)) - return false; - GeoLatitudeZone other = (GeoLatitudeZone)o; - return other.topPlane.equals(topPlane) && other.bottomPlane.equals(bottomPlane); - } + final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint); + final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint); - @Override - public int hashCode() { - int result = topPlane.hashCode(); - result = 31 * result + bottomPlane.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoLatitudeZone: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}"; - } + if (topBoundaryInsideShape && !bottomBoundaryInsideShape || + !topBoundaryInsideShape && bottomBoundaryInsideShape) + return OVERLAPS; + + final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape; + + if (insideRectangle == ALL_INSIDE && insideShape) + return OVERLAPS; + + // Second, the shortcut of seeing whether endpoints are in/out is not going to + // work with no area endpoints. So we rely entirely on intersections. + + if (path.intersects(topPlane, planePoints, bottomPlane) || + path.intersects(bottomPlane, planePoints, topPlane)) + return OVERLAPS; + + // There is another case for latitude zones only. This is when the boundaries of the shape all fit + // within the zone, but the shape includes areas outside the zone crossing a pole. + // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary + // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If + // one such point is within, then OVERLAPS is the right answer. + + if (insideShape) + return CONTAINS; + + if (insideRectangle == ALL_INSIDE) + return WITHIN; + + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoLatitudeZone)) + return false; + GeoLatitudeZone other = (GeoLatitudeZone) o; + return other.topPlane.equals(topPlane) && other.bottomPlane.equals(bottomPlane); + } + + @Override + public int hashCode() { + int result = topPlane.hashCode(); + result = 31 * result + bottomPlane.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoLatitudeZone: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java index c9ca4d02a2b..cbb31e63135 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoLongitudeSlice.java @@ -17,187 +17,184 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box limited on left and right. -* The left-right maximum extent for this shape is PI; for anything larger, use -* GeoWideLongitudeSlice. -*/ -public class GeoLongitudeSlice extends GeoBBoxBase -{ - public final double leftLon; - public final double rightLon; - - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; - - public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE,SOUTH_POLE}; - - public final GeoPoint centerPoint; +/** + * Bounding box limited on left and right. + * The left-right maximum extent for this shape is PI; for anything larger, use + * GeoWideLongitudeSlice. + */ +public class GeoLongitudeSlice extends GeoBBoxBase { + public final double leftLon; + public final double rightLon; - public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; - - /** Accepts only values in the following ranges: lon: {@code -PI -> PI} */ - public GeoLongitudeSlice(final double leftLon, double rightLon) - { - // Argument checking - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent > Math.PI) - throw new IllegalArgumentException("Width of rectangle too great"); + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); + public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE}; - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - this.centerPoint = new GeoPoint(0.0,middleLon); - - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); - + public final GeoPoint centerPoint; + + public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + + /** + * Accepts only values in the following ranges: lon: {@code -PI -> PI} + */ + public GeoLongitudeSlice(final double leftLon, double rightLon) { + // Argument checking + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent > Math.PI) + throw new IllegalArgumentException("Width of rectangle too great"); + + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + this.centerPoint = new GeoPoint(0.0, middleLon); + + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + } + + @Override + public GeoBBox expand(final double angle) { + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return leftPlane.isWithin(point) && + rightPlane.isWithin(point); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return leftPlane.isWithin(x, y, z) && + rightPlane.isWithin(x, y, z); + } + + @Override + public double getRadius() { + // Compute the extent and divide by two + double extent = rightLon - leftLon; + if (extent < 0.0) + extent += Math.PI * 2.0; + return Math.max(Math.PI * 0.5, extent * 0.5); + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(leftPlane, notablePoints, planePoints, bounds, rightPlane) || + p.intersects(rightPlane, notablePoints, planePoints, bounds, leftPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noTopLatitudeBound().noBottomLatitudeBound(); + bounds.addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) + return OVERLAPS; + + final boolean insideShape = path.isWithin(NORTH_POLE); + + if (insideRectangle == ALL_INSIDE && insideShape) + return OVERLAPS; + + if (path.intersects(leftPlane, planePoints, rightPlane) || + path.intersects(rightPlane, planePoints, leftPlane)) { + return OVERLAPS; } - @Override - public GeoBBox expand(final double angle) - { - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5,-Math.PI * 0.5,newLeftLon,newRightLon); + if (insideRectangle == ALL_INSIDE) { + return WITHIN; } - @Override - public boolean isWithin(final Vector point) - { - return leftPlane.isWithin(point) && - rightPlane.isWithin(point); + if (insideShape) { + return CONTAINS; } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return leftPlane.isWithin(x,y,z) && - rightPlane.isWithin(x,y,z); - } + return DISJOINT; + } - @Override - public double getRadius() - { - // Compute the extent and divide by two - double extent = rightLon - leftLon; - if (extent < 0.0) - extent += Math.PI * 2.0; - return Math.max(Math.PI * 0.5, extent * 0.5); - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoLongitudeSlice)) + return false; + GeoLongitudeSlice other = (GeoLongitudeSlice) o; + return other.leftLon == leftLon && other.rightLon == rightLon; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(leftLon); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(rightLon); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(leftPlane,notablePoints,planePoints,bounds,rightPlane) || - p.intersects(rightPlane,notablePoints,planePoints,bounds,leftPlane); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noTopLatitudeBound().noBottomLatitudeBound(); - bounds.addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - return OVERLAPS; - - final boolean insideShape = path.isWithin(NORTH_POLE); - - if (insideRectangle == ALL_INSIDE && insideShape) - return OVERLAPS; - - if (path.intersects(leftPlane,planePoints,rightPlane) || - path.intersects(rightPlane,planePoints,leftPlane)) { - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) { - return WITHIN; - } - - if (insideShape) { - return CONTAINS; - } - - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoLongitudeSlice)) - return false; - GeoLongitudeSlice other = (GeoLongitudeSlice)o; - return other.leftLon == leftLon && other.rightLon == rightLon; - } - - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(leftLon); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(rightLon); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "GeoLongitudeSlice: {leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } + @Override + public String toString() { + return "GeoLongitudeSlice: {leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoMembershipShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoMembershipShape.java index d06b871f891..e31e7114007 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoMembershipShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoMembershipShape.java @@ -17,7 +17,8 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Membership shapes have capabilities of both geohashing and membership +/** + * Membership shapes have capabilities of both geohashing and membership * determination. */ public interface GeoMembershipShape extends GeoShape, Membership { diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java index e2601fe3e8a..42c7cbd7d49 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthLatitudeZone.java @@ -17,163 +17,158 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** This GeoBBox represents an area rectangle limited only in south latitude. -*/ -public class GeoNorthLatitudeZone extends GeoBBoxBase -{ - public final double bottomLat; - public final double cosBottomLat; - public final SidedPlane bottomPlane; - public final GeoPoint interiorPoint; - public final static GeoPoint[] planePoints = new GeoPoint[0]; +/** + * This GeoBBox represents an area rectangle limited only in south latitude. + */ +public class GeoNorthLatitudeZone extends GeoBBoxBase { + public final double bottomLat; + public final double cosBottomLat; + public final SidedPlane bottomPlane; + public final GeoPoint interiorPoint; + public final static GeoPoint[] planePoints = new GeoPoint[0]; - public final GeoPoint bottomBoundaryPoint; - - // Edge points - public final GeoPoint[] edgePoints; - - public GeoNorthLatitudeZone(final double bottomLat) - { - this.bottomLat = bottomLat; - - final double sinBottomLat = Math.sin(bottomLat); - this.cosBottomLat = Math.cos(bottomLat); - - // Construct sample points, so we get our sidedness right - final Vector bottomPoint = new Vector(0.0,0.0,sinBottomLat); + public final GeoPoint bottomBoundaryPoint; - // Compute an interior point. Pick one whose lat is between top and bottom. - final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat); - this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat),0.0,sinBottomLat); - - this.bottomPlane = new SidedPlane(interiorPoint,sinBottomLat); - - this.edgePoints = new GeoPoint[]{bottomBoundaryPoint}; - } + // Edge points + public final GeoPoint[] edgePoints; - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = Math.PI * 0.5; - final double newBottomLat = bottomLat - angle; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); - } + public GeoNorthLatitudeZone(final double bottomLat) { + this.bottomLat = bottomLat; - @Override - public boolean isWithin(final Vector point) - { - return - bottomPlane.isWithin(point); - } + final double sinBottomLat = Math.sin(bottomLat); + this.cosBottomLat = Math.cos(bottomLat); - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return - bottomPlane.isWithin(x,y,z); - } + // Construct sample points, so we get our sidedness right + final Vector bottomPoint = new Vector(0.0, 0.0, sinBottomLat); - @Override - public double getRadius() - { - // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that - // would contain all the bounding box points, when starting in the "center". - if (bottomLat < 0.0) - return Math.PI; - double maxCosLat = cosBottomLat; - return maxCosLat * Math.PI; - } + // Compute an interior point. Pick one whose lat is between top and bottom. + final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat); + this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 0.0, sinBottomLat); - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return interiorPoint; - } + this.bottomPlane = new SidedPlane(interiorPoint, sinBottomLat); - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return - p.intersects(bottomPlane,notablePoints,planePoints,bounds); - } + this.edgePoints = new GeoPoint[]{bottomBoundaryPoint}; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noLongitudeBound().noTopLatitudeBound().addLatitudeZone(bottomLat); - return bounds; - } + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = Math.PI * 0.5; + final double newBottomLat = bottomLat - angle; + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + } - @Override - public int getRelationship(final GeoShape path) { - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - return OVERLAPS; + @Override + public boolean isWithin(final Vector point) { + return + bottomPlane.isWithin(point); + } - final boolean insideShape = path.isWithin(bottomBoundaryPoint); + @Override + public boolean isWithin(final double x, final double y, final double z) { + return + bottomPlane.isWithin(x, y, z); + } - if (insideRectangle == ALL_INSIDE && insideShape) - return OVERLAPS; + @Override + public double getRadius() { + // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that + // would contain all the bounding box points, when starting in the "center". + if (bottomLat < 0.0) + return Math.PI; + double maxCosLat = cosBottomLat; + return maxCosLat * Math.PI; + } - // Second, the shortcut of seeing whether endpoints are in/out is not going to - // work with no area endpoints. So we rely entirely on intersections. + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return interiorPoint; + } - if ( - path.intersects(bottomPlane,planePoints)) - return OVERLAPS; + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - // There is another case for latitude zones only. This is when the boundaries of the shape all fit - // within the zone, but the shape includes areas outside the zone crossing a pole. - // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary - // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If - // one such point is within, then OVERLAPS is the right answer. - - if (insideShape) - return CONTAINS; + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return + p.intersects(bottomPlane, notablePoints, planePoints, bounds); + } - if (insideRectangle == ALL_INSIDE) - return WITHIN; + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noLongitudeBound().noTopLatitudeBound().addLatitudeZone(bottomLat); + return bounds; + } - return DISJOINT; - } + @Override + public int getRelationship(final GeoShape path) { + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) + return OVERLAPS; - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoNorthLatitudeZone)) - return false; - GeoNorthLatitudeZone other = (GeoNorthLatitudeZone)o; - return other.bottomPlane.equals(bottomPlane); - } + final boolean insideShape = path.isWithin(bottomBoundaryPoint); - @Override - public int hashCode() { - int result = bottomPlane.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoNorthLatitudeZone: {bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}"; - } + if (insideRectangle == ALL_INSIDE && insideShape) + return OVERLAPS; + + // Second, the shortcut of seeing whether endpoints are in/out is not going to + // work with no area endpoints. So we rely entirely on intersections. + + if ( + path.intersects(bottomPlane, planePoints)) + return OVERLAPS; + + // There is another case for latitude zones only. This is when the boundaries of the shape all fit + // within the zone, but the shape includes areas outside the zone crossing a pole. + // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary + // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If + // one such point is within, then OVERLAPS is the right answer. + + if (insideShape) + return CONTAINS; + + if (insideRectangle == ALL_INSIDE) + return WITHIN; + + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoNorthLatitudeZone)) + return false; + GeoNorthLatitudeZone other = (GeoNorthLatitudeZone) o; + return other.bottomPlane.equals(bottomPlane); + } + + @Override + public int hashCode() { + int result = bottomPlane.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoNorthLatitudeZone: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java index 22ed0ae9e4f..deec4e21581 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoNorthRectangle.java @@ -17,235 +17,230 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box limited on three sides (bottom lat, left lon, right lon), including -* the north pole. -* The left-right maximum extent for this shape is PI; for anything larger, use -* GeoWideNorthRectangle. -*/ -public class GeoNorthRectangle extends GeoBBoxBase -{ - public final double bottomLat; - public final double leftLon; - public final double rightLon; - - public final double cosMiddleLat; - - public final GeoPoint LRHC; - public final GeoPoint LLHC; - - public final SidedPlane bottomPlane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; - - public final GeoPoint[] bottomPlanePoints; - public final GeoPoint[] leftPlanePoints; - public final GeoPoint[] rightPlanePoints; - - public final GeoPoint centerPoint; +/** + * Bounding box limited on three sides (bottom lat, left lon, right lon), including + * the north pole. + * The left-right maximum extent for this shape is PI; for anything larger, use + * GeoWideNorthRectangle. + */ +public class GeoNorthRectangle extends GeoBBoxBase { + public final double bottomLat; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoNorthRectangle(final double bottomLat, final double leftLon, double rightLon) - { - // Argument checking - if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Bottom latitude out of range"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent > Math.PI) - throw new IllegalArgumentException("Width of rectangle too great"); + public final double cosMiddleLat; - this.bottomLat = bottomLat; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinBottomLat = Math.sin(bottomLat); - final double cosBottomLat = Math.cos(bottomLat); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the points - this.LRHC = new GeoPoint(sinBottomLat,sinRightLon,cosBottomLat,cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat,sinLeftLon,cosBottomLat,cosLeftLon); - - final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.cosMiddleLat = Math.cos(middleLat); - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon); + public final GeoPoint LRHC; + public final GeoPoint LLHC; - this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final SidedPlane bottomPlane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC}; - this.leftPlanePoints = new GeoPoint[]{NORTH_POLE,LLHC}; - this.rightPlanePoints = new GeoPoint[]{NORTH_POLE,LRHC}; - + public final GeoPoint[] bottomPlanePoints; + public final GeoPoint[] leftPlanePoints; + public final GeoPoint[] rightPlanePoints; + + public final GeoPoint centerPoint; + + public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} + */ + public GeoNorthRectangle(final double bottomLat, final double leftLon, double rightLon) { + // Argument checking + if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Bottom latitude out of range"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent > Math.PI) + throw new IllegalArgumentException("Width of rectangle too great"); + + this.bottomLat = bottomLat; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinBottomLat = Math.sin(bottomLat); + final double cosBottomLat = Math.cos(bottomLat); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the points + this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + + final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.cosMiddleLat = Math.cos(middleLat); + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + + this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC}; + this.leftPlanePoints = new GeoPoint[]{NORTH_POLE, LLHC}; + this.rightPlanePoints = new GeoPoint[]{NORTH_POLE, LRHC}; + + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = Math.PI * 0.5; + final double newBottomLat = bottomLat - angle; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return + bottomPlane.isWithin(point) && + leftPlane.isWithin(point) && + rightPlane.isWithin(point); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return + bottomPlane.isWithin(x, y, z) && + leftPlane.isWithin(x, y, z) && + rightPlane.isWithin(x, y, z); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; + final double bottomAngle = centerPoint.arcDistance(LLHC); + return Math.max(centerAngle, bottomAngle); + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return + p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, leftPlane, rightPlane) || + p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, bottomPlane) || + p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, bottomPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noTopLatitudeBound().addLatitudeZone(bottomLat) + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" getrelationship with "+path); + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) { + //System.err.println(" some inside"); + return OVERLAPS; } - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = Math.PI * 0.5; - final double newBottomLat = bottomLat - angle; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + final boolean insideShape = path.isWithin(NORTH_POLE); + + if (insideRectangle == ALL_INSIDE && insideShape) { + //System.err.println(" inside of each other"); + return OVERLAPS; } - @Override - public boolean isWithin(final Vector point) - { - return - bottomPlane.isWithin(point) && - leftPlane.isWithin(point) && - rightPlane.isWithin(point); + if ( + path.intersects(bottomPlane, bottomPlanePoints, leftPlane, rightPlane) || + path.intersects(leftPlane, leftPlanePoints, bottomPlane, rightPlane) || + path.intersects(rightPlane, rightPlanePoints, leftPlane, bottomPlane)) { + //System.err.println(" edges intersect"); + return OVERLAPS; } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return - bottomPlane.isWithin(x,y,z) && - leftPlane.isWithin(x,y,z) && - rightPlane.isWithin(x,y,z); + if (insideRectangle == ALL_INSIDE) { + //System.err.println(" shape inside rectangle"); + return WITHIN; } - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; - final double bottomAngle = centerPoint.arcDistance(LLHC); - return Math.max(centerAngle,bottomAngle); - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; + if (insideShape) { + //System.err.println(" shape contains rectangle"); + return CONTAINS; } + //System.err.println(" disjoint"); + return DISJOINT; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return - p.intersects(bottomPlane,notablePoints,bottomPlanePoints,bounds,leftPlane,rightPlane) || - p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,rightPlane,bottomPlane) || - p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,leftPlane,bottomPlane); - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoNorthRectangle)) + return false; + GeoNorthRectangle other = (GeoNorthRectangle) o; + return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noTopLatitudeBound().addLatitudeZone(bottomLat) - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } + @Override + public int hashCode() { + int result = LLHC.hashCode(); + result = 31 * result + LRHC.hashCode(); + return result; + } - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" getrelationship with "+path); - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - { - //System.err.println(" some inside"); - return OVERLAPS; - } - - final boolean insideShape = path.isWithin(NORTH_POLE); - - if (insideRectangle == ALL_INSIDE && insideShape) { - //System.err.println(" inside of each other"); - return OVERLAPS; - } - - if ( - path.intersects(bottomPlane,bottomPlanePoints,leftPlane,rightPlane) || - path.intersects(leftPlane,leftPlanePoints,bottomPlane,rightPlane) || - path.intersects(rightPlane,rightPlanePoints,leftPlane,bottomPlane)) { - //System.err.println(" edges intersect"); - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) - { - //System.err.println(" shape inside rectangle"); - return WITHIN; - } - - if (insideShape) { - //System.err.println(" shape contains rectangle"); - return CONTAINS; - } - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoNorthRectangle)) - return false; - GeoNorthRectangle other = (GeoNorthRectangle)o; - return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); - } - - @Override - public int hashCode() { - int result = LLHC.hashCode(); - result = 31 * result + LRHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoNorthRectangle: {bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } + @Override + public String toString() { + return "GeoNorthRectangle: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java index 05614b96b43..ca8f582be9a 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPath.java @@ -20,671 +20,648 @@ package org.apache.lucene.spatial.spatial4j.geo3d; import java.util.ArrayList; import java.util.List; -/** GeoSearchableShape 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. -*/ -public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape -{ +/** + * GeoSearchableShape 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. + */ +public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape { + public final double cutoffAngle; + public final double cutoffOffset; + public final double originDistance; + public final double chordDistance; + + public final List points = new ArrayList(); + public final List segments = new ArrayList(); + + public GeoPoint[] edgePoints = null; + + public GeoPath(final double cutoffAngle) { + super(); + if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI * 0.5) + throw new IllegalArgumentException("Cutoff angle out of bounds"); + this.cutoffAngle = cutoffAngle; + final double cosAngle = Math.cos(cutoffAngle); + final double sinAngle = Math.sin(cutoffAngle); + // Cutoff offset is the linear distance given the angle + this.cutoffOffset = sinAngle; + this.originDistance = cosAngle; + // Compute chord distance + double xDiff = 1.0 - cosAngle; + this.chordDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle); + } + + public void addPoint(double lat, double lon) { + if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5) + throw new IllegalArgumentException("Latitude out of range"); + if (lon < -Math.PI || lon > Math.PI) + throw new IllegalArgumentException("Longitude out of range"); + final GeoPoint end = new GeoPoint(lat, lon); + if (points.size() > 0) { + final GeoPoint start = points.get(points.size() - 1).point; + final PathSegment ps = new PathSegment(start, end, cutoffOffset, cutoffAngle, chordDistance); + // Check for degeneracy; if the segment is degenerate, don't include the point + if (ps.isDegenerate()) + return; + segments.add(ps); + } else { + // First point. We compute the basic set of edgepoints here because we've got the lat and lon available. + // Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also. + double newLat = lat + cutoffAngle; + double newLon = lon; + if (newLat > Math.PI * 0.5) { + newLat = Math.PI - newLat; + newLon += Math.PI; + } + while (newLon > Math.PI) { + newLon -= Math.PI * 2.0; + } + final GeoPoint edgePoint = new GeoPoint(newLat, newLon); + this.edgePoints = new GeoPoint[]{edgePoint}; + } + final SegmentEndpoint se = new SegmentEndpoint(end, originDistance, cutoffOffset, cutoffAngle, chordDistance); + points.add(se); + } + + public void done() { + if (points.size() == 0) + throw new IllegalArgumentException("Path must have at least one point"); + if (segments.size() > 0) { + edgePoints = new GeoPoint[]{points.get(0).circlePlane.getSampleIntersectionPoint(segments.get(0).invertedStartCutoffPlane)}; + } + for (int i = 0; i < points.size(); i++) { + final SegmentEndpoint pathPoint = points.get(i); + Membership previousEndBound = null; + GeoPoint[] previousEndNotablePoints = null; + Membership nextStartBound = null; + GeoPoint[] nextStartNotablePoints = null; + if (i > 0) { + final PathSegment previousSegment = segments.get(i - 1); + previousEndBound = previousSegment.invertedEndCutoffPlane; + previousEndNotablePoints = previousSegment.endCutoffPlanePoints; + } + if (i < segments.size()) { + final PathSegment nextSegment = segments.get(i); + nextStartBound = nextSegment.invertedStartCutoffPlane; + nextStartNotablePoints = nextSegment.startCutoffPlanePoints; + } + pathPoint.setCutoffPlanes(previousEndNotablePoints, previousEndBound, nextStartNotablePoints, nextStartBound); + } + } + + /** + * Compute an estimate of "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + */ + @Override + public double computeNormalDistance(final GeoPoint point) { + // Algorithm: + // (1) If the point is within any of the segments along the path, return that value. + // (2) If the point is within any of the segment end circles along the path, return that value. + double currentDistance = 0.0; + for (PathSegment segment : segments) { + double distance = segment.pathNormalDistance(point); + if (distance != Double.MAX_VALUE) + return currentDistance + distance; + currentDistance += segment.fullNormalDistance; + } + + int segmentIndex = 0; + currentDistance = 0.0; + for (SegmentEndpoint endpoint : points) { + double distance = endpoint.pathNormalDistance(point); + if (distance != Double.MAX_VALUE) + return currentDistance + distance; + if (segmentIndex < segments.size()) + currentDistance += segments.get(segmentIndex++).fullNormalDistance; + } + + return Double.MAX_VALUE; + } + + /** + * Compute an estimate of "distance" to the GeoPoint. + * A return value of Double.MAX_VALUE should be returned for + * points outside of the shape. + */ + @Override + public double computeNormalDistance(final double x, final double y, final double z) { + return computeNormalDistance(new GeoPoint(x, y, z)); + } + + /** + * Compute a squared estimate of the "distance" to the + * GeoPoint. Double.MAX_VALUE indicates a point outside of the + * shape. + */ + @Override + public double computeSquaredNormalDistance(final GeoPoint point) { + double pd = computeNormalDistance(point); + if (pd == Double.MAX_VALUE) + return pd; + return pd * pd; + } + + /** + * Compute a squared estimate of the "distance" to the + * GeoPoint. Double.MAX_VALUE indicates a point outside of the + * shape. + */ + @Override + public double computeSquaredNormalDistance(final double x, final double y, final double z) { + return computeSquaredNormalDistance(new GeoPoint(x, y, z)); + } + + /** + * Compute a linear distance to the point. + */ + @Override + public double computeLinearDistance(final GeoPoint point) { + // Algorithm: + // (1) If the point is within any of the segments along the path, return that value. + // (2) If the point is within any of the segment end circles along the path, return that value. + double currentDistance = 0.0; + for (PathSegment segment : segments) { + double distance = segment.pathLinearDistance(point); + if (distance != Double.MAX_VALUE) + return currentDistance + distance; + currentDistance += segment.fullLinearDistance; + } + + int segmentIndex = 0; + currentDistance = 0.0; + for (SegmentEndpoint endpoint : points) { + double distance = endpoint.pathLinearDistance(point); + if (distance != Double.MAX_VALUE) + return currentDistance + distance; + if (segmentIndex < segments.size()) + currentDistance += segments.get(segmentIndex++).fullLinearDistance; + } + + return Double.MAX_VALUE; + } + + /** + * Compute a linear distance to the point. + */ + @Override + public double computeLinearDistance(final double x, final double y, final double z) { + return computeLinearDistance(new GeoPoint(x, y, z)); + } + + /** + * Compute a squared linear distance to the vector. + */ + @Override + public double computeSquaredLinearDistance(final GeoPoint point) { + double pd = computeLinearDistance(point); + if (pd == Double.MAX_VALUE) + return pd; + return pd * pd; + } + + /** + * Compute a squared linear distance to the vector. + */ + @Override + public double computeSquaredLinearDistance(final double x, final double y, final double z) { + return computeSquaredLinearDistance(new GeoPoint(x, y, z)); + } + + /** + * Compute a true, accurate, great-circle distance. + * Double.MAX_VALUE indicates a point is outside of the shape. + */ + @Override + public double computeArcDistance(final GeoPoint point) { + // Algorithm: + // (1) If the point is within any of the segments along the path, return that value. + // (2) If the point is within any of the segment end circles along the path, return that value. + double currentDistance = 0.0; + for (PathSegment segment : segments) { + double distance = segment.pathDistance(point); + if (distance != Double.MAX_VALUE) + return currentDistance + distance; + currentDistance += segment.fullDistance; + } + + int segmentIndex = 0; + currentDistance = 0.0; + for (SegmentEndpoint endpoint : points) { + double distance = endpoint.pathDistance(point); + if (distance != Double.MAX_VALUE) + return currentDistance + distance; + if (segmentIndex < segments.size()) + currentDistance += segments.get(segmentIndex++).fullDistance; + } + + return Double.MAX_VALUE; + } + + @Override + public boolean isWithin(final Vector point) { + for (SegmentEndpoint pathPoint : points) { + if (pathPoint.isWithin(point)) + return true; + } + for (PathSegment pathSegment : segments) { + if (pathSegment.isWithin(point)) + return true; + } + return false; + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + for (SegmentEndpoint pathPoint : points) { + 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. + + for (final SegmentEndpoint pathPoint : points) { + if (pathPoint.intersects(plane, notablePoints, bounds)) { + return true; + } + } + + for (final PathSegment pathSegment : segments) { + if (pathSegment.intersects(plane, notablePoints, bounds)) { + return true; + } + } + + return false; + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + bounds = super.getBounds(bounds); + // 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(bounds); + } + for (SegmentEndpoint pathPoint : points) { + pathPoint.getBounds(bounds); + } + return bounds; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoPath)) + return false; + GeoPath p = (GeoPath) o; + if (points.size() != p.points.size()) + return false; + if (cutoffAngle != p.cutoffAngle) + return false; + for (int i = 0; i < points.size(); i++) { + SegmentEndpoint point = points.get(i); + SegmentEndpoint point2 = p.points.get(i); + if (!point.equals(point2)) + return false; + } + return true; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(cutoffAngle); + result = (int) (temp ^ (temp >>> 32)); + result = 31 * result + points.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoPath: {width=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + "), points={" + points + "}}"; + } + + /** + * This is precalculated data for segment endpoint. + */ + public static class SegmentEndpoint { + public final GeoPoint point; + public final SidedPlane circlePlane; + public final double cutoffNormalDistance; public final double cutoffAngle; - public final double cutoffOffset; - public final double originDistance; public final double chordDistance; - - public final List points = new ArrayList(); - public final List segments = new ArrayList(); - - public GeoPoint[] edgePoints = null; - - public GeoPath(final double cutoffAngle) - { - super(); - if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI * 0.5) - throw new IllegalArgumentException("Cutoff angle out of bounds"); - this.cutoffAngle = cutoffAngle; - final double cosAngle = Math.cos(cutoffAngle); - final double sinAngle = Math.sin(cutoffAngle); - // Cutoff offset is the linear distance given the angle - this.cutoffOffset = sinAngle; - this.originDistance = cosAngle; - // Compute chord distance - double xDiff = 1.0 - cosAngle; - this.chordDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle); - } - - public void addPoint(double lat, double lon) - { - if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5) - throw new IllegalArgumentException("Latitude out of range"); - if (lon < -Math.PI || lon > Math.PI) - throw new IllegalArgumentException("Longitude out of range"); - final GeoPoint end = new GeoPoint(lat,lon); - if (points.size() > 0) { - final GeoPoint start = points.get(points.size()-1).point; - final PathSegment ps = new PathSegment(start,end,cutoffOffset,cutoffAngle,chordDistance); - // Check for degeneracy; if the segment is degenerate, don't include the point - if (ps.isDegenerate()) - return; - segments.add(ps); - } else { - // First point. We compute the basic set of edgepoints here because we've got the lat and lon available. - // Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also. - double newLat = lat + cutoffAngle; - double newLon = lon; - if (newLat > Math.PI * 0.5) { - newLat = Math.PI - newLat; - newLon += Math.PI; - } - while (newLon > Math.PI) { - newLon -= Math.PI * 2.0; - } - final GeoPoint edgePoint = new GeoPoint(newLat,newLon); - this.edgePoints = new GeoPoint[]{edgePoint}; - } - final SegmentEndpoint se = new SegmentEndpoint(end, originDistance, cutoffOffset, cutoffAngle, chordDistance); - points.add(se); - } - - public void done() { - if (points.size() == 0) - throw new IllegalArgumentException("Path must have at least one point"); - if (segments.size() > 0) { - edgePoints = new GeoPoint[]{points.get(0).circlePlane.getSampleIntersectionPoint(segments.get(0).invertedStartCutoffPlane)}; - } - for (int i = 0; i < points.size(); i++) { - final SegmentEndpoint pathPoint = points.get(i); - Membership previousEndBound = null; - GeoPoint[] previousEndNotablePoints = null; - Membership nextStartBound = null; - GeoPoint[] nextStartNotablePoints = null; - if (i > 0) { - final PathSegment previousSegment = segments.get(i-1); - previousEndBound = previousSegment.invertedEndCutoffPlane; - previousEndNotablePoints = previousSegment.endCutoffPlanePoints; - } - if (i < segments.size()) { - final PathSegment nextSegment = segments.get(i); - nextStartBound = nextSegment.invertedStartCutoffPlane; - nextStartNotablePoints = nextSegment.startCutoffPlanePoints; - } - pathPoint.setCutoffPlanes(previousEndNotablePoints,previousEndBound,nextStartNotablePoints,nextStartBound); - } - } - - /** Compute an estimate of "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - */ - @Override - public double computeNormalDistance(final GeoPoint point) - { - // Algorithm: - // (1) If the point is within any of the segments along the path, return that value. - // (2) If the point is within any of the segment end circles along the path, return that value. - double currentDistance = 0.0; - for (PathSegment segment : segments) { - double distance = segment.pathNormalDistance(point); - if (distance != Double.MAX_VALUE) - return currentDistance + distance; - currentDistance += segment.fullNormalDistance; - } + public Membership[] cutoffPlanes = null; + public GeoPoint[] notablePoints = null; - int segmentIndex = 0; - currentDistance = 0.0; - for (SegmentEndpoint endpoint : points) { - double distance = endpoint.pathNormalDistance(point); - if (distance != Double.MAX_VALUE) - return currentDistance + distance; - if (segmentIndex < segments.size()) - currentDistance += segments.get(segmentIndex++).fullNormalDistance; - } + public final static GeoPoint[] circlePoints = new GeoPoint[0]; + public SegmentEndpoint(final GeoPoint point, final double originDistance, final double cutoffOffset, final double cutoffAngle, final double chordDistance) { + this.point = point; + this.cutoffNormalDistance = cutoffOffset; + this.cutoffAngle = cutoffAngle; + this.chordDistance = chordDistance; + this.circlePlane = new SidedPlane(point, point, -originDistance); + } + + public void setCutoffPlanes(final GeoPoint[] previousEndNotablePoints, final Membership previousEndPlane, + final GeoPoint[] nextStartNotablePoints, final Membership nextStartPlane) { + if (previousEndNotablePoints == null && nextStartNotablePoints == null) { + cutoffPlanes = new Membership[0]; + notablePoints = new GeoPoint[0]; + } else if (previousEndNotablePoints != null && nextStartNotablePoints == null) { + cutoffPlanes = new Membership[]{previousEndPlane}; + notablePoints = previousEndNotablePoints; + } else if (previousEndNotablePoints == null && nextStartNotablePoints != null) { + cutoffPlanes = new Membership[]{nextStartPlane}; + notablePoints = nextStartNotablePoints; + } else { + cutoffPlanes = new Membership[]{previousEndPlane, nextStartPlane}; + notablePoints = new GeoPoint[previousEndNotablePoints.length + nextStartNotablePoints.length]; + int i = 0; + for (GeoPoint p : previousEndNotablePoints) { + notablePoints[i++] = p; + } + for (GeoPoint p : nextStartNotablePoints) { + notablePoints[i++] = p; + } + } + } + + public boolean isWithin(final Vector point) { + return circlePlane.isWithin(point); + } + + public boolean isWithin(final double x, final double y, final double z) { + return circlePlane.isWithin(x, y, z); + } + + public double pathDistance(final GeoPoint point) { + double dist = this.point.arcDistance(point); + if (dist > cutoffAngle) return Double.MAX_VALUE; + return dist; } - /** Compute an estimate of "distance" to the GeoPoint. - * A return value of Double.MAX_VALUE should be returned for - * points outside of the shape. - */ - @Override - public double computeNormalDistance(final double x, final double y, final double z) - { - return computeNormalDistance(new GeoPoint(x,y,z)); - } - - /** Compute a squared estimate of the "distance" to the - * GeoPoint. Double.MAX_VALUE indicates a point outside of the - * shape. - */ - @Override - public double computeSquaredNormalDistance(final GeoPoint point) - { - double pd = computeNormalDistance(point); - if (pd == Double.MAX_VALUE) - return pd; - return pd * pd; - } - - /** Compute a squared estimate of the "distance" to the - * GeoPoint. Double.MAX_VALUE indicates a point outside of the - * shape. - */ - @Override - public double computeSquaredNormalDistance(final double x, final double y, final double z) - { - return computeSquaredNormalDistance(new GeoPoint(x,y,z)); - } - - /** Compute a linear distance to the point. - */ - @Override - public double computeLinearDistance(final GeoPoint point) - { - // Algorithm: - // (1) If the point is within any of the segments along the path, return that value. - // (2) If the point is within any of the segment end circles along the path, return that value. - double currentDistance = 0.0; - for (PathSegment segment : segments) { - double distance = segment.pathLinearDistance(point); - if (distance != Double.MAX_VALUE) - return currentDistance + distance; - currentDistance += segment.fullLinearDistance; - } - - int segmentIndex = 0; - currentDistance = 0.0; - for (SegmentEndpoint endpoint : points) { - double distance = endpoint.pathLinearDistance(point); - if (distance != Double.MAX_VALUE) - return currentDistance + distance; - if (segmentIndex < segments.size()) - currentDistance += segments.get(segmentIndex++).fullLinearDistance; - } - + public double pathNormalDistance(final GeoPoint point) { + double dist = this.point.normalDistance(point); + if (dist > cutoffNormalDistance) return Double.MAX_VALUE; + return dist; } - /** Compute a linear distance to the point. - */ - @Override - public double computeLinearDistance(final double x, final double y, final double z) - { - return computeLinearDistance(new GeoPoint(x,y,z)); - } - - /** Compute a squared linear distance to the vector. - */ - @Override - public double computeSquaredLinearDistance(final GeoPoint point) - { - double pd = computeLinearDistance(point); - if (pd == Double.MAX_VALUE) - return pd; - return pd * pd; - } - - /** Compute a squared linear distance to the vector. - */ - @Override - public double computeSquaredLinearDistance(final double x, final double y, final double z) - { - return computeSquaredLinearDistance(new GeoPoint(x,y,z)); - } - - /** Compute a true, accurate, great-circle distance. - * Double.MAX_VALUE indicates a point is outside of the shape. - */ - @Override - public double computeArcDistance(final GeoPoint point) - { - // Algorithm: - // (1) If the point is within any of the segments along the path, return that value. - // (2) If the point is within any of the segment end circles along the path, return that value. - double currentDistance = 0.0; - for (PathSegment segment : segments) { - double distance = segment.pathDistance(point); - if (distance != Double.MAX_VALUE) - return currentDistance + distance; - currentDistance += segment.fullDistance; - } - - int segmentIndex = 0; - currentDistance = 0.0; - for (SegmentEndpoint endpoint : points) { - double distance = endpoint.pathDistance(point); - if (distance != Double.MAX_VALUE) - return currentDistance + distance; - if (segmentIndex < segments.size()) - currentDistance += segments.get(segmentIndex++).fullDistance; - } - + public double pathLinearDistance(final GeoPoint point) { + double dist = this.point.linearDistance(point); + if (dist > chordDistance) return Double.MAX_VALUE; + return dist; } - @Override - public boolean isWithin(final Vector point) - { - for (SegmentEndpoint pathPoint : points) { - if (pathPoint.isWithin(point)) - return true; - } - for (PathSegment pathSegment : segments) { - if (pathSegment.isWithin(point)) - return true; - } - return false; + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) { + return circlePlane.intersects(p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes); } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - for (SegmentEndpoint pathPoint : points) { - 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. - - for (final SegmentEndpoint pathPoint : points) { - if (pathPoint.intersects(plane, notablePoints, bounds)) { - return true; - } - } - - for (final PathSegment pathSegment : segments) { - if (pathSegment.intersects(plane, notablePoints, bounds)) { - return true; - } - } - - return false; - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - bounds = super.getBounds(bounds); - // 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(bounds); - } - for (SegmentEndpoint pathPoint : points) { - pathPoint.getBounds(bounds); - } - return bounds; + public void getBounds(Bounds bounds) { + bounds.addPoint(point); + circlePlane.recordBounds(bounds); } @Override public boolean equals(Object o) { - if (!(o instanceof GeoPath)) - return false; - GeoPath p = (GeoPath)o; - if (points.size() != p.points.size()) - return false; - if (cutoffAngle != p.cutoffAngle) - return false; - for (int i = 0; i < points.size(); i++) { - SegmentEndpoint point = points.get(i); - SegmentEndpoint point2 = p.points.get(i); - if (!point.equals(point2)) - return false; - } - return true; + if (!(o instanceof SegmentEndpoint)) + return false; + SegmentEndpoint other = (SegmentEndpoint) o; + return point.equals(other.point); } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(cutoffAngle); - result = (int) (temp ^ (temp >>> 32)); - result = 31 * result + points.hashCode(); - return result; + return point.hashCode(); } @Override public String toString() { - return "GeoPath: {width="+cutoffAngle+"("+cutoffAngle*180.0/Math.PI+"), points={"+points+"}}"; + return point.toString(); } - - /** This is precalculated data for segment endpoint. - */ - public static class SegmentEndpoint - { - public final GeoPoint point; - public final SidedPlane circlePlane; - public final double cutoffNormalDistance; - public final double cutoffAngle; - public final double chordDistance; - public Membership[] cutoffPlanes = null; - public GeoPoint[] notablePoints = null; - - public final static GeoPoint[] circlePoints = new GeoPoint[0]; - - public SegmentEndpoint(final GeoPoint point, final double originDistance, final double cutoffOffset, final double cutoffAngle, final double chordDistance) - { - this.point = point; - this.cutoffNormalDistance = cutoffOffset; - this.cutoffAngle = cutoffAngle; - this.chordDistance = chordDistance; - this.circlePlane = new SidedPlane(point, point, -originDistance); - } - - public void setCutoffPlanes(final GeoPoint[] previousEndNotablePoints, final Membership previousEndPlane, - final GeoPoint[] nextStartNotablePoints, final Membership nextStartPlane) { - if (previousEndNotablePoints == null && nextStartNotablePoints == null) { - cutoffPlanes = new Membership[0]; - notablePoints = new GeoPoint[0]; - } else if (previousEndNotablePoints != null && nextStartNotablePoints == null) { - cutoffPlanes = new Membership[]{previousEndPlane}; - notablePoints = previousEndNotablePoints; - } else if (previousEndNotablePoints == null && nextStartNotablePoints != null) { - cutoffPlanes = new Membership[]{nextStartPlane}; - notablePoints = nextStartNotablePoints; - } else { - cutoffPlanes = new Membership[]{previousEndPlane,nextStartPlane}; - notablePoints = new GeoPoint[previousEndNotablePoints.length + nextStartNotablePoints.length]; - int i = 0; - for (GeoPoint p : previousEndNotablePoints) { - notablePoints[i++] = p; - } - for (GeoPoint p : nextStartNotablePoints) { - notablePoints[i++] = p; - } - } - } - - public boolean isWithin(final Vector point) - { - return circlePlane.isWithin(point); - } + } - public boolean isWithin(final double x, final double y, final double z) - { - return circlePlane.isWithin(x,y,z); - } - - public double pathDistance(final GeoPoint point) - { - double dist = this.point.arcDistance(point); - if (dist > cutoffAngle) - return Double.MAX_VALUE; - return dist; - } - - public double pathNormalDistance(final GeoPoint point) - { - double dist = this.point.normalDistance(point); - if (dist > cutoffNormalDistance) - return Double.MAX_VALUE; - return dist; - } + /** + * This is the precalculated data for a path segment. + */ + public static class PathSegment { + public final GeoPoint start; + public final GeoPoint end; + public final double fullDistance; + public final double fullNormalDistance; + public final double fullLinearDistance; + public final Plane normalizedConnectingPlane; + public final SidedPlane upperConnectingPlane; + public final SidedPlane lowerConnectingPlane; + public final SidedPlane startCutoffPlane; + public final SidedPlane endCutoffPlane; + public final GeoPoint[] upperConnectingPlanePoints; + public final GeoPoint[] lowerConnectingPlanePoints; + public final GeoPoint[] startCutoffPlanePoints; + public final GeoPoint[] endCutoffPlanePoints; + public final double planeBoundingOffset; + public final double arcWidth; + public final double chordDistance; - public double pathLinearDistance(final GeoPoint point) - { - double dist = this.point.linearDistance(point); - if (dist > chordDistance) - return Double.MAX_VALUE; - return dist; - } - - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) - { - return circlePlane.intersects(p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes); - } + // For the adjoining SegmentEndpoint... + public final SidedPlane invertedStartCutoffPlane; + public final SidedPlane invertedEndCutoffPlane; - public void getBounds(Bounds bounds) - { - bounds.addPoint(point); - circlePlane.recordBounds(bounds); - } + public PathSegment(final GeoPoint start, final GeoPoint end, final double planeBoundingOffset, final double arcWidth, final double chordDistance) { + this.start = start; + this.end = end; + this.planeBoundingOffset = planeBoundingOffset; + this.arcWidth = arcWidth; + this.chordDistance = chordDistance; - @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(); - } + fullDistance = start.arcDistance(end); + fullNormalDistance = start.normalDistance(end); + fullLinearDistance = start.linearDistance(end); + normalizedConnectingPlane = new Plane(start, end).normalize(); + if (normalizedConnectingPlane == null) { + upperConnectingPlane = null; + lowerConnectingPlane = null; + startCutoffPlane = null; + endCutoffPlane = null; + upperConnectingPlanePoints = null; + lowerConnectingPlanePoints = null; + startCutoffPlanePoints = null; + endCutoffPlanePoints = null; + invertedStartCutoffPlane = null; + invertedEndCutoffPlane = null; + } else { + // 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}; + final GeoPoint ULHC = upperConnectingPlane.findIntersections(startCutoffPlane, lowerSide, endSide)[0]; + final GeoPoint URHC = upperConnectingPlane.findIntersections(endCutoffPlane, lowerSide, startSide)[0]; + final GeoPoint LLHC = lowerConnectingPlane.findIntersections(startCutoffPlane, upperSide, endSide)[0]; + final GeoPoint LRHC = lowerConnectingPlane.findIntersections(endCutoffPlane, upperSide, startSide)[0]; + upperConnectingPlanePoints = new GeoPoint[]{ULHC, URHC}; + lowerConnectingPlanePoints = new GeoPoint[]{LLHC, LRHC}; + startCutoffPlanePoints = new GeoPoint[]{ULHC, LLHC}; + endCutoffPlanePoints = new GeoPoint[]{URHC, LRHC}; + invertedStartCutoffPlane = new SidedPlane(startCutoffPlane); + invertedEndCutoffPlane = new SidedPlane(endCutoffPlane); + } } - - /** This is the precalculated data for a path segment. - */ - public static class PathSegment - { - public final GeoPoint start; - public final GeoPoint end; - public final double fullDistance; - public final double fullNormalDistance; - public final double fullLinearDistance; - public final Plane normalizedConnectingPlane; - public final SidedPlane upperConnectingPlane; - public final SidedPlane lowerConnectingPlane; - public final SidedPlane startCutoffPlane; - public final SidedPlane endCutoffPlane; - public final GeoPoint[] upperConnectingPlanePoints; - public final GeoPoint[] lowerConnectingPlanePoints; - public final GeoPoint[] startCutoffPlanePoints; - public final GeoPoint[] endCutoffPlanePoints; - public final double planeBoundingOffset; - public final double arcWidth; - public final double chordDistance; - - // For the adjoining SegmentEndpoint... - public final SidedPlane invertedStartCutoffPlane; - public final SidedPlane invertedEndCutoffPlane; - - public PathSegment(final GeoPoint start, final GeoPoint end, final double planeBoundingOffset, final double arcWidth, final double chordDistance) - { - this.start = start; - this.end = end; - this.planeBoundingOffset = planeBoundingOffset; - this.arcWidth = arcWidth; - this.chordDistance = chordDistance; - - fullDistance = start.arcDistance(end); - fullNormalDistance = start.normalDistance(end); - fullLinearDistance = start.linearDistance(end); - normalizedConnectingPlane = new Plane(start,end).normalize(); - if (normalizedConnectingPlane == null) { - upperConnectingPlane = null; - lowerConnectingPlane = null; - startCutoffPlane = null; - endCutoffPlane = null; - upperConnectingPlanePoints = null; - lowerConnectingPlanePoints = null; - startCutoffPlanePoints = null; - endCutoffPlanePoints = null; - invertedStartCutoffPlane = null; - invertedEndCutoffPlane = null; - } else { - // 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}; - final GeoPoint ULHC = upperConnectingPlane.findIntersections(startCutoffPlane,lowerSide,endSide)[0]; - final GeoPoint URHC = upperConnectingPlane.findIntersections(endCutoffPlane,lowerSide,startSide)[0]; - final GeoPoint LLHC = lowerConnectingPlane.findIntersections(startCutoffPlane,upperSide,endSide)[0]; - final GeoPoint LRHC = lowerConnectingPlane.findIntersections(endCutoffPlane,upperSide,startSide)[0]; - upperConnectingPlanePoints = new GeoPoint[]{ULHC,URHC}; - lowerConnectingPlanePoints = new GeoPoint[]{LLHC,LRHC}; - startCutoffPlanePoints = new GeoPoint[]{ULHC,LLHC}; - endCutoffPlanePoints = new GeoPoint[]{URHC,LRHC}; - invertedStartCutoffPlane = new SidedPlane(startCutoffPlane); - invertedEndCutoffPlane = new SidedPlane(endCutoffPlane); - } - } - public boolean isDegenerate() - { - return normalizedConnectingPlane == null; - } - - public boolean isWithin(final Vector point) - { - return startCutoffPlane.isWithin(point) && - endCutoffPlane.isWithin(point) && - upperConnectingPlane.isWithin(point) && - lowerConnectingPlane.isWithin(point); - } - - 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); - } - - public double pathDistance(final GeoPoint point) - { - if (!isWithin(point)) - return Double.MAX_VALUE; - - // Compute the distance, filling in both components. - final double perpDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedConnectingPlane.evaluate(point))); - final Plane normalizedPerpPlane = new Plane(normalizedConnectingPlane,point).normalize(); - final double pathDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedPerpPlane.evaluate(start))); - return perpDistance + pathDistance; - } - - public double pathNormalDistance(final GeoPoint point) - { - if (!isWithin(point)) - return Double.MAX_VALUE; - - final double pointEval = Math.abs(normalizedConnectingPlane.evaluate(point)); - - // Want no allocations or expensive operations! so we do this the hard way - final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y; - final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z; - final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x; - - // If we have a degenerate line, then just compute the normal distance from point x to the start - if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION) - return point.normalDistance(start); - - final double normFactor = 1.0 / Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ); - final double perpEval = Math.abs(perpX * start.x + perpY * start.y + perpZ * start.z); - return perpEval * normFactor + pointEval; - } - - public double pathLinearDistance(final GeoPoint point) - { - if (!isWithin(point)) - return Double.MAX_VALUE; - - // We have a normalized connecting plane. - // First, compute the perpendicular plane. - // Want no allocations or expensive operations! so we do this the hard way - final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y; - final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z; - final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x; - - // If we have a degenerate line, then just compute the normal distance from point x to the start - if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION) - return point.linearDistance(start); - - // Next, we need the vector of the line, which is the cross product of the normalized connecting plane - // and the perpendicular plane that we just calculated. - final double lineX = normalizedConnectingPlane.y * perpZ - normalizedConnectingPlane.z * perpY; - final double lineY = normalizedConnectingPlane.z * perpX - normalizedConnectingPlane.x * perpZ; - final double lineZ = normalizedConnectingPlane.x * perpY - normalizedConnectingPlane.y * perpX; - - // Now, compute a normalization factor - final double normalizer = 1.0/Math.sqrt(lineX * lineX + lineY * lineY + lineZ * lineZ); - - // Pick which point by using bounding planes - double normLineX = lineX * normalizer; - double normLineY = lineY * normalizer; - double normLineZ = lineZ * normalizer; - if (!startCutoffPlane.isWithin(normLineX,normLineY,normLineZ) || - !endCutoffPlane.isWithin(normLineX,normLineY,normLineZ)) - { - normLineX = -normLineX; - normLineY = -normLineY; - normLineZ = -normLineZ; - } - - // Compute linear distance for the two points - return point.linearDistance(normLineX,normLineY,normLineZ) + start.linearDistance(normLineX,normLineY,normLineZ); - } - - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) - { - return upperConnectingPlane.intersects(p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) || - lowerConnectingPlane.intersects(p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); - } - - public void getBounds(Bounds bounds) - { - // We need to do all bounding planes as well as corner points - bounds.addPoint(start).addPoint(end); - upperConnectingPlane.recordBounds(startCutoffPlane, bounds, lowerConnectingPlane, endCutoffPlane); - startCutoffPlane.recordBounds(lowerConnectingPlane, bounds, endCutoffPlane, upperConnectingPlane); - lowerConnectingPlane.recordBounds(endCutoffPlane, bounds, upperConnectingPlane, startCutoffPlane); - endCutoffPlane.recordBounds(upperConnectingPlane, bounds, startCutoffPlane, lowerConnectingPlane); - upperConnectingPlane.recordBounds(bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane); - lowerConnectingPlane.recordBounds(bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); - startCutoffPlane.recordBounds(bounds, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane); - endCutoffPlane.recordBounds(bounds, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane); - if (fullDistance >= Math.PI) { - // Too large a segment basically means that we can confuse the Bounds object. Specifically, if our span exceeds 180 degrees - // in longitude (which even a segment whose actual length is less than that might if it goes close to a pole). - // Unfortunately, we can get arbitrarily close to the pole, so this may still not work in all cases. - bounds.noLongitudeBound(); - } - } - + public boolean isDegenerate() { + return normalizedConnectingPlane == null; } + public boolean isWithin(final Vector point) { + return startCutoffPlane.isWithin(point) && + endCutoffPlane.isWithin(point) && + upperConnectingPlane.isWithin(point) && + lowerConnectingPlane.isWithin(point); + } + + 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); + } + + public double pathDistance(final GeoPoint point) { + if (!isWithin(point)) + return Double.MAX_VALUE; + + // Compute the distance, filling in both components. + final double perpDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedConnectingPlane.evaluate(point))); + final Plane normalizedPerpPlane = new Plane(normalizedConnectingPlane, point).normalize(); + final double pathDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedPerpPlane.evaluate(start))); + return perpDistance + pathDistance; + } + + public double pathNormalDistance(final GeoPoint point) { + if (!isWithin(point)) + return Double.MAX_VALUE; + + final double pointEval = Math.abs(normalizedConnectingPlane.evaluate(point)); + + // Want no allocations or expensive operations! so we do this the hard way + final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y; + final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z; + final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x; + + // If we have a degenerate line, then just compute the normal distance from point x to the start + if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION) + return point.normalDistance(start); + + final double normFactor = 1.0 / Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ); + final double perpEval = Math.abs(perpX * start.x + perpY * start.y + perpZ * start.z); + return perpEval * normFactor + pointEval; + } + + public double pathLinearDistance(final GeoPoint point) { + if (!isWithin(point)) + return Double.MAX_VALUE; + + // We have a normalized connecting plane. + // First, compute the perpendicular plane. + // Want no allocations or expensive operations! so we do this the hard way + final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y; + final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z; + final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x; + + // If we have a degenerate line, then just compute the normal distance from point x to the start + if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION) + return point.linearDistance(start); + + // Next, we need the vector of the line, which is the cross product of the normalized connecting plane + // and the perpendicular plane that we just calculated. + final double lineX = normalizedConnectingPlane.y * perpZ - normalizedConnectingPlane.z * perpY; + final double lineY = normalizedConnectingPlane.z * perpX - normalizedConnectingPlane.x * perpZ; + final double lineZ = normalizedConnectingPlane.x * perpY - normalizedConnectingPlane.y * perpX; + + // Now, compute a normalization factor + final double normalizer = 1.0 / Math.sqrt(lineX * lineX + lineY * lineY + lineZ * lineZ); + + // Pick which point by using bounding planes + double normLineX = lineX * normalizer; + double normLineY = lineY * normalizer; + double normLineZ = lineZ * normalizer; + if (!startCutoffPlane.isWithin(normLineX, normLineY, normLineZ) || + !endCutoffPlane.isWithin(normLineX, normLineY, normLineZ)) { + normLineX = -normLineX; + normLineY = -normLineY; + normLineZ = -normLineZ; + } + + // Compute linear distance for the two points + return point.linearDistance(normLineX, normLineY, normLineZ) + start.linearDistance(normLineX, normLineY, normLineZ); + } + + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds) { + return upperConnectingPlane.intersects(p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) || + lowerConnectingPlane.intersects(p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); + } + + public void getBounds(Bounds bounds) { + // We need to do all bounding planes as well as corner points + bounds.addPoint(start).addPoint(end); + upperConnectingPlane.recordBounds(startCutoffPlane, bounds, lowerConnectingPlane, endCutoffPlane); + startCutoffPlane.recordBounds(lowerConnectingPlane, bounds, endCutoffPlane, upperConnectingPlane); + lowerConnectingPlane.recordBounds(endCutoffPlane, bounds, upperConnectingPlane, startCutoffPlane); + endCutoffPlane.recordBounds(upperConnectingPlane, bounds, startCutoffPlane, lowerConnectingPlane); + upperConnectingPlane.recordBounds(bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane); + lowerConnectingPlane.recordBounds(bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane); + startCutoffPlane.recordBounds(bounds, endCutoffPlane, upperConnectingPlane, lowerConnectingPlane); + endCutoffPlane.recordBounds(bounds, startCutoffPlane, upperConnectingPlane, lowerConnectingPlane); + if (fullDistance >= Math.PI) { + // Too large a segment basically means that we can confuse the Bounds object. Specifically, if our span exceeds 180 degrees + // in longitude (which even a segment whose actual length is less than that might if it goes close to a pole). + // Unfortunately, we can get arbitrarily close to the pole, so this may still not work in all cases. + bounds.noLongitudeBound(); + } + } + + } + } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java index 29f35a22b7f..4aa98679d25 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPoint.java @@ -17,28 +17,24 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** This class represents a point on the surface of a unit sphere. -*/ -public class GeoPoint extends Vector -{ - public GeoPoint(final double sinLat, final double sinLon, final double cosLat, final double cosLon) - { - super(cosLat*cosLon,cosLat*sinLon,sinLat); - } - - public GeoPoint(final double lat, final double lon) - { - this(Math.sin(lat),Math.sin(lon),Math.cos(lat),Math.cos(lon)); - } - - public GeoPoint(final double x, final double y, final double z) - { - super(x,y,z); - } - - public double arcDistance(final GeoPoint v) - { - return Tools.safeAcos(dotProduct(v)); - } +/** + * This class represents a point on the surface of a unit sphere. + */ +public class GeoPoint extends Vector { + public GeoPoint(final double sinLat, final double sinLon, final double cosLat, final double cosLon) { + super(cosLat * cosLon, cosLat * sinLon, sinLat); + } + + public GeoPoint(final double lat, final double lon) { + this(Math.sin(lat), Math.sin(lon), Math.cos(lat), Math.cos(lon)); + } + + public GeoPoint(final double x, final double y, final double z) { + super(x, y, z); + } + + public double arcDistance(final GeoPoint v) { + return Tools.safeAcos(dotProduct(v)); + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java index b72c3485edf..4b14e2d24f1 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonFactory.java @@ -21,146 +21,148 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.List; -/** Class which constructs a GeoMembershipShape representing an arbitrary polygon. -*/ -public class GeoPolygonFactory -{ - private GeoPolygonFactory() { - } - - /** Create a GeoMembershipShape of the right kind given the specified bounds. - *@param pointList is a list of the GeoPoints to build an arbitrary polygon out of. - *@param convexPointIndex is the index of a single convex point whose conformation with - * its neighbors determines inside/outside for the entire polygon. - *@return a GeoMembershipShape corresponding to what was specified. - */ - public static GeoMembershipShape makeGeoPolygon(List pointList, int convexPointIndex) { - // The basic operation uses a set of points, two points determining one particular edge, and a sided plane - // describing membership. - return buildPolygonShape(pointList, convexPointIndex, getLegalIndex(convexPointIndex+1,pointList.size()), - new SidedPlane(pointList.get(getLegalIndex(convexPointIndex-1,pointList.size())), - pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex+1,pointList.size()))), - false); - } +/** + * Class which constructs a GeoMembershipShape representing an arbitrary polygon. + */ +public class GeoPolygonFactory { + private GeoPolygonFactory() { + } - public static GeoMembershipShape buildPolygonShape(List pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge, boolean isInternalEdge) { - // Algorithm as follows: - // Start with sided edge. Go through all points in some order. For each new point, determine if the point is within all edges considered so far. - // If not, put it into a list of points for recursion. If it is within, add new edge and keep going. - // Once we detect a point that is within, if there are points put aside for recursion, then call recursively. - - // Current composite. This is what we'll actually be returning. - final GeoCompositeMembershipShape rval = new GeoCompositeMembershipShape(); - - final List recursionList = new ArrayList(); - final List currentList = new ArrayList(); - final BitSet internalEdgeList = new BitSet(); - final List currentPlanes = new ArrayList(); - - // Initialize the current list and current planes - currentList.add(pointsList.get(startPointIndex)); - currentList.add(pointsList.get(endPointIndex)); - internalEdgeList.set(currentPlanes.size(),isInternalEdge); - currentPlanes.add(startingEdge); - - // Now, scan all remaining points, in order. We'll use an index and just add to it. - for (int i = 0; i < pointsList.size() - 2; i++) { - GeoPoint newPoint = pointsList.get(getLegalIndex(i + endPointIndex + 1, pointsList.size())); - if (isWithin(newPoint, currentPlanes)) { - // Construct a sided plane based on the last two points, and the previous point - SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size()-2),newPoint,currentList.get(currentList.size()-1)); - // Construct a sided plane based on the return trip - SidedPlane returnBoundary = new SidedPlane(currentList.get(currentList.size()-1),currentList.get(0),newPoint); - // Verify that none of the points beyond the new point in the list are inside the polygon we'd - // be creating if we stopped making the current polygon right now. - boolean pointInside = false; - for (int j = i + 1; j < pointsList.size() - 2; j++) { - GeoPoint checkPoint = pointsList.get(getLegalIndex(j + endPointIndex + 1, pointsList.size())); - boolean isInside = true; - if (isInside && !newBoundary.isWithin(checkPoint)) - isInside = false; - if (isInside && !returnBoundary.isWithin(checkPoint)) - isInside = false; - if (isInside) { - for (SidedPlane plane : currentPlanes) { - if (!plane.isWithin(checkPoint)) { - isInside = false; - break; - } - } - } - if (isInside) { - pointInside = true; - break; - } - } - if (!pointInside) { - // Any excluded points? - boolean isInternalBoundary = recursionList.size() > 0; - if (isInternalBoundary) { - // Handle exclusion - recursionList.add(newPoint); - recursionList.add(currentList.get(currentList.size()-1)); - if (recursionList.size() == pointsList.size()) { - // We are trying to recurse with a list the same size as the one we started with. - // Clearly, the polygon cannot be constructed - throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts"); - } - // We want the other side for the recursion - SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); - rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary,true)); - recursionList.clear(); - } - currentList.add(newPoint); - internalEdgeList.set(currentPlanes.size(),isInternalBoundary); - currentPlanes.add(newBoundary); - } else { - recursionList.add(newPoint); - } - } else { - recursionList.add(newPoint); + /** + * Create a GeoMembershipShape of the right kind given the specified bounds. + * + * @param pointList is a list of the GeoPoints to build an arbitrary polygon out of. + * @param convexPointIndex is the index of a single convex point whose conformation with + * its neighbors determines inside/outside for the entire polygon. + * @return a GeoMembershipShape corresponding to what was specified. + */ + public static GeoMembershipShape makeGeoPolygon(List pointList, int convexPointIndex) { + // The basic operation uses a set of points, two points determining one particular edge, and a sided plane + // describing membership. + return buildPolygonShape(pointList, convexPointIndex, getLegalIndex(convexPointIndex + 1, pointList.size()), + new SidedPlane(pointList.get(getLegalIndex(convexPointIndex - 1, pointList.size())), + pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex + 1, pointList.size()))), + false); + } + + public static GeoMembershipShape buildPolygonShape(List pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge, boolean isInternalEdge) { + // Algorithm as follows: + // Start with sided edge. Go through all points in some order. For each new point, determine if the point is within all edges considered so far. + // If not, put it into a list of points for recursion. If it is within, add new edge and keep going. + // Once we detect a point that is within, if there are points put aside for recursion, then call recursively. + + // Current composite. This is what we'll actually be returning. + final GeoCompositeMembershipShape rval = new GeoCompositeMembershipShape(); + + final List recursionList = new ArrayList(); + final List currentList = new ArrayList(); + final BitSet internalEdgeList = new BitSet(); + final List currentPlanes = new ArrayList(); + + // Initialize the current list and current planes + currentList.add(pointsList.get(startPointIndex)); + currentList.add(pointsList.get(endPointIndex)); + internalEdgeList.set(currentPlanes.size(), isInternalEdge); + currentPlanes.add(startingEdge); + + // Now, scan all remaining points, in order. We'll use an index and just add to it. + for (int i = 0; i < pointsList.size() - 2; i++) { + GeoPoint newPoint = pointsList.get(getLegalIndex(i + endPointIndex + 1, pointsList.size())); + if (isWithin(newPoint, currentPlanes)) { + // Construct a sided plane based on the last two points, and the previous point + SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size() - 2), newPoint, currentList.get(currentList.size() - 1)); + // Construct a sided plane based on the return trip + SidedPlane returnBoundary = new SidedPlane(currentList.get(currentList.size() - 1), currentList.get(0), newPoint); + // Verify that none of the points beyond the new point in the list are inside the polygon we'd + // be creating if we stopped making the current polygon right now. + boolean pointInside = false; + for (int j = i + 1; j < pointsList.size() - 2; j++) { + GeoPoint checkPoint = pointsList.get(getLegalIndex(j + endPointIndex + 1, pointsList.size())); + boolean isInside = true; + if (isInside && !newBoundary.isWithin(checkPoint)) + isInside = false; + if (isInside && !returnBoundary.isWithin(checkPoint)) + isInside = false; + if (isInside) { + for (SidedPlane plane : currentPlanes) { + if (!plane.isWithin(checkPoint)) { + isInside = false; + break; + } } + } + if (isInside) { + pointInside = true; + break; + } } - - boolean returnEdgeInternalBoundary = recursionList.size() > 0; - if (returnEdgeInternalBoundary) { - // The last step back to the start point had a recursion, so take care of that before we complete our work - recursionList.add(currentList.get(0)); - recursionList.add(currentList.get(currentList.size()-1)); + if (!pointInside) { + // Any excluded points? + boolean isInternalBoundary = recursionList.size() > 0; + if (isInternalBoundary) { + // Handle exclusion + recursionList.add(newPoint); + recursionList.add(currentList.get(currentList.size() - 1)); if (recursionList.size() == pointsList.size()) { - // We are trying to recurse with a list the same size as the one we started with. - // Clearly, the polygon cannot be constructed - throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts"); + // We are trying to recurse with a list the same size as the one we started with. + // Clearly, the polygon cannot be constructed + throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts"); } - // Construct a sided plane based on these two points, and the previous point - SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size()-2),currentList.get(0),currentList.get(currentList.size()-1)); // We want the other side for the recursion SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); - rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary,true)); + rval.addShape(buildPolygonShape(recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); recursionList.clear(); + } + currentList.add(newPoint); + internalEdgeList.set(currentPlanes.size(), isInternalBoundary); + currentPlanes.add(newBoundary); + } else { + recursionList.add(newPoint); } - // Now, add in the current shape. - rval.addShape(new GeoConvexPolygon(currentList,internalEdgeList,returnEdgeInternalBoundary)); - //System.out.println("Done creating polygon"); - return rval; + } else { + recursionList.add(newPoint); + } } - protected static boolean isWithin(GeoPoint newPoint, List currentPlanes) { - for (SidedPlane p : currentPlanes) { - if (!p.isWithin(newPoint)) - return false; - } - return true; + boolean returnEdgeInternalBoundary = recursionList.size() > 0; + if (returnEdgeInternalBoundary) { + // The last step back to the start point had a recursion, so take care of that before we complete our work + recursionList.add(currentList.get(0)); + recursionList.add(currentList.get(currentList.size() - 1)); + if (recursionList.size() == pointsList.size()) { + // We are trying to recurse with a list the same size as the one we started with. + // Clearly, the polygon cannot be constructed + throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts"); + } + // Construct a sided plane based on these two points, and the previous point + SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size() - 2), currentList.get(0), currentList.get(currentList.size() - 1)); + // We want the other side for the recursion + SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary); + rval.addShape(buildPolygonShape(recursionList, recursionList.size() - 2, recursionList.size() - 1, otherSideNewBoundary, true)); + recursionList.clear(); } + // Now, add in the current shape. + rval.addShape(new GeoConvexPolygon(currentList, internalEdgeList, returnEdgeInternalBoundary)); + //System.out.println("Done creating polygon"); + return rval; + } - protected static int getLegalIndex(int index, int size) { - while (index < 0) { - index += size; - } - while (index >= size) { - index -= size; - } - return index; + protected static boolean isWithin(GeoPoint newPoint, List currentPlanes) { + for (SidedPlane p : currentPlanes) { + if (!p.isWithin(newPoint)) + return false; } - + return true; + } + + protected static int getLegalIndex(int index, int size) { + while (index < 0) { + index += size; + } + while (index >= size) { + index -= size; + } + return index; + } + } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java index af25d4a3cb4..219f78196a5 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoRectangle.java @@ -17,250 +17,246 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box limited on four sides (top lat, bottom lat, left lon, right lon). -* The left-right maximum extent for this shape is PI; for anything larger, use -* GeoWideRectangle. -*/ -public class GeoRectangle extends GeoBBoxBase -{ - public final double topLat; - public final double bottomLat; - public final double leftLon; - public final double rightLon; - - public final double cosMiddleLat; - - public final GeoPoint ULHC; - public final GeoPoint URHC; - public final GeoPoint LRHC; - public final GeoPoint LLHC; - - public final SidedPlane topPlane; - public final SidedPlane bottomPlane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; - - public final GeoPoint[] topPlanePoints; - public final GeoPoint[] bottomPlanePoints; - public final GeoPoint[] leftPlanePoints; - public final GeoPoint[] rightPlanePoints; - - public final GeoPoint centerPoint; +/** + * Bounding box limited on four sides (top lat, bottom lat, left lon, right lon). + * The left-right maximum extent for this shape is PI; for anything larger, use + * GeoWideRectangle. + */ +public class GeoRectangle extends GeoBBoxBase { + public final double topLat; + public final double bottomLat; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] edgePoints; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon) - { - // Argument checking - if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Top latitude out of range"); - if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Bottom latitude out of range"); - if (topLat < bottomLat) - throw new IllegalArgumentException("Top latitude less than bottom latitude"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent > Math.PI) - throw new IllegalArgumentException("Width of rectangle too great"); + public final double cosMiddleLat; - this.topLat = topLat; - this.bottomLat = bottomLat; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinTopLat = Math.sin(topLat); - final double cosTopLat = Math.cos(topLat); - final double sinBottomLat = Math.sin(bottomLat); - final double cosBottomLat = Math.cos(bottomLat); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the four points - this.ULHC = new GeoPoint(sinTopLat,sinLeftLon,cosTopLat,cosLeftLon); - this.URHC = new GeoPoint(sinTopLat,sinRightLon,cosTopLat,cosRightLon); - this.LRHC = new GeoPoint(sinBottomLat,sinRightLon,cosBottomLat,cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat,sinLeftLon,cosBottomLat,cosLeftLon); - - final double middleLat = (topLat + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.cosMiddleLat = Math.cos(middleLat); - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon); + public final GeoPoint ULHC; + public final GeoPoint URHC; + public final GeoPoint LRHC; + public final GeoPoint LLHC; - this.topPlane = new SidedPlane(centerPoint,sinTopLat); - this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final SidedPlane topPlane; + public final SidedPlane bottomPlane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.topPlanePoints = new GeoPoint[]{ULHC,URHC}; - this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC}; - this.leftPlanePoints = new GeoPoint[]{ULHC,LLHC}; - this.rightPlanePoints = new GeoPoint[]{URHC,LRHC}; - - this.edgePoints = new GeoPoint[]{ULHC}; + public final GeoPoint[] topPlanePoints; + public final GeoPoint[] bottomPlanePoints; + public final GeoPoint[] leftPlanePoints; + public final GeoPoint[] rightPlanePoints; + + public final GeoPoint centerPoint; + + public final GeoPoint[] edgePoints; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} + */ + public GeoRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon) { + // Argument checking + if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Top latitude out of range"); + if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Bottom latitude out of range"); + if (topLat < bottomLat) + throw new IllegalArgumentException("Top latitude less than bottom latitude"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent > Math.PI) + throw new IllegalArgumentException("Width of rectangle too great"); + + this.topLat = topLat; + this.bottomLat = bottomLat; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinTopLat = Math.sin(topLat); + final double cosTopLat = Math.cos(topLat); + final double sinBottomLat = Math.sin(bottomLat); + final double cosBottomLat = Math.cos(bottomLat); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the four points + this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); + this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + + final double middleLat = (topLat + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.cosMiddleLat = Math.cos(middleLat); + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + + this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.topPlanePoints = new GeoPoint[]{ULHC, URHC}; + this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC}; + this.leftPlanePoints = new GeoPoint[]{ULHC, LLHC}; + this.rightPlanePoints = new GeoPoint[]{URHC, LRHC}; + + this.edgePoints = new GeoPoint[]{ULHC}; + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = bottomLat - angle; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return topPlane.isWithin(point) && + bottomPlane.isWithin(point) && + leftPlane.isWithin(point) && + rightPlane.isWithin(point); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return topPlane.isWithin(x, y, z) && + bottomPlane.isWithin(x, y, z) && + leftPlane.isWithin(x, y, z) && + rightPlane.isWithin(x, y, z); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; + final double topAngle = centerPoint.arcDistance(URHC); + final double bottomAngle = centerPoint.arcDistance(LLHC); + return Math.max(centerAngle, Math.max(topAngle, bottomAngle)); + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, leftPlane, rightPlane) || + p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, leftPlane, rightPlane) || + p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane, bottomPlane) || + p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat) + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" getrelationship with "+path); + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) { + //System.err.println(" some inside"); + return OVERLAPS; } - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = bottomLat - angle; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + final boolean insideShape = path.isWithin(ULHC); + + if (insideRectangle == ALL_INSIDE && insideShape) { + //System.err.println(" inside of each other"); + return OVERLAPS; } - @Override - public boolean isWithin(final Vector point) - { - return topPlane.isWithin(point) && - bottomPlane.isWithin(point) && - leftPlane.isWithin(point) && - rightPlane.isWithin(point); + if (path.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) || + path.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) || + path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane, rightPlane) || + path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane, bottomPlane)) { + //System.err.println(" edges intersect"); + return OVERLAPS; } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return topPlane.isWithin(x,y,z) && - bottomPlane.isWithin(x,y,z) && - leftPlane.isWithin(x,y,z) && - rightPlane.isWithin(x,y,z); + if (insideRectangle == ALL_INSIDE) { + //System.err.println(" shape inside rectangle"); + return WITHIN; } - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; - final double topAngle = centerPoint.arcDistance(URHC); - final double bottomAngle = centerPoint.arcDistance(LLHC); - return Math.max(centerAngle,Math.max(topAngle,bottomAngle)); - } - - @Override - public GeoPoint[] getEdgePoints() { - return edgePoints; + if (insideShape) { + //System.err.println(" shape contains rectangle"); + return CONTAINS; } + //System.err.println(" disjoint"); + return DISJOINT; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoRectangle)) + return false; + GeoRectangle other = (GeoRectangle) o; + return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); + } - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(topPlane,notablePoints,topPlanePoints,bounds,bottomPlane,leftPlane,rightPlane) || - p.intersects(bottomPlane,notablePoints,bottomPlanePoints,bounds,topPlane,leftPlane,rightPlane) || - p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,rightPlane,topPlane,bottomPlane) || - p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,leftPlane,topPlane,bottomPlane); - } + @Override + public int hashCode() { + int result = ULHC.hashCode(); + result = 31 * result + LRHC.hashCode(); + return result; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat) - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" getrelationship with "+path); - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - { - //System.err.println(" some inside"); - return OVERLAPS; - } - - final boolean insideShape = path.isWithin(ULHC); - - if (insideRectangle == ALL_INSIDE && insideShape) { - //System.err.println(" inside of each other"); - return OVERLAPS; - } - - if (path.intersects(topPlane,topPlanePoints,bottomPlane,leftPlane,rightPlane) || - path.intersects(bottomPlane,bottomPlanePoints,topPlane,leftPlane,rightPlane) || - path.intersects(leftPlane,leftPlanePoints,topPlane,bottomPlane,rightPlane) || - path.intersects(rightPlane,rightPlanePoints,leftPlane,topPlane,bottomPlane)) { - //System.err.println(" edges intersect"); - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) - { - //System.err.println(" shape inside rectangle"); - return WITHIN; - } - - if (insideShape) { - //System.err.println(" shape contains rectangle"); - return CONTAINS; - } - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoRectangle)) - return false; - GeoRectangle other = (GeoRectangle)o; - return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); - } - - @Override - public int hashCode() { - int result = ULHC.hashCode(); - result = 31 * result + LRHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoRectangle: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } + @Override + public String toString() { + return "GeoRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java index 9770c7cca81..44eadcf90e6 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoShape.java @@ -17,44 +17,53 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Generic shape. This describes methods that help GeoAreas figure out -* how they interact with a shape, for the purposes of coming up with a -* set of geo hash values. -*/ +/** + * Generic shape. This describes methods that help GeoAreas figure out + * how they interact with a shape, for the purposes of coming up with a + * set of geo hash values. + */ public interface GeoShape extends Membership { - /** Return a sample point that is on the outside edge/boundary of the shape. - *@return samples of all edge points from distinct edge sections. Typically one point - * is returned, but zero or two are also possible. - */ - public GeoPoint[] getEdgePoints(); - - /** Assess whether a plane, within the provided bounds, intersects - * with the shape. Note well that this method is allowed to return "true" - * if there are internal edges of a composite shape which intersect the plane. - * Doing this can cause getRelationship() for most GeoBBox shapes to return - * OVERLAPS rather than the more correct CONTAINS, but that cannot be - * helped for some complex shapes that are built out of overlapping parts. - *@param plane is the plane to assess for intersection with the shape's edges or - * bounding curves. - *@param notablePoints represents the intersections of the plane with the supplied - * bounds. These are used to disambiguate when two planes are identical and it needs - * to be determined whether any points exist that fulfill all the bounds. - *@param bounds are a set of bounds that define an area that an - * intersection must be within in order to qualify (provided by a GeoArea). - *@return true if there's such an intersection, false if not. - */ - public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds); + /** + * Return a sample point that is on the outside edge/boundary of the shape. + * + * @return samples of all edge points from distinct edge sections. Typically one point + * is returned, but zero or two are also possible. + */ + public GeoPoint[] getEdgePoints(); - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - public Bounds getBounds(final Bounds bounds); + /** + * Assess whether a plane, within the provided bounds, intersects + * with the shape. Note well that this method is allowed to return "true" + * if there are internal edges of a composite shape which intersect the plane. + * Doing this can cause getRelationship() for most GeoBBox shapes to return + * OVERLAPS rather than the more correct CONTAINS, but that cannot be + * helped for some complex shapes that are built out of overlapping parts. + * + * @param plane is the plane to assess for intersection with the shape's edges or + * bounding curves. + * @param notablePoints represents the intersections of the plane with the supplied + * bounds. These are used to disambiguate when two planes are identical and it needs + * to be determined whether any points exist that fulfill all the bounds. + * @param bounds are a set of bounds that define an area that an + * intersection must be within in order to qualify (provided by a GeoArea). + * @return true if there's such an intersection, false if not. + */ + public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds); - /** Equals */ - public boolean equals(Object o); + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + public Bounds getBounds(final Bounds bounds); + + /** + * Equals + */ + public boolean equals(Object o); } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSizeable.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSizeable.java index d12f73480a4..4380cd3f308 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSizeable.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSizeable.java @@ -17,19 +17,23 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Some shapes can compute radii of a geocircle in which they are inscribed. -*/ -public interface GeoSizeable -{ - /** Returns the radius of a circle into which the GeoSizeable area can - * be inscribed. - *@return the radius. - */ - public double getRadius(); +/** + * Some shapes can compute radii of a geocircle in which they are inscribed. + */ +public interface GeoSizeable { + /** + * Returns the radius of a circle into which the GeoSizeable area can + * be inscribed. + * + * @return the radius. + */ + public double getRadius(); + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + public GeoPoint getCenter(); - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - public GeoPoint getCenter(); - } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java index 054fb1fdadf..0cb3a824af3 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthLatitudeZone.java @@ -17,159 +17,154 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** This GeoBBox represents an area rectangle limited only in north latitude. -*/ -public class GeoSouthLatitudeZone extends GeoBBoxBase -{ - public final double topLat; - public final double cosTopLat; - public final SidedPlane topPlane; - public final GeoPoint interiorPoint; - public final static GeoPoint[] planePoints = new GeoPoint[0]; +/** + * This GeoBBox represents an area rectangle limited only in north latitude. + */ +public class GeoSouthLatitudeZone extends GeoBBoxBase { + public final double topLat; + public final double cosTopLat; + public final SidedPlane topPlane; + public final GeoPoint interiorPoint; + public final static GeoPoint[] planePoints = new GeoPoint[0]; - public final GeoPoint topBoundaryPoint; - - // Edge points - public final GeoPoint[] edgePoints; - - public GeoSouthLatitudeZone(final double topLat) - { - this.topLat = topLat; - - final double sinTopLat = Math.sin(topLat); - this.cosTopLat = Math.cos(topLat); - - // Construct sample points, so we get our sidedness right - final Vector topPoint = new Vector(0.0,0.0,sinTopLat); + public final GeoPoint topBoundaryPoint; - // Compute an interior point. Pick one whose lat is between top and bottom. - final double middleLat = (topLat - Math.PI * 0.5) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat); - this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat),0.0,sinTopLat); - - this.topPlane = new SidedPlane(interiorPoint,sinTopLat); - - this.edgePoints = new GeoPoint[]{topBoundaryPoint}; - } + // Edge points + public final GeoPoint[] edgePoints; - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = -Math.PI * 0.5; - return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); - } + public GeoSouthLatitudeZone(final double topLat) { + this.topLat = topLat; - @Override - public boolean isWithin(final Vector point) - { - return topPlane.isWithin(point); - } + final double sinTopLat = Math.sin(topLat); + this.cosTopLat = Math.cos(topLat); - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return topPlane.isWithin(x,y,z); - } + // Construct sample points, so we get our sidedness right + final Vector topPoint = new Vector(0.0, 0.0, sinTopLat); - @Override - public double getRadius() - { - // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that - // would contain all the bounding box points, when starting in the "center". - if (topLat > 0.0) - return Math.PI; - double maxCosLat = cosTopLat; - return maxCosLat * Math.PI; - } + // Compute an interior point. Pick one whose lat is between top and bottom. + final double middleLat = (topLat - Math.PI * 0.5) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat); + this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat), 0.0, sinTopLat); - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return interiorPoint; - } + this.topPlane = new SidedPlane(interiorPoint, sinTopLat); - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(topPlane,notablePoints,planePoints,bounds); - } + this.edgePoints = new GeoPoint[]{topBoundaryPoint}; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noLongitudeBound().addLatitudeZone(topLat).noBottomLatitudeBound(); - return bounds; - } + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = -Math.PI * 0.5; + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI); + } - @Override - public int getRelationship(final GeoShape path) { - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - return OVERLAPS; + @Override + public boolean isWithin(final Vector point) { + return topPlane.isWithin(point); + } - final boolean insideShape = path.isWithin(topBoundaryPoint); - - if (insideRectangle == ALL_INSIDE && insideShape) - return OVERLAPS; + @Override + public boolean isWithin(final double x, final double y, final double z) { + return topPlane.isWithin(x, y, z); + } - // Second, the shortcut of seeing whether endpoints are in/out is not going to - // work with no area endpoints. So we rely entirely on intersections. + @Override + public double getRadius() { + // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that + // would contain all the bounding box points, when starting in the "center". + if (topLat > 0.0) + return Math.PI; + double maxCosLat = cosTopLat; + return maxCosLat * Math.PI; + } - if (path.intersects(topPlane,planePoints)) - return OVERLAPS; + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return interiorPoint; + } - // There is another case for latitude zones only. This is when the boundaries of the shape all fit - // within the zone, but the shape includes areas outside the zone crossing a pole. - // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary - // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If - // one such point is within, then OVERLAPS is the right answer. - - if (insideShape) - return CONTAINS; + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - if (insideRectangle == ALL_INSIDE) - return WITHIN; + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(topPlane, notablePoints, planePoints, bounds); + } - return DISJOINT; - } + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noLongitudeBound().addLatitudeZone(topLat).noBottomLatitudeBound(); + return bounds; + } - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoSouthLatitudeZone)) - return false; - GeoSouthLatitudeZone other = (GeoSouthLatitudeZone)o; - return other.topPlane.equals(topPlane); - } + @Override + public int getRelationship(final GeoShape path) { + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) + return OVERLAPS; - @Override - public int hashCode() { - int result = topPlane.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoSouthLatitudeZone: {toplat="+topLat+"("+topLat*180.0/Math.PI+")}"; - } + final boolean insideShape = path.isWithin(topBoundaryPoint); + + if (insideRectangle == ALL_INSIDE && insideShape) + return OVERLAPS; + + // Second, the shortcut of seeing whether endpoints are in/out is not going to + // work with no area endpoints. So we rely entirely on intersections. + + if (path.intersects(topPlane, planePoints)) + return OVERLAPS; + + // There is another case for latitude zones only. This is when the boundaries of the shape all fit + // within the zone, but the shape includes areas outside the zone crossing a pole. + // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary + // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If + // one such point is within, then OVERLAPS is the right answer. + + if (insideShape) + return CONTAINS; + + if (insideRectangle == ALL_INSIDE) + return WITHIN; + + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoSouthLatitudeZone)) + return false; + GeoSouthLatitudeZone other = (GeoSouthLatitudeZone) o; + return other.topPlane.equals(topPlane); + } + + @Override + public int hashCode() { + int result = topPlane.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoSouthLatitudeZone: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java index 8d06fc743be..9ce3dc027c2 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoSouthRectangle.java @@ -17,230 +17,225 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box limited on three sides (top lat, left lon, right lon). The -* other corner is the south pole. -* The left-right maximum extent for this shape is PI; for anything larger, use -* GeoWideSouthRectangle. -*/ -public class GeoSouthRectangle extends GeoBBoxBase -{ - public final double topLat; - public final double leftLon; - public final double rightLon; - - public final double cosMiddleLat; - - public final GeoPoint ULHC; - public final GeoPoint URHC; - - public final SidedPlane topPlane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; - - public final GeoPoint[] topPlanePoints; - public final GeoPoint[] leftPlanePoints; - public final GeoPoint[] rightPlanePoints; - - public final GeoPoint centerPoint; +/** + * Bounding box limited on three sides (top lat, left lon, right lon). The + * other corner is the south pole. + * The left-right maximum extent for this shape is PI; for anything larger, use + * GeoWideSouthRectangle. + */ +public class GeoSouthRectangle extends GeoBBoxBase { + public final double topLat; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE}; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */ - public GeoSouthRectangle(final double topLat, final double leftLon, double rightLon) - { - // Argument checking - if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Top latitude out of range"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent > Math.PI) - throw new IllegalArgumentException("Width of rectangle too great"); + public final double cosMiddleLat; - this.topLat = topLat; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinTopLat = Math.sin(topLat); - final double cosTopLat = Math.cos(topLat); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the four points - this.ULHC = new GeoPoint(sinTopLat,sinLeftLon,cosTopLat,cosLeftLon); - this.URHC = new GeoPoint(sinTopLat,sinRightLon,cosTopLat,cosRightLon); - - final double middleLat = (topLat - Math.PI * 0.5) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.cosMiddleLat = Math.cos(middleLat); - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon); + public final GeoPoint ULHC; + public final GeoPoint URHC; - this.topPlane = new SidedPlane(centerPoint,sinTopLat); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final SidedPlane topPlane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.topPlanePoints = new GeoPoint[]{ULHC,URHC}; - this.leftPlanePoints = new GeoPoint[]{ULHC,SOUTH_POLE}; - this.rightPlanePoints = new GeoPoint[]{URHC,SOUTH_POLE}; + public final GeoPoint[] topPlanePoints; + public final GeoPoint[] leftPlanePoints; + public final GeoPoint[] rightPlanePoints; + + public final GeoPoint centerPoint; + + public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE}; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} + */ + public GeoSouthRectangle(final double topLat, final double leftLon, double rightLon) { + // Argument checking + if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Top latitude out of range"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent > Math.PI) + throw new IllegalArgumentException("Width of rectangle too great"); + + this.topLat = topLat; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinTopLat = Math.sin(topLat); + final double cosTopLat = Math.cos(topLat); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the four points + this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); + + final double middleLat = (topLat - Math.PI * 0.5) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.cosMiddleLat = Math.cos(middleLat); + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + + this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.topPlanePoints = new GeoPoint[]{ULHC, URHC}; + this.leftPlanePoints = new GeoPoint[]{ULHC, SOUTH_POLE}; + this.rightPlanePoints = new GeoPoint[]{URHC, SOUTH_POLE}; + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = -Math.PI * 0.5; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return topPlane.isWithin(point) && + leftPlane.isWithin(point) && + rightPlane.isWithin(point); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return topPlane.isWithin(x, y, z) && + leftPlane.isWithin(x, y, z) && + rightPlane.isWithin(x, y, z); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; + final double topAngle = centerPoint.arcDistance(URHC); + return Math.max(centerAngle, topAngle); + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, leftPlane, rightPlane) || + p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane) || + p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(topLat).noBottomLatitudeBound() + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" getrelationship with "+path); + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) { + //System.err.println(" some inside"); + return OVERLAPS; } - @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = -Math.PI * 0.5; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + final boolean insideShape = path.isWithin(SOUTH_POLE); + + if (insideRectangle == ALL_INSIDE && insideShape) { + //System.err.println(" inside of each other"); + return OVERLAPS; } - @Override - public boolean isWithin(final Vector point) - { - return topPlane.isWithin(point) && - leftPlane.isWithin(point) && - rightPlane.isWithin(point); + if (path.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) || + path.intersects(leftPlane, leftPlanePoints, topPlane, rightPlane) || + path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane)) { + //System.err.println(" edges intersect"); + return OVERLAPS; } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return topPlane.isWithin(x,y,z) && - leftPlane.isWithin(x,y,z) && - rightPlane.isWithin(x,y,z); + if (insideRectangle == ALL_INSIDE) { + //System.err.println(" shape inside rectangle"); + return WITHIN; } - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; - final double topAngle = centerPoint.arcDistance(URHC); - return Math.max(centerAngle,topAngle); - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; + if (insideShape) { + //System.err.println(" shape contains rectangle"); + return CONTAINS; } + //System.err.println(" disjoint"); + return DISJOINT; + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoSouthRectangle)) + return false; + GeoSouthRectangle other = (GeoSouthRectangle) o; + return other.ULHC.equals(ULHC) && other.URHC.equals(URHC); + } - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return p.intersects(topPlane,notablePoints,topPlanePoints,bounds,leftPlane,rightPlane) || - p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,rightPlane,topPlane) || - p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,leftPlane,topPlane); - } + @Override + public int hashCode() { + int result = ULHC.hashCode(); + result = 31 * result + URHC.hashCode(); + return result; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(topLat).noBottomLatitudeBound() - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" getrelationship with "+path); - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - { - //System.err.println(" some inside"); - return OVERLAPS; - } - - final boolean insideShape = path.isWithin(SOUTH_POLE); - - if (insideRectangle == ALL_INSIDE && insideShape) { - //System.err.println(" inside of each other"); - return OVERLAPS; - } - - if (path.intersects(topPlane,topPlanePoints,leftPlane,rightPlane) || - path.intersects(leftPlane,leftPlanePoints,topPlane,rightPlane) || - path.intersects(rightPlane,rightPlanePoints,leftPlane,topPlane)) { - //System.err.println(" edges intersect"); - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) - { - //System.err.println(" shape inside rectangle"); - return WITHIN; - } - - if (insideShape) { - //System.err.println(" shape contains rectangle"); - return CONTAINS; - } - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoSouthRectangle)) - return false; - GeoSouthRectangle other = (GeoSouthRectangle)o; - return other.ULHC.equals(ULHC) && other.URHC.equals(URHC); - } - - @Override - public int hashCode() { - int result = ULHC.hashCode(); - result = 31 * result + URHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoSouthRectangle: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } + @Override + public String toString() { + return "GeoSouthRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java index 4236a722c5c..9a7991a31ae 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideDegenerateHorizontalLine.java @@ -17,219 +17,215 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Degenerate bounding box wider than PI and limited on two sides (left lon, right lon). -*/ -public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase -{ - public final double latitude; - public final double leftLon; - public final double rightLon; - - public final GeoPoint LHC; - public final GeoPoint RHC; - - public final Plane plane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; +/** + * Degenerate bounding box wider than PI and limited on two sides (left lon, right lon). + */ +public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase { + public final double latitude; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] planePoints; - - public final GeoPoint centerPoint; + public final GeoPoint LHC; + public final GeoPoint RHC; - public final EitherBound eitherBound; - - public final GeoPoint[] edgePoints; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. - * Horizontal angle must be greater than or equal to PI. - */ - public GeoWideDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon) - { - // Argument checking - if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) - throw new IllegalArgumentException("Latitude out of range"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent < Math.PI) - throw new IllegalArgumentException("Width of rectangle too small"); + public final Plane plane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.latitude = latitude; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinLatitude = Math.sin(latitude); - final double cosLatitude = Math.cos(latitude); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the two points - this.LHC = new GeoPoint(sinLatitude,sinLeftLon,cosLatitude,cosLeftLon); - this.RHC = new GeoPoint(sinLatitude,sinRightLon,cosLatitude,cosRightLon); - - this.plane = new Plane(sinLatitude); + public final GeoPoint[] planePoints; - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - double middleLon = (leftLon + rightLon) * 0.5; - double sinMiddleLon = Math.sin(middleLon); - double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinLatitude,sinMiddleLon,cosLatitude,cosMiddleLon); + public final GeoPoint centerPoint; - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final EitherBound eitherBound; - this.planePoints = new GeoPoint[]{LHC,RHC}; - - this.eitherBound = new EitherBound(); - - this.edgePoints = new GeoPoint[]{centerPoint}; + public final GeoPoint[] edgePoints; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. + * Horizontal angle must be greater than or equal to PI. + */ + public GeoWideDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon) { + // Argument checking + if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5) + throw new IllegalArgumentException("Latitude out of range"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent < Math.PI) + throw new IllegalArgumentException("Width of rectangle too small"); + + this.latitude = latitude; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinLatitude = Math.sin(latitude); + final double cosLatitude = Math.cos(latitude); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the two points + this.LHC = new GeoPoint(sinLatitude, sinLeftLon, cosLatitude, cosLeftLon); + this.RHC = new GeoPoint(sinLatitude, sinRightLon, cosLatitude, cosRightLon); + + this.plane = new Plane(sinLatitude); + + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + double middleLon = (leftLon + rightLon) * 0.5; + double sinMiddleLon = Math.sin(middleLon); + double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon); + + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.planePoints = new GeoPoint[]{LHC, RHC}; + + this.eitherBound = new EitherBound(); + + this.edgePoints = new GeoPoint[]{centerPoint}; + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = latitude + angle; + final double newBottomLat = latitude - angle; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + if (point == null) + return false; + return plane.evaluateIsZero(point) && + (leftPlane.isWithin(point) || + rightPlane.isWithin(point)); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return plane.evaluateIsZero(x, y, z) && + (leftPlane.isWithin(x, y, z) || + rightPlane.isWithin(x, y, z)); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double topAngle = centerPoint.arcDistance(RHC); + final double bottomAngle = centerPoint.arcDistance(LHC); + return Math.max(topAngle, bottomAngle); + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one + // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. + return p.intersects(plane, notablePoints, planePoints, bounds, eitherBound); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(latitude) + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + if (path.intersects(plane, planePoints, eitherBound)) { + return OVERLAPS; + } + + if (path.isWithin(centerPoint)) { + return CONTAINS; + } + + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoWideDegenerateHorizontalLine)) + return false; + GeoWideDegenerateHorizontalLine other = (GeoWideDegenerateHorizontalLine) o; + return other.LHC.equals(LHC) && other.RHC.equals(RHC); + } + + @Override + public int hashCode() { + int result = LHC.hashCode(); + result = 31 * result + RHC.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoWideDegenerateHorizontalLine: {latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } + + protected class EitherBound implements Membership { + public EitherBound() { } @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = latitude + angle; - final double newBottomLat = latitude - angle; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + public boolean isWithin(final Vector v) { + return leftPlane.isWithin(v) || rightPlane.isWithin(v); } @Override - public boolean isWithin(final Vector point) - { - if (point == null) - return false; - return plane.evaluateIsZero(point) && - (leftPlane.isWithin(point) || - rightPlane.isWithin(point)); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return plane.evaluateIsZero(x,y,z) && - (leftPlane.isWithin(x,y,z) || - rightPlane.isWithin(x,y,z)); - } - - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double topAngle = centerPoint.arcDistance(RHC); - final double bottomAngle = centerPoint.arcDistance(LHC); - return Math.max(topAngle,bottomAngle); - } - - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one - // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(plane,notablePoints,planePoints,bounds,eitherBound); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(latitude) - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - if (path.intersects(plane,planePoints,eitherBound)) { - return OVERLAPS; - } - - if (path.isWithin(centerPoint)) { - return CONTAINS; - } - - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoWideDegenerateHorizontalLine)) - return false; - GeoWideDegenerateHorizontalLine other = (GeoWideDegenerateHorizontalLine)o; - return other.LHC.equals(LHC) && other.RHC.equals(RHC); - } - - @Override - public int hashCode() { - int result = LHC.hashCode(); - result = 31 * result + RHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoWideDegenerateHorizontalLine: {latitude="+latitude+"("+latitude*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightLon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } - - protected class EitherBound implements Membership { - public EitherBound() { - } - - @Override - public boolean isWithin(final Vector v) { - return leftPlane.isWithin(v) || rightPlane.isWithin(v); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) { - return leftPlane.isWithin(x,y,z) || rightPlane.isWithin(x,y,z); - } + public boolean isWithin(final double x, final double y, final double z) { + return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z); } + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java index 607f482e03c..73857ed7a73 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideLongitudeSlice.java @@ -17,186 +17,182 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box wider than PI but limited on left and right sides ( -* left lon, right lon). -*/ -public class GeoWideLongitudeSlice extends GeoBBoxBase -{ - public final double leftLon; - public final double rightLon; - - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; - - public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE,SOUTH_POLE}; - - public final GeoPoint centerPoint; +/** + * Bounding box wider than PI but limited on left and right sides ( + * left lon, right lon). + */ +public class GeoWideLongitudeSlice extends GeoBBoxBase { + public final double leftLon; + public final double rightLon; - public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; - - /** Accepts only values in the following ranges: lon: {@code -PI -> PI}. - * Horizantal angle must be greater than or equal to PI. - */ - public GeoWideLongitudeSlice(final double leftLon, double rightLon) - { - // Argument checking - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent < Math.PI) - throw new IllegalArgumentException("Width of rectangle too small"); + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); + public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE}; - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - this.centerPoint = new GeoPoint(0.0,middleLon); - - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final GeoPoint centerPoint; + + public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + + /** + * Accepts only values in the following ranges: lon: {@code -PI -> PI}. + * Horizantal angle must be greater than or equal to PI. + */ + public GeoWideLongitudeSlice(final double leftLon, double rightLon) { + // Argument checking + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; } + if (extent < Math.PI) + throw new IllegalArgumentException("Width of rectangle too small"); - @Override - public GeoBBox expand(final double angle) - { - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5,-Math.PI * 0.5,newLeftLon,newRightLon); + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; } + final double middleLon = (leftLon + rightLon) * 0.5; + this.centerPoint = new GeoPoint(0.0, middleLon); - @Override - public boolean isWithin(final Vector point) - { - return leftPlane.isWithin(point) || - rightPlane.isWithin(point); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + } + + @Override + public GeoBBox expand(final double angle) { + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; } + return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon); + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return leftPlane.isWithin(x,y,z) || - rightPlane.isWithin(x,y,z); - } + @Override + public boolean isWithin(final Vector point) { + return leftPlane.isWithin(point) || + rightPlane.isWithin(point); + } - @Override - public double getRadius() - { - // Compute the extent and divide by two - double extent = rightLon - leftLon; - if (extent < 0.0) - extent += Math.PI * 2.0; - return Math.max(Math.PI * 0.5, extent * 0.5); - } + @Override + public boolean isWithin(final double x, final double y, final double z) { + return leftPlane.isWithin(x, y, z) || + rightPlane.isWithin(x, y, z); + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } + @Override + public double getRadius() { + // Compute the extent and divide by two + double extent = rightLon - leftLon; + if (extent < 0.0) + extent += Math.PI * 2.0; + return Math.max(Math.PI * 0.5, extent * 0.5); + } - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one - // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(leftPlane,notablePoints,planePoints,bounds) || - p.intersects(rightPlane,notablePoints,planePoints,bounds); - } + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noTopLatitudeBound().noBottomLatitudeBound(); - bounds.addLongitudeSlice(leftLon,rightLon); - return bounds; - } + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - @Override - public int getRelationship(final GeoShape path) { - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) - return OVERLAPS; + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one + // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. + return p.intersects(leftPlane, notablePoints, planePoints, bounds) || + p.intersects(rightPlane, notablePoints, planePoints, bounds); + } - final boolean insideShape = path.isWithin(NORTH_POLE); - - if (insideRectangle == ALL_INSIDE && insideShape) - return OVERLAPS; + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noTopLatitudeBound().noBottomLatitudeBound(); + bounds.addLongitudeSlice(leftLon, rightLon); + return bounds; + } - if (path.intersects(leftPlane,planePoints) || - path.intersects(rightPlane,planePoints)) - return OVERLAPS; + @Override + public int getRelationship(final GeoShape path) { + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) + return OVERLAPS; - if (insideRectangle == ALL_INSIDE) - return WITHIN; + final boolean insideShape = path.isWithin(NORTH_POLE); - if (insideShape) - return CONTAINS; - - return DISJOINT; - } + if (insideRectangle == ALL_INSIDE && insideShape) + return OVERLAPS; - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoWideLongitudeSlice)) - return false; - GeoWideLongitudeSlice other = (GeoWideLongitudeSlice)o; - return other.leftLon == leftLon && other.rightLon == rightLon; - } + if (path.intersects(leftPlane, planePoints) || + path.intersects(rightPlane, planePoints)) + return OVERLAPS; - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(leftLon); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(rightLon); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "GeoWideLongitudeSlice: {leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } + if (insideRectangle == ALL_INSIDE) + return WITHIN; + + if (insideShape) + return CONTAINS; + + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoWideLongitudeSlice)) + return false; + GeoWideLongitudeSlice other = (GeoWideLongitudeSlice) o; + return other.leftLon == leftLon && other.rightLon == rightLon; + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(leftLon); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(rightLon); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public String toString() { + return "GeoWideLongitudeSlice: {leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java index f984a0f894c..e1a4f1bcd41 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideNorthRectangle.java @@ -17,255 +17,250 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box wider than PI but limited on three sides ( -* bottom lat, left lon, right lon). -*/ -public class GeoWideNorthRectangle extends GeoBBoxBase -{ - public final double bottomLat; - public final double leftLon; - public final double rightLon; - - public final double cosMiddleLat; - - public final GeoPoint LRHC; - public final GeoPoint LLHC; - - public final SidedPlane bottomPlane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; +/** + * Bounding box wider than PI but limited on three sides ( + * bottom lat, left lon, right lon). + */ +public class GeoWideNorthRectangle extends GeoBBoxBase { + public final double bottomLat; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] bottomPlanePoints; - public final GeoPoint[] leftPlanePoints; - public final GeoPoint[] rightPlanePoints; + public final double cosMiddleLat; - public final GeoPoint centerPoint; + public final GeoPoint LRHC; + public final GeoPoint LLHC; - public final EitherBound eitherBound; - - public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. - * Horizontal angle must be greater than or equal to PI. - */ - public GeoWideNorthRectangle(final double bottomLat, final double leftLon, double rightLon) - { - // Argument checking - if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Bottom latitude out of range"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent < Math.PI) - throw new IllegalArgumentException("Width of rectangle too small"); + public final SidedPlane bottomPlane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.bottomLat = bottomLat; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinBottomLat = Math.sin(bottomLat); - final double cosBottomLat = Math.cos(bottomLat); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the four points - this.LRHC = new GeoPoint(sinBottomLat,sinRightLon,cosBottomLat,cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat,sinLeftLon,cosBottomLat,cosLeftLon); - - final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.cosMiddleLat = Math.cos(middleLat); - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon); + public final GeoPoint[] bottomPlanePoints; + public final GeoPoint[] leftPlanePoints; + public final GeoPoint[] rightPlanePoints; - this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final GeoPoint centerPoint; - this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC}; - this.leftPlanePoints = new GeoPoint[]{NORTH_POLE,LLHC}; - this.rightPlanePoints = new GeoPoint[]{NORTH_POLE,LRHC}; + public final EitherBound eitherBound; - this.eitherBound = new EitherBound(); + public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE}; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. + * Horizontal angle must be greater than or equal to PI. + */ + public GeoWideNorthRectangle(final double bottomLat, final double leftLon, double rightLon) { + // Argument checking + if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Bottom latitude out of range"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent < Math.PI) + throw new IllegalArgumentException("Width of rectangle too small"); + + this.bottomLat = bottomLat; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinBottomLat = Math.sin(bottomLat); + final double cosBottomLat = Math.cos(bottomLat); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the four points + this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + + final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.cosMiddleLat = Math.cos(middleLat); + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + + this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC}; + this.leftPlanePoints = new GeoPoint[]{NORTH_POLE, LLHC}; + this.rightPlanePoints = new GeoPoint[]{NORTH_POLE, LRHC}; + + this.eitherBound = new EitherBound(); + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = Math.PI * 0.5; + final double newBottomLat = bottomLat - angle; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return + bottomPlane.isWithin(point) && + (leftPlane.isWithin(point) || + rightPlane.isWithin(point)); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return + bottomPlane.isWithin(x, y, z) && + (leftPlane.isWithin(x, y, z) || + rightPlane.isWithin(x, y, z)); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; + final double bottomAngle = centerPoint.arcDistance(LLHC); + return Math.max(centerAngle, bottomAngle); + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one + // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. + return + p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, eitherBound) || + p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, bottomPlane) || + p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noTopLatitudeBound().addLatitudeZone(bottomLat) + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" comparing to "+path); + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) { + //System.err.println(" some inside"); + return OVERLAPS; + } + + final boolean insideShape = path.isWithin(NORTH_POLE); + + if (insideRectangle == ALL_INSIDE && insideShape) { + //System.err.println(" both inside each other"); + return OVERLAPS; + } + + if ( + path.intersects(bottomPlane, bottomPlanePoints, eitherBound) || + path.intersects(leftPlane, leftPlanePoints, bottomPlane) || + path.intersects(rightPlane, rightPlanePoints, bottomPlane)) { + //System.err.println(" edges intersect"); + return OVERLAPS; + } + + if (insideRectangle == ALL_INSIDE) { + //System.err.println(" shape inside rectangle"); + return WITHIN; + } + + if (insideShape) { + //System.err.println(" rectangle inside shape"); + return CONTAINS; + } + + //System.err.println(" disjoint"); + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoWideNorthRectangle)) + return false; + GeoWideNorthRectangle other = (GeoWideNorthRectangle) o; + return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); + } + + @Override + public int hashCode() { + int result = LLHC.hashCode(); + result = 31 * result + LRHC.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoWideNorthRectangle: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } + + protected class EitherBound implements Membership { + public EitherBound() { } @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = Math.PI * 0.5; - final double newBottomLat = bottomLat - angle; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + public boolean isWithin(final Vector v) { + return leftPlane.isWithin(v) || rightPlane.isWithin(v); } @Override - public boolean isWithin(final Vector point) - { - return - bottomPlane.isWithin(point) && - (leftPlane.isWithin(point) || - rightPlane.isWithin(point)); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return - bottomPlane.isWithin(x,y,z) && - (leftPlane.isWithin(x,y,z) || - rightPlane.isWithin(x,y,z)); - } - - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; - final double bottomAngle = centerPoint.arcDistance(LLHC); - return Math.max(centerAngle,bottomAngle); - } - - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one - // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return - p.intersects(bottomPlane,notablePoints,bottomPlanePoints,bounds,eitherBound) || - p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,bottomPlane) || - p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,bottomPlane); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noTopLatitudeBound().addLatitudeZone(bottomLat) - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" comparing to "+path); - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) { - //System.err.println(" some inside"); - return OVERLAPS; - } - - final boolean insideShape = path.isWithin(NORTH_POLE); - - if (insideRectangle == ALL_INSIDE && insideShape) - { - //System.err.println(" both inside each other"); - return OVERLAPS; - } - - if ( - path.intersects(bottomPlane,bottomPlanePoints,eitherBound) || - path.intersects(leftPlane,leftPlanePoints,bottomPlane) || - path.intersects(rightPlane,rightPlanePoints,bottomPlane)) { - //System.err.println(" edges intersect"); - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) { - //System.err.println(" shape inside rectangle"); - return WITHIN; - } - - if (insideShape) { - //System.err.println(" rectangle inside shape"); - return CONTAINS; - } - - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoWideNorthRectangle)) - return false; - GeoWideNorthRectangle other = (GeoWideNorthRectangle)o; - return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC); - } - - @Override - public int hashCode() { - int result = LLHC.hashCode(); - result = 31 * result + LRHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoWideNorthRectangle: {bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } - - protected class EitherBound implements Membership { - public EitherBound() { - } - - @Override - public boolean isWithin(final Vector v) { - return leftPlane.isWithin(v) || rightPlane.isWithin(v); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) { - return leftPlane.isWithin(x,y,z) || rightPlane.isWithin(x,y,z); - } + public boolean isWithin(final double x, final double y, final double z) { + return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z); } + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java index 324a70aa1c8..352a8482ffc 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideRectangle.java @@ -17,273 +17,268 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box wider than PI but limited on four sides (top lat, -* bottom lat, left lon, right lon). -*/ -public class GeoWideRectangle extends GeoBBoxBase -{ - public final double topLat; - public final double bottomLat; - public final double leftLon; - public final double rightLon; - - public final double cosMiddleLat; - - public final GeoPoint ULHC; - public final GeoPoint URHC; - public final GeoPoint LRHC; - public final GeoPoint LLHC; - - public final SidedPlane topPlane; - public final SidedPlane bottomPlane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; +/** + * Bounding box wider than PI but limited on four sides (top lat, + * bottom lat, left lon, right lon). + */ +public class GeoWideRectangle extends GeoBBoxBase { + public final double topLat; + public final double bottomLat; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] topPlanePoints; - public final GeoPoint[] bottomPlanePoints; - public final GeoPoint[] leftPlanePoints; - public final GeoPoint[] rightPlanePoints; + public final double cosMiddleLat; - public final GeoPoint centerPoint; + public final GeoPoint ULHC; + public final GeoPoint URHC; + public final GeoPoint LRHC; + public final GeoPoint LLHC; - public final EitherBound eitherBound; - - public final GeoPoint[] edgePoints; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. - * Horizontal angle must be greater than or equal to PI. - */ - public GeoWideRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon) - { - // Argument checking - if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Top latitude out of range"); - if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Bottom latitude out of range"); - if (topLat < bottomLat) - throw new IllegalArgumentException("Top latitude less than bottom latitude"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent < Math.PI) - throw new IllegalArgumentException("Width of rectangle too small"); + public final SidedPlane topPlane; + public final SidedPlane bottomPlane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.topLat = topLat; - this.bottomLat = bottomLat; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinTopLat = Math.sin(topLat); - final double cosTopLat = Math.cos(topLat); - final double sinBottomLat = Math.sin(bottomLat); - final double cosBottomLat = Math.cos(bottomLat); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the four points - this.ULHC = new GeoPoint(sinTopLat,sinLeftLon,cosTopLat,cosLeftLon); - this.URHC = new GeoPoint(sinTopLat,sinRightLon,cosTopLat,cosRightLon); - this.LRHC = new GeoPoint(sinBottomLat,sinRightLon,cosBottomLat,cosRightLon); - this.LLHC = new GeoPoint(sinBottomLat,sinLeftLon,cosBottomLat,cosLeftLon); - - final double middleLat = (topLat + bottomLat) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.cosMiddleLat = Math.cos(middleLat); - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon); + public final GeoPoint[] topPlanePoints; + public final GeoPoint[] bottomPlanePoints; + public final GeoPoint[] leftPlanePoints; + public final GeoPoint[] rightPlanePoints; - this.topPlane = new SidedPlane(centerPoint,sinTopLat); - this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final GeoPoint centerPoint; - this.topPlanePoints = new GeoPoint[]{ULHC,URHC}; - this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC}; - this.leftPlanePoints = new GeoPoint[]{ULHC,LLHC}; - this.rightPlanePoints = new GeoPoint[]{URHC,LRHC}; + public final EitherBound eitherBound; - this.eitherBound = new EitherBound(); - - this.edgePoints = new GeoPoint[]{ULHC}; + public final GeoPoint[] edgePoints; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. + * Horizontal angle must be greater than or equal to PI. + */ + public GeoWideRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon) { + // Argument checking + if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Top latitude out of range"); + if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Bottom latitude out of range"); + if (topLat < bottomLat) + throw new IllegalArgumentException("Top latitude less than bottom latitude"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent < Math.PI) + throw new IllegalArgumentException("Width of rectangle too small"); + + this.topLat = topLat; + this.bottomLat = bottomLat; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinTopLat = Math.sin(topLat); + final double cosTopLat = Math.cos(topLat); + final double sinBottomLat = Math.sin(bottomLat); + final double cosBottomLat = Math.cos(bottomLat); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the four points + this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); + this.LRHC = new GeoPoint(sinBottomLat, sinRightLon, cosBottomLat, cosRightLon); + this.LLHC = new GeoPoint(sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon); + + final double middleLat = (topLat + bottomLat) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.cosMiddleLat = Math.cos(middleLat); + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + + this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.bottomPlane = new SidedPlane(centerPoint, sinBottomLat); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.topPlanePoints = new GeoPoint[]{ULHC, URHC}; + this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC}; + this.leftPlanePoints = new GeoPoint[]{ULHC, LLHC}; + this.rightPlanePoints = new GeoPoint[]{URHC, LRHC}; + + this.eitherBound = new EitherBound(); + + this.edgePoints = new GeoPoint[]{ULHC}; + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = bottomLat - angle; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return topPlane.isWithin(point) && + bottomPlane.isWithin(point) && + (leftPlane.isWithin(point) || + rightPlane.isWithin(point)); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return topPlane.isWithin(x, y, z) && + bottomPlane.isWithin(x, y, z) && + (leftPlane.isWithin(x, y, z) || + rightPlane.isWithin(x, y, z)); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; + final double topAngle = centerPoint.arcDistance(URHC); + final double bottomAngle = centerPoint.arcDistance(LLHC); + return Math.max(centerAngle, Math.max(topAngle, bottomAngle)); + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one + // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. + return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, eitherBound) || + p.intersects(bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, eitherBound) || + p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, topPlane, bottomPlane) || + p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat) + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" comparing to "+path); + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) { + //System.err.println(" some inside"); + return OVERLAPS; + } + + final boolean insideShape = path.isWithin(ULHC); + + if (insideRectangle == ALL_INSIDE && insideShape) { + //System.err.println(" both inside each other"); + return OVERLAPS; + } + + if (path.intersects(topPlane, topPlanePoints, bottomPlane, eitherBound) || + path.intersects(bottomPlane, bottomPlanePoints, topPlane, eitherBound) || + path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane) || + path.intersects(rightPlane, rightPlanePoints, topPlane, bottomPlane)) { + //System.err.println(" edges intersect"); + return OVERLAPS; + } + + if (insideRectangle == ALL_INSIDE) { + //System.err.println(" shape inside rectangle"); + return WITHIN; + } + + if (insideShape) { + //System.err.println(" rectangle inside shape"); + return CONTAINS; + } + + //System.err.println(" disjoint"); + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoWideRectangle)) + return false; + GeoWideRectangle other = (GeoWideRectangle) o; + return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); + } + + @Override + public int hashCode() { + int result = ULHC.hashCode(); + result = 31 * result + LRHC.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoWideRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } + + protected class EitherBound implements Membership { + public EitherBound() { } @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = bottomLat - angle; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + public boolean isWithin(final Vector v) { + return leftPlane.isWithin(v) || rightPlane.isWithin(v); } @Override - public boolean isWithin(final Vector point) - { - return topPlane.isWithin(point) && - bottomPlane.isWithin(point) && - (leftPlane.isWithin(point) || - rightPlane.isWithin(point)); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return topPlane.isWithin(x,y,z) && - bottomPlane.isWithin(x,y,z) && - (leftPlane.isWithin(x,y,z) || - rightPlane.isWithin(x,y,z)); - } - - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; - final double topAngle = centerPoint.arcDistance(URHC); - final double bottomAngle = centerPoint.arcDistance(LLHC); - return Math.max(centerAngle,Math.max(topAngle,bottomAngle)); - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one - // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(topPlane,notablePoints,topPlanePoints,bounds,bottomPlane,eitherBound) || - p.intersects(bottomPlane,notablePoints,bottomPlanePoints,bounds,topPlane,eitherBound) || - p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,topPlane,bottomPlane) || - p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,topPlane,bottomPlane); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(topLat).addLatitudeZone(bottomLat) - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" comparing to "+path); - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) { - //System.err.println(" some inside"); - return OVERLAPS; - } - - final boolean insideShape = path.isWithin(ULHC); - - if (insideRectangle == ALL_INSIDE && insideShape) - { - //System.err.println(" both inside each other"); - return OVERLAPS; - } - - if (path.intersects(topPlane,topPlanePoints,bottomPlane,eitherBound) || - path.intersects(bottomPlane,bottomPlanePoints,topPlane,eitherBound) || - path.intersects(leftPlane,leftPlanePoints,topPlane,bottomPlane) || - path.intersects(rightPlane,rightPlanePoints,topPlane,bottomPlane)) { - //System.err.println(" edges intersect"); - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) { - //System.err.println(" shape inside rectangle"); - return WITHIN; - } - - if (insideShape) { - //System.err.println(" rectangle inside shape"); - return CONTAINS; - } - - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoWideRectangle)) - return false; - GeoWideRectangle other = (GeoWideRectangle)o; - return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC); - } - - @Override - public int hashCode() { - int result = ULHC.hashCode(); - result = 31 * result + LRHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoWideRectangle: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } - - protected class EitherBound implements Membership { - public EitherBound() { - } - - @Override - public boolean isWithin(final Vector v) { - return leftPlane.isWithin(v) || rightPlane.isWithin(v); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) { - return leftPlane.isWithin(x,y,z) || rightPlane.isWithin(x,y,z); - } + public boolean isWithin(final double x, final double y, final double z) { + return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z); } + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java index c596ef03c6d..8bf8f0f06a5 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWideSouthRectangle.java @@ -17,251 +17,246 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box wider than PI but limited on three sides (top lat, -* left lon, right lon). -*/ -public class GeoWideSouthRectangle extends GeoBBoxBase -{ - public final double topLat; - public final double leftLon; - public final double rightLon; - - public final double cosMiddleLat; - - public final GeoPoint ULHC; - public final GeoPoint URHC; - - public final SidedPlane topPlane; - public final SidedPlane leftPlane; - public final SidedPlane rightPlane; +/** + * Bounding box wider than PI but limited on three sides (top lat, + * left lon, right lon). + */ +public class GeoWideSouthRectangle extends GeoBBoxBase { + public final double topLat; + public final double leftLon; + public final double rightLon; - public final GeoPoint[] topPlanePoints; - public final GeoPoint[] leftPlanePoints; - public final GeoPoint[] rightPlanePoints; + public final double cosMiddleLat; - public final GeoPoint centerPoint; + public final GeoPoint ULHC; + public final GeoPoint URHC; - public final EitherBound eitherBound; - - public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE}; - - /** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. - * Horizontal angle must be greater than or equal to PI. - */ - public GeoWideSouthRectangle(final double topLat, final double leftLon, double rightLon) - { - // Argument checking - if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) - throw new IllegalArgumentException("Top latitude out of range"); - if (leftLon < -Math.PI || leftLon > Math.PI) - throw new IllegalArgumentException("Left longitude out of range"); - if (rightLon < -Math.PI || rightLon > Math.PI) - throw new IllegalArgumentException("Right longitude out of range"); - double extent = rightLon - leftLon; - if (extent < 0.0) { - extent += 2.0 * Math.PI; - } - if (extent < Math.PI) - throw new IllegalArgumentException("Width of rectangle too small"); + public final SidedPlane topPlane; + public final SidedPlane leftPlane; + public final SidedPlane rightPlane; - this.topLat = topLat; - this.leftLon = leftLon; - this.rightLon = rightLon; - - final double sinTopLat = Math.sin(topLat); - final double cosTopLat = Math.cos(topLat); - final double sinLeftLon = Math.sin(leftLon); - final double cosLeftLon = Math.cos(leftLon); - final double sinRightLon = Math.sin(rightLon); - final double cosRightLon = Math.cos(rightLon); - - // Now build the four points - this.ULHC = new GeoPoint(sinTopLat,sinLeftLon,cosTopLat,cosLeftLon); - this.URHC = new GeoPoint(sinTopLat,sinRightLon,cosTopLat,cosRightLon); - - final double middleLat = (topLat - Math.PI * 0.5) * 0.5; - final double sinMiddleLat = Math.sin(middleLat); - this.cosMiddleLat = Math.cos(middleLat); - // Normalize - while (leftLon > rightLon) { - rightLon += Math.PI * 2.0; - } - final double middleLon = (leftLon + rightLon) * 0.5; - final double sinMiddleLon = Math.sin(middleLon); - final double cosMiddleLon = Math.cos(middleLon); - - this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon); + public final GeoPoint[] topPlanePoints; + public final GeoPoint[] leftPlanePoints; + public final GeoPoint[] rightPlanePoints; - this.topPlane = new SidedPlane(centerPoint,sinTopLat); - this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon); - this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon); + public final GeoPoint centerPoint; - this.topPlanePoints = new GeoPoint[]{ULHC,URHC}; - this.leftPlanePoints = new GeoPoint[]{ULHC,SOUTH_POLE}; - this.rightPlanePoints = new GeoPoint[]{URHC,SOUTH_POLE}; + public final EitherBound eitherBound; - this.eitherBound = new EitherBound(); + public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE}; + + /** + * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}. + * Horizontal angle must be greater than or equal to PI. + */ + public GeoWideSouthRectangle(final double topLat, final double leftLon, double rightLon) { + // Argument checking + if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5) + throw new IllegalArgumentException("Top latitude out of range"); + if (leftLon < -Math.PI || leftLon > Math.PI) + throw new IllegalArgumentException("Left longitude out of range"); + if (rightLon < -Math.PI || rightLon > Math.PI) + throw new IllegalArgumentException("Right longitude out of range"); + double extent = rightLon - leftLon; + if (extent < 0.0) { + extent += 2.0 * Math.PI; + } + if (extent < Math.PI) + throw new IllegalArgumentException("Width of rectangle too small"); + + this.topLat = topLat; + this.leftLon = leftLon; + this.rightLon = rightLon; + + final double sinTopLat = Math.sin(topLat); + final double cosTopLat = Math.cos(topLat); + final double sinLeftLon = Math.sin(leftLon); + final double cosLeftLon = Math.cos(leftLon); + final double sinRightLon = Math.sin(rightLon); + final double cosRightLon = Math.cos(rightLon); + + // Now build the four points + this.ULHC = new GeoPoint(sinTopLat, sinLeftLon, cosTopLat, cosLeftLon); + this.URHC = new GeoPoint(sinTopLat, sinRightLon, cosTopLat, cosRightLon); + + final double middleLat = (topLat - Math.PI * 0.5) * 0.5; + final double sinMiddleLat = Math.sin(middleLat); + this.cosMiddleLat = Math.cos(middleLat); + // Normalize + while (leftLon > rightLon) { + rightLon += Math.PI * 2.0; + } + final double middleLon = (leftLon + rightLon) * 0.5; + final double sinMiddleLon = Math.sin(middleLon); + final double cosMiddleLon = Math.cos(middleLon); + + this.centerPoint = new GeoPoint(sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon); + + this.topPlane = new SidedPlane(centerPoint, sinTopLat); + this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon); + this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon); + + this.topPlanePoints = new GeoPoint[]{ULHC, URHC}; + this.leftPlanePoints = new GeoPoint[]{ULHC, SOUTH_POLE}; + this.rightPlanePoints = new GeoPoint[]{URHC, SOUTH_POLE}; + + this.eitherBound = new EitherBound(); + } + + @Override + public GeoBBox expand(final double angle) { + final double newTopLat = topLat + angle; + final double newBottomLat = -Math.PI * 0.5; + // Figuring out when we escalate to a special case requires some prefiguring + double currentLonSpan = rightLon - leftLon; + if (currentLonSpan < 0.0) + currentLonSpan += Math.PI * 2.0; + double newLeftLon = leftLon - angle; + double newRightLon = rightLon + angle; + if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { + newLeftLon = -Math.PI; + newRightLon = Math.PI; + } + return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon); + } + + @Override + public boolean isWithin(final Vector point) { + return topPlane.isWithin(point) && + (leftPlane.isWithin(point) || + rightPlane.isWithin(point)); + } + + @Override + public boolean isWithin(final double x, final double y, final double z) { + return topPlane.isWithin(x, y, z) && + (leftPlane.isWithin(x, y, z) || + rightPlane.isWithin(x, y, z)); + } + + @Override + public double getRadius() { + // Here we compute the distance from the middle point to one of the corners. However, we need to be careful + // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and + // the distance to the right or left edge from the center. + final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; + final double topAngle = centerPoint.arcDistance(URHC); + return Math.max(centerAngle, topAngle); + } + + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + return centerPoint; + } + + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } + + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one + // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. + return p.intersects(topPlane, notablePoints, topPlanePoints, bounds, eitherBound) || + p.intersects(leftPlane, notablePoints, leftPlanePoints, bounds, topPlane) || + p.intersects(rightPlane, notablePoints, rightPlanePoints, bounds, topPlane); + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.addLatitudeZone(topLat).noBottomLatitudeBound() + .addLongitudeSlice(leftLon, rightLon); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + //System.err.println(this+" comparing to "+path); + final int insideRectangle = isShapeInsideBBox(path); + if (insideRectangle == SOME_INSIDE) { + //System.err.println(" some inside"); + return OVERLAPS; + } + + final boolean insideShape = path.isWithin(SOUTH_POLE); + + if (insideRectangle == ALL_INSIDE && insideShape) { + //System.err.println(" both inside each other"); + return OVERLAPS; + } + + if (path.intersects(topPlane, topPlanePoints, eitherBound) || + path.intersects(leftPlane, leftPlanePoints, topPlane) || + path.intersects(rightPlane, rightPlanePoints, topPlane)) { + //System.err.println(" edges intersect"); + return OVERLAPS; + } + + if (insideRectangle == ALL_INSIDE) { + //System.err.println(" shape inside rectangle"); + return WITHIN; + } + + if (insideShape) { + //System.err.println(" rectangle inside shape"); + return CONTAINS; + } + + //System.err.println(" disjoint"); + return DISJOINT; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoWideSouthRectangle)) + return false; + GeoWideSouthRectangle other = (GeoWideSouthRectangle) o; + return other.ULHC.equals(ULHC) && other.URHC.equals(URHC); + } + + @Override + public int hashCode() { + int result = ULHC.hashCode(); + result = 31 * result + URHC.hashCode(); + return result; + } + + @Override + public String toString() { + return "GeoWideSouthRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}"; + } + + protected class EitherBound implements Membership { + public EitherBound() { } @Override - public GeoBBox expand(final double angle) - { - final double newTopLat = topLat + angle; - final double newBottomLat = -Math.PI * 0.5; - // Figuring out when we escalate to a special case requires some prefiguring - double currentLonSpan = rightLon - leftLon; - if (currentLonSpan < 0.0) - currentLonSpan += Math.PI * 2.0; - double newLeftLon = leftLon - angle; - double newRightLon = rightLon + angle; - if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) { - newLeftLon = -Math.PI; - newRightLon = Math.PI; - } - return GeoBBoxFactory.makeGeoBBox(newTopLat,newBottomLat,newLeftLon,newRightLon); + public boolean isWithin(final Vector v) { + return leftPlane.isWithin(v) || rightPlane.isWithin(v); } @Override - public boolean isWithin(final Vector point) - { - return topPlane.isWithin(point) && - (leftPlane.isWithin(point) || - rightPlane.isWithin(point)); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return topPlane.isWithin(x,y,z) && - (leftPlane.isWithin(x,y,z) || - rightPlane.isWithin(x,y,z)); - } - - @Override - public double getRadius() - { - // Here we compute the distance from the middle point to one of the corners. However, we need to be careful - // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and - // the distance to the right or left edge from the center. - final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat; - final double topAngle = centerPoint.arcDistance(URHC); - return Math.max(centerAngle,topAngle); - } - - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - return centerPoint; - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one - // requires crossing into the right part of the other. So intersection can ignore the left/right bounds. - return p.intersects(topPlane,notablePoints,topPlanePoints,bounds,eitherBound) || - p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,topPlane) || - p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,topPlane); - } - - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.addLatitudeZone(topLat).noBottomLatitudeBound() - .addLongitudeSlice(leftLon,rightLon); - return bounds; - } - - @Override - public int getRelationship(final GeoShape path) { - //System.err.println(this+" comparing to "+path); - final int insideRectangle = isShapeInsideBBox(path); - if (insideRectangle == SOME_INSIDE) { - //System.err.println(" some inside"); - return OVERLAPS; - } - - final boolean insideShape = path.isWithin(SOUTH_POLE); - - if (insideRectangle == ALL_INSIDE && insideShape) - { - //System.err.println(" both inside each other"); - return OVERLAPS; - } - - if (path.intersects(topPlane,topPlanePoints,eitherBound) || - path.intersects(leftPlane,leftPlanePoints,topPlane) || - path.intersects(rightPlane,rightPlanePoints,topPlane)) { - //System.err.println(" edges intersect"); - return OVERLAPS; - } - - if (insideRectangle == ALL_INSIDE) { - //System.err.println(" shape inside rectangle"); - return WITHIN; - } - - if (insideShape) { - //System.err.println(" rectangle inside shape"); - return CONTAINS; - } - - //System.err.println(" disjoint"); - return DISJOINT; - } - - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoWideSouthRectangle)) - return false; - GeoWideSouthRectangle other = (GeoWideSouthRectangle)o; - return other.ULHC.equals(ULHC) && other.URHC.equals(URHC); - } - - @Override - public int hashCode() { - int result = ULHC.hashCode(); - result = 31 * result + URHC.hashCode(); - return result; - } - - @Override - public String toString() { - return "GeoWideSouthRectangle: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}"; - } - - protected class EitherBound implements Membership { - public EitherBound() { - } - - @Override - public boolean isWithin(final Vector v) { - return leftPlane.isWithin(v) || rightPlane.isWithin(v); - } - - @Override - public boolean isWithin(final double x, final double y, final double z) { - return leftPlane.isWithin(x,y,z) || rightPlane.isWithin(x,y,z); - } + public boolean isWithin(final double x, final double y, final double z) { + return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z); } + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java index 20bb15447d7..a4ebb6bf0d3 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/GeoWorld.java @@ -17,102 +17,97 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Bounding box including the entire world. -*/ -public class GeoWorld extends GeoBBoxBase -{ - protected final static GeoPoint originPoint = new GeoPoint(1.0,0.0,0.0); - protected final static GeoPoint[] edgePoints = new GeoPoint[0]; - - public GeoWorld() - { - } - - @Override - public GeoBBox expand(final double angle) - { - return this; - } +/** + * Bounding box including the entire world. + */ +public class GeoWorld extends GeoBBoxBase { + protected final static GeoPoint originPoint = new GeoPoint(1.0, 0.0, 0.0); + protected final static GeoPoint[] edgePoints = new GeoPoint[0]; - @Override - public double getRadius() - { - return Math.PI; - } + public GeoWorld() { + } - /** Returns the center of a circle into which the area will be inscribed. - *@return the center. - */ - @Override - public GeoPoint getCenter() { - // Totally arbitrary - return originPoint; - } + @Override + public GeoBBox expand(final double angle) { + return this; + } - @Override - public boolean isWithin(final Vector point) - { - return true; - } + @Override + public double getRadius() { + return Math.PI; + } - @Override - public boolean isWithin(final double x, final double y, final double z) - { - return true; - } - - @Override - public GeoPoint[] getEdgePoints() - { - return edgePoints; - } - - @Override - public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) - { - return false; - } + /** + * Returns the center of a circle into which the area will be inscribed. + * + * @return the center. + */ + @Override + public GeoPoint getCenter() { + // Totally arbitrary + return originPoint; + } - /** Compute longitude/latitude bounds for the shape. - *@param bounds is the optional input bounds object. If this is null, - * a bounds object will be created. Otherwise, the input object will be modified. - *@return a Bounds object describing the shape's bounds. If the bounds cannot - * be computed, then return a Bounds object with noLongitudeBound, - * noTopLatitudeBound, and noBottomLatitudeBound. - */ - @Override - public Bounds getBounds(Bounds bounds) - { - if (bounds == null) - bounds = new Bounds(); - bounds.noLongitudeBound().noTopLatitudeBound().noBottomLatitudeBound(); - return bounds; - } + @Override + public boolean isWithin(final Vector point) { + return true; + } - @Override - public int getRelationship(final GeoShape path) { - if (path.getEdgePoints().length > 0) - // Path is always within the world - return WITHIN; - - return OVERLAPS; - } + @Override + public boolean isWithin(final double x, final double y, final double z) { + return true; + } - @Override - public boolean equals(Object o) - { - if (!(o instanceof GeoWorld)) - return false; - return true; - } + @Override + public GeoPoint[] getEdgePoints() { + return edgePoints; + } - @Override - public int hashCode() { - return 0; - } - - @Override - public String toString() { - return "GeoWorld"; - } + @Override + public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) { + return false; + } + + /** + * Compute longitude/latitude bounds for the shape. + * + * @param bounds is the optional input bounds object. If this is null, + * a bounds object will be created. Otherwise, the input object will be modified. + * @return a Bounds object describing the shape's bounds. If the bounds cannot + * be computed, then return a Bounds object with noLongitudeBound, + * noTopLatitudeBound, and noBottomLatitudeBound. + */ + @Override + public Bounds getBounds(Bounds bounds) { + if (bounds == null) + bounds = new Bounds(); + bounds.noLongitudeBound().noTopLatitudeBound().noBottomLatitudeBound(); + return bounds; + } + + @Override + public int getRelationship(final GeoShape path) { + if (path.getEdgePoints().length > 0) + // Path is always within the world + return WITHIN; + + return OVERLAPS; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof GeoWorld)) + return false; + return true; + } + + @Override + public int hashCode() { + return 0; + } + + @Override + public String toString() { + return "GeoWorld"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java index 76364b8579a..2a70342e886 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Membership.java @@ -17,22 +17,27 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Interface describing 3d shape membership methods. -*/ +/** + * Interface describing 3d shape membership methods. + */ public interface Membership { - /** Check if a point is within this shape. - *@param point is the point to check. - *@return true if the point is within this shape - */ - public boolean isWithin(final Vector point); + /** + * Check if a point is within this shape. + * + * @param point is the point to check. + * @return true if the point is within this shape + */ + public boolean isWithin(final Vector point); - /** Check if a point is within this shape. - *@param x is x coordinate of point to check. - *@param y is y coordinate of point to check. - *@param z is z coordinate of point to check. - *@return true if the point is within this shape - */ - public boolean isWithin(final double x, final double y, final double z); + /** + * Check if a point is within this shape. + * + * @param x is x coordinate of point to check. + * @param y is y coordinate of point to check. + * @param z is z coordinate of point to check. + * @return true if the point is within this shape + */ + public boolean isWithin(final double x, final double y, final double z); } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java index 5d63e312d35..0b1a9e8f981 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Plane.java @@ -17,1034 +17,1066 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** We know about three kinds of planes. First kind: general plain through two points and origin -* Second kind: horizontal plane at specified height. Third kind: vertical plane with specified x and y value, through origin. -*/ -public class Plane extends Vector -{ - protected final static GeoPoint[] NO_POINTS = new GeoPoint[0]; - protected final static Membership[] NO_BOUNDS = new Membership[0]; - - public final double D; - - /** Construct a plane with all four coefficients defined. - */ - public Plane(final double A, final double B, final double C, final double D) { - super(A,B,C); - this.D = D; - } - - /** Construct a plane through two points and origin. - *@param A is the first point (origin based). - *@param B is the second point (origin based). - */ - public Plane(final Vector A, final Vector B) { - super(A,B); - D = 0.0; - } - - /** Construct a horizontal plane at a specified Z. - *@param height is the specified Z coordinate. - */ - public Plane(final double height) { - super(0.0,0.0,1.0); - D = -height; - } - - /** Construct a vertical plane through a specified - * x, y and origin. - *@param x is the specified x value. - *@param y is the specified y value. - */ - public Plane(final double x, final double y) { - super(y,-x,0.0); - D = 0.0; - } - - /** Construct a plane with a specific vector, and D offset - * from origin. - *@param D is the D offset from the origin. - */ - public Plane(final Vector v, final double D) { - super(v.x,v.y,v.z); - this.D = D; - } - - /** Evaluate the plane equation for a given point, as represented - * by a vector. - *@param v is the vector. - *@return the result of the evaluation. - */ - public double evaluate(final Vector v) { - return dotProduct(v) + D; +/** + * We know about three kinds of planes. First kind: general plain through two points and origin + * Second kind: horizontal plane at specified height. Third kind: vertical plane with specified x and y value, through origin. + */ +public class Plane extends Vector { + protected final static GeoPoint[] NO_POINTS = new GeoPoint[0]; + protected final static Membership[] NO_BOUNDS = new Membership[0]; + + public final double D; + + /** + * Construct a plane with all four coefficients defined. + */ + public Plane(final double A, final double B, final double C, final double D) { + super(A, B, C); + this.D = D; + } + + /** + * Construct a plane through two points and origin. + * + * @param A is the first point (origin based). + * @param B is the second point (origin based). + */ + public Plane(final Vector A, final Vector B) { + super(A, B); + D = 0.0; + } + + /** + * Construct a horizontal plane at a specified Z. + * + * @param height is the specified Z coordinate. + */ + public Plane(final double height) { + super(0.0, 0.0, 1.0); + D = -height; + } + + /** + * Construct a vertical plane through a specified + * x, y and origin. + * + * @param x is the specified x value. + * @param y is the specified y value. + */ + public Plane(final double x, final double y) { + super(y, -x, 0.0); + D = 0.0; + } + + /** + * Construct a plane with a specific vector, and D offset + * from origin. + * + * @param D is the D offset from the origin. + */ + public Plane(final Vector v, final double D) { + super(v.x, v.y, v.z); + this.D = D; + } + + /** + * Evaluate the plane equation for a given point, as represented + * by a vector. + * + * @param v is the vector. + * @return the result of the evaluation. + */ + public double evaluate(final Vector v) { + return dotProduct(v) + D; + } + + /** + * Evaluate the plane equation for a given point, as represented + * by a vector. + * + * @param x,y,z is the vector. + * @return the result of the evaluation. + */ + public double evaluate(final double x, final double y, final double z) { + return dotProduct(x, y, z) + D; + } + + /** + * Evaluate the plane equation for a given point, as represented + * by a vector. + * + * @param v is the vector. + * @return true if the result is on the plane. + */ + public boolean evaluateIsZero(final Vector v) { + return Math.abs(evaluate(v)) < MINIMUM_RESOLUTION; + } + + /** + * Evaluate the plane equation for a given point, as represented + * by a vector. + * + * @param x,y,z is the vector. + * @return true if the result is on the plane. + */ + public boolean evaluateIsZero(final double x, final double y, final double z) { + return Math.abs(evaluate(x, y, z)) < MINIMUM_RESOLUTION; + } + + /** + * Build a normalized plane, so that the vector is normalized. + * + * @return the normalized plane object, or null if the plane is indeterminate. + */ + public Plane normalize() { + Vector normVect = super.normalize(); + if (normVect == null) + return null; + return new Plane(normVect, this.D); + } + + /** + * Find points on the boundary of the intersection of a plane and the unit sphere, + * given a starting point, and ending point, and a list of proportions of the arc (e.g. 0.25, 0.5, 0.75). + * The angle between the starting point and ending point is assumed to be less than pi. + */ + public GeoPoint[] interpolate(final GeoPoint start, final GeoPoint end, final double[] proportions) { + // Steps: + // (1) Translate (x0,y0,z0) of endpoints into origin-centered place: + // x1 = x0 + D*A + // y1 = y0 + D*B + // z1 = z0 + D*C + // (2) Rotate counterclockwise in x-y: + // ra = -atan2(B,A) + // x2 = x1 cos ra - y1 sin ra + // y2 = x1 sin ra + y1 cos ra + // z2 = z1 + // Faster: + // cos ra = A/sqrt(A^2+B^2+C^2) + // sin ra = -B/sqrt(A^2+B^2+C^2) + // cos (-ra) = A/sqrt(A^2+B^2+C^2) + // sin (-ra) = B/sqrt(A^2+B^2+C^2) + // (3) Rotate clockwise in x-z: + // ha = pi/2 - asin(C/sqrt(A^2+B^2+C^2)) + // x3 = x2 cos ha - z2 sin ha + // y3 = y2 + // z3 = x2 sin ha + z2 cos ha + // At this point, z3 should be zero. + // Faster: + // sin(ha) = cos(asin(C/sqrt(A^2+B^2+C^2))) = sqrt(1 - C^2/(A^2+B^2+C^2)) = sqrt(A^2+B^2)/sqrt(A^2+B^2+C^2) + // cos(ha) = sin(asin(C/sqrt(A^2+B^2+C^2))) = C/sqrt(A^2+B^2+C^2) + // (4) Compute interpolations by getting longitudes of original points + // la = atan2(y3,x3) + // (5) Rotate new points (xN0, yN0, zN0) counter-clockwise in x-z: + // ha = -(pi - asin(C/sqrt(A^2+B^2+C^2))) + // xN1 = xN0 cos ha - zN0 sin ha + // yN1 = yN0 + // zN1 = xN0 sin ha + zN0 cos ha + // (6) Rotate new points clockwise in x-y: + // ra = atan2(B,A) + // xN2 = xN1 cos ra - yN1 sin ra + // yN2 = xN1 sin ra + yN1 cos ra + // zN2 = zN1 + // (7) Translate new points: + // xN3 = xN2 - D*A + // yN3 = yN2 - D*B + // zN3 = zN2 - D*C + + // First, calculate the angles and their sin/cos values + double A = x; + double B = y; + double C = z; + + // Translation amounts + final double transX = -D * A; + final double transY = -D * B; + final double transZ = -D * C; + + double cosRA; + double sinRA; + double cosHA; + double sinHA; + + double magnitude = magnitude(); + if (magnitude >= MINIMUM_RESOLUTION) { + final double denom = 1.0 / magnitude; + A *= denom; + B *= denom; + C *= denom; + + // cos ra = A/sqrt(A^2+B^2+C^2) + // sin ra = -B/sqrt(A^2+B^2+C^2) + // cos (-ra) = A/sqrt(A^2+B^2+C^2) + // sin (-ra) = B/sqrt(A^2+B^2+C^2) + final double xyMagnitude = Math.sqrt(A * A + B * B); + if (xyMagnitude >= MINIMUM_RESOLUTION) { + final double xyDenom = 1.0 / xyMagnitude; + cosRA = A * xyDenom; + sinRA = -B * xyDenom; + } else { + cosRA = 1.0; + sinRA = 0.0; + } + + // sin(ha) = cos(asin(C/sqrt(A^2+B^2+C^2))) = sqrt(1 - C^2/(A^2+B^2+C^2)) = sqrt(A^2+B^2)/sqrt(A^2+B^2+C^2) + // cos(ha) = sin(asin(C/sqrt(A^2+B^2+C^2))) = C/sqrt(A^2+B^2+C^2) + sinHA = xyMagnitude; + cosHA = C; + } else { + cosRA = 1.0; + sinRA = 0.0; + cosHA = 1.0; + sinHA = 0.0; } - /** Evaluate the plane equation for a given point, as represented - * by a vector. - *@param x,y,z is the vector. - *@return the result of the evaluation. - */ - public double evaluate(final double x, final double y, final double z) { - return dotProduct(x,y,z) + D; + // Forward-translate the start and end points + final Vector modifiedStart = modify(start, transX, transY, transZ, sinRA, cosRA, sinHA, cosHA); + final Vector modifiedEnd = modify(end, transX, transY, transZ, sinRA, cosRA, sinHA, cosHA); + if (Math.abs(modifiedStart.z) >= MINIMUM_RESOLUTION) + throw new IllegalArgumentException("Start point was not on plane: " + modifiedStart.z); + if (Math.abs(modifiedEnd.z) >= MINIMUM_RESOLUTION) + throw new IllegalArgumentException("End point was not on plane: " + modifiedEnd.z); + + // Compute the angular distance between start and end point + final double startAngle = Math.atan2(modifiedStart.y, modifiedStart.x); + final double endAngle = Math.atan2(modifiedEnd.y, modifiedEnd.x); + + final double startMagnitude = Math.sqrt(modifiedStart.x * modifiedStart.x + modifiedStart.y * modifiedStart.y); + double delta; + double beginAngle; + + double newEndAngle = endAngle; + while (newEndAngle < startAngle) { + newEndAngle += Math.PI * 2.0; } - /** Evaluate the plane equation for a given point, as represented - * by a vector. - *@param v is the vector. - *@return true if the result is on the plane. - */ - public boolean evaluateIsZero(final Vector v) { - return Math.abs(evaluate(v)) < MINIMUM_RESOLUTION; + if (newEndAngle - startAngle <= Math.PI) { + delta = newEndAngle - startAngle; + beginAngle = startAngle; + } else { + double newStartAngle = startAngle; + while (newStartAngle < endAngle) { + newStartAngle += Math.PI * 2.0; + } + delta = newStartAngle - endAngle; + beginAngle = endAngle; } - /** Evaluate the plane equation for a given point, as represented - * by a vector. - *@param x,y,z is the vector. - *@return true if the result is on the plane. - */ - public boolean evaluateIsZero(final double x, final double y, final double z) { - return Math.abs(evaluate(x,y,z)) < MINIMUM_RESOLUTION; - } - - /** Build a normalized plane, so that the vector is normalized. - *@return the normalized plane object, or null if the plane is indeterminate. - */ - public Plane normalize() { - Vector normVect = super.normalize(); - if (normVect == null) - return null; - return new Plane(normVect,this.D); + final GeoPoint[] returnValues = new GeoPoint[proportions.length]; + for (int i = 0; i < returnValues.length; i++) { + final double newAngle = startAngle + proportions[i] * delta; + final double sinNewAngle = Math.sin(newAngle); + final double cosNewAngle = Math.cos(newAngle); + final Vector newVector = new Vector(cosNewAngle * startMagnitude, sinNewAngle * startMagnitude, 0.0); + returnValues[i] = reverseModify(newVector, transX, transY, transZ, sinRA, cosRA, sinHA, cosHA); } - /** Find points on the boundary of the intersection of a plane and the unit sphere, - * given a starting point, and ending point, and a list of proportions of the arc (e.g. 0.25, 0.5, 0.75). - * The angle between the starting point and ending point is assumed to be less than pi. - */ - public GeoPoint[] interpolate(final GeoPoint start, final GeoPoint end, final double[] proportions) { - // Steps: - // (1) Translate (x0,y0,z0) of endpoints into origin-centered place: - // x1 = x0 + D*A - // y1 = y0 + D*B - // z1 = z0 + D*C - // (2) Rotate counterclockwise in x-y: - // ra = -atan2(B,A) - // x2 = x1 cos ra - y1 sin ra - // y2 = x1 sin ra + y1 cos ra - // z2 = z1 - // Faster: - // cos ra = A/sqrt(A^2+B^2+C^2) - // sin ra = -B/sqrt(A^2+B^2+C^2) - // cos (-ra) = A/sqrt(A^2+B^2+C^2) - // sin (-ra) = B/sqrt(A^2+B^2+C^2) - // (3) Rotate clockwise in x-z: - // ha = pi/2 - asin(C/sqrt(A^2+B^2+C^2)) - // x3 = x2 cos ha - z2 sin ha - // y3 = y2 - // z3 = x2 sin ha + z2 cos ha - // At this point, z3 should be zero. - // Faster: - // sin(ha) = cos(asin(C/sqrt(A^2+B^2+C^2))) = sqrt(1 - C^2/(A^2+B^2+C^2)) = sqrt(A^2+B^2)/sqrt(A^2+B^2+C^2) - // cos(ha) = sin(asin(C/sqrt(A^2+B^2+C^2))) = C/sqrt(A^2+B^2+C^2) - // (4) Compute interpolations by getting longitudes of original points - // la = atan2(y3,x3) - // (5) Rotate new points (xN0, yN0, zN0) counter-clockwise in x-z: - // ha = -(pi - asin(C/sqrt(A^2+B^2+C^2))) - // xN1 = xN0 cos ha - zN0 sin ha - // yN1 = yN0 - // zN1 = xN0 sin ha + zN0 cos ha - // (6) Rotate new points clockwise in x-y: - // ra = atan2(B,A) - // xN2 = xN1 cos ra - yN1 sin ra - // yN2 = xN1 sin ra + yN1 cos ra - // zN2 = zN1 - // (7) Translate new points: - // xN3 = xN2 - D*A - // yN3 = yN2 - D*B - // zN3 = zN2 - D*C + return returnValues; + } - // First, calculate the angles and their sin/cos values - double A = x; - double B = y; - double C = z; - - // Translation amounts - final double transX = -D * A; - final double transY = -D * B; - final double transZ = -D * C; + /** + * Modify a point to produce a vector in translated/rotated space. + */ + protected static Vector modify(final GeoPoint start, final double transX, final double transY, final double transZ, + final double sinRA, final double cosRA, final double sinHA, final double cosHA) { + return start.translate(transX, transY, transZ).rotateXY(sinRA, cosRA).rotateXZ(sinHA, cosHA); + } + + /** + * Reverse modify a point to produce a GeoPoint in normal space. + */ + protected static GeoPoint reverseModify(final Vector point, final double transX, final double transY, final double transZ, + final double sinRA, final double cosRA, final double sinHA, final double cosHA) { + final Vector result = point.rotateXZ(-sinHA, cosHA).rotateXY(-sinRA, cosRA).translate(-transX, -transY, -transZ); + return new GeoPoint(result.x, result.y, result.z); + } + + /** + * Find the intersection points between two planes, given a set of bounds. + * + * @param q is the plane to intersect with. + * @param bounds is the set of bounds. + * @param moreBounds is another set of bounds. + * @return the intersection point(s) on the unit sphere, if there are any. + */ + protected GeoPoint[] findIntersections(final Plane q, final Membership[] bounds, final Membership[] moreBounds) { + final Vector lineVector = new Vector(this, q); + if (Math.abs(lineVector.x) < MINIMUM_RESOLUTION && Math.abs(lineVector.y) < MINIMUM_RESOLUTION && Math.abs(lineVector.z) < MINIMUM_RESOLUTION) { + // Degenerate case: parallel planes + //System.err.println(" planes are parallel - no intersection"); + return NO_POINTS; + } + + // The line will have the equation: A t + A0 = x, B t + B0 = y, C t + C0 = z. + // We have A, B, and C. In order to come up with A0, B0, and C0, we need to find a point that is on both planes. + // To do this, we find the largest vector value (either x, y, or z), and look for a point that solves both plane equations + // simultaneous. For example, let's say that the vector is (0.5,0.5,1), and the two plane equations are: + // 0.7 x + 0.3 y + 0.1 z + 0.0 = 0 + // and + // 0.9 x - 0.1 y + 0.2 z + 4.0 = 0 + // Then we'd pick z = 0, so the equations to solve for x and y would be: + // 0.7 x + 0.3y = 0.0 + // 0.9 x - 0.1y = -4.0 + // ... which can readily be solved using standard linear algebra. Generally: + // Q0 x + R0 y = S0 + // Q1 x + R1 y = S1 + // ... can be solved by Cramer's rule: + // x = det(S0 R0 / S1 R1) / det(Q0 R0 / Q1 R1) + // y = det(Q0 S0 / Q1 S1) / det(Q0 R0 / Q1 R1) + // ... where det( a b / c d ) = ad - bc, so: + // x = (S0 * R1 - R0 * S1) / (Q0 * R1 - R0 * Q1) + // y = (Q0 * S1 - S0 * Q1) / (Q0 * R1 - R0 * Q1) + double x0; + double y0; + double z0; + // We try to maximize the determinant in the denominator + final double denomYZ = this.y * q.z - this.z * q.y; + final double denomXZ = this.x * q.z - this.z * q.x; + final double denomXY = this.x * q.y - this.y * q.x; + if (Math.abs(denomYZ) >= Math.abs(denomXZ) && Math.abs(denomYZ) >= Math.abs(denomXY)) { + // X is the biggest, so our point will have x0 = 0.0 + if (Math.abs(denomYZ) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" Denominator is zero: no intersection"); + return NO_POINTS; + } + final double denom = 1.0 / denomYZ; + x0 = 0.0; + y0 = (-this.D * q.z - this.z * -q.D) * denom; + z0 = (this.y * -q.D + this.D * q.y) * denom; + } else if (Math.abs(denomXZ) >= Math.abs(denomXY) && Math.abs(denomXZ) >= Math.abs(denomYZ)) { + // Y is the biggest, so y0 = 0.0 + if (Math.abs(denomXZ) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" Denominator is zero: no intersection"); + return NO_POINTS; + } + final double denom = 1.0 / denomXZ; + x0 = (-this.D * q.z - this.z * -q.D) * denom; + y0 = 0.0; + z0 = (this.x * -q.D + this.D * q.x) * denom; + } else { + // Z is the biggest, so Z0 = 0.0 + if (Math.abs(denomXY) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" Denominator is zero: no intersection"); + return NO_POINTS; + } + final double denom = 1.0 / denomXY; + x0 = (-this.D * q.y - this.y * -q.D) * denom; + y0 = (this.x * -q.D + this.D * q.x) * denom; + z0 = 0.0; + } + + // Once an intersecting line is determined, the next step is to intersect that line with the unit sphere, which + // will yield zero, one, or two points. + // The equation of the sphere is: 1.0 = x^2 + y^2 + z^2. Plugging in the parameterized line values yields: + // 1.0 = (At+A0)^2 + (Bt+B0)^2 + (Ct+C0)^2 + // A^2 t^2 + 2AA0t + A0^2 + B^2 t^2 + 2BB0t + B0^2 + C^2 t^2 + 2CC0t + C0^2 - 1,0 = 0.0 + // [A^2 + B^2 + C^2] t^2 + [2AA0 + 2BB0 + 2CC0] t + [A0^2 + B0^2 + C0^2 - 1,0] = 0.0 + // Use the quadratic formula to determine t values and candidate point(s) + final double A = lineVector.x * lineVector.x + lineVector.y * lineVector.y + lineVector.z * lineVector.z; + final double B = 2.0 * (lineVector.x * x0 + lineVector.y * y0 + lineVector.z * z0); + final double C = x0 * x0 + y0 * y0 + z0 * z0 - 1.0; + + final double BsquaredMinus = B * B - 4.0 * A * C; + if (Math.abs(BsquaredMinus) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" One point of intersection"); + final double inverse2A = 1.0 / (2.0 * A); + // One solution only + final double t = -B * inverse2A; + GeoPoint point = new GeoPoint(lineVector.x * t + x0, lineVector.y * t + y0, lineVector.z * t + z0); + if (point.isWithin(bounds, moreBounds)) + return new GeoPoint[]{point}; + return NO_POINTS; + } else if (BsquaredMinus > 0.0) { + //System.err.println(" Two points of intersection"); + final double inverse2A = 1.0 / (2.0 * A); + // Two solutions + final double sqrtTerm = Math.sqrt(BsquaredMinus); + final double t1 = (-B + sqrtTerm) * inverse2A; + final double t2 = (-B - sqrtTerm) * inverse2A; + GeoPoint point1 = new GeoPoint(lineVector.x * t1 + x0, lineVector.y * t1 + y0, lineVector.z * t1 + z0); + GeoPoint point2 = new GeoPoint(lineVector.x * t2 + x0, lineVector.y * t2 + y0, lineVector.z * t2 + z0); + //System.err.println(" "+point1+" and "+point2); + if (point1.isWithin(bounds, moreBounds)) { + if (point2.isWithin(bounds, moreBounds)) + return new GeoPoint[]{point1, point2}; + return new GeoPoint[]{point1}; + } + if (point2.isWithin(bounds, moreBounds)) + return new GeoPoint[]{point2}; + return NO_POINTS; + } else { + //System.err.println(" no solutions - no intersection"); + return NO_POINTS; + } + } + + /** + * Accumulate bounds information for this plane, intersected with another plane + * and with the unit sphere. + * Updates both latitude and longitude information, using max/min points found + * within the specified bounds. + * + * @param q is the plane to intersect with. + * @param boundsInfo is the info to update with additional bounding information. + * @param bounds are the surfaces delineating what's inside the shape. + */ + public void recordBounds(final Plane q, final Bounds boundsInfo, final Membership... bounds) { + final GeoPoint[] intersectionPoints = findIntersections(q, bounds, NO_BOUNDS); + for (GeoPoint intersectionPoint : intersectionPoints) { + boundsInfo.addPoint(intersectionPoint); + } + } + + /** + * Accumulate bounds information for this plane, intersected with the unit sphere. + * Updates both latitude and longitude information, using max/min points found + * within the specified bounds. + * + * @param boundsInfo is the info to update with additional bounding information. + * @param bounds are the surfaces delineating what's inside the shape. + */ + public void recordBounds(final Bounds boundsInfo, final Membership... bounds) { + // For clarity, load local variables with good names + final double A = this.x; + final double B = this.y; + final double C = this.z; + + // Now compute latitude min/max points + if (!boundsInfo.checkNoTopLatitudeBound() || !boundsInfo.checkNoBottomLatitudeBound()) { + //System.err.println("Looking at latitude for plane "+this); + if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) { + //System.out.println("A = "+A+" B = "+B+" C = "+C+" D = "+D); + // sin (phi) = z + // cos (theta - phi) = D + // sin (theta) = C (the dot product of (0,0,1) and (A,B,C) ) + // Q: what is z? + // + // cos (theta-phi) = cos(theta)cos(phi) + sin(theta)sin(phi) = D + + if (Math.abs(C) < MINIMUM_RESOLUTION) { + // Special case: circle is vertical. + //System.err.println(" Degenerate case; it's vertical circle"); + // cos(phi) = D, and we want sin(phi) = z + // There are two solutions for phi given cos(phi) = D: a positive solution and a negative solution. + // So, when we compute z = sqrt(1-D^2), it's really z = +/- sqrt(1-D^2) . + + double z; + double x; + double y; + + final double denom = 1.0 / (A * A + B * B); + + z = Math.sqrt(1.0 - D * D); + y = -B * D * denom; + x = -A * D * denom; + addPoint(boundsInfo, bounds, x, y, z); + + z = -z; + addPoint(boundsInfo, bounds, x, y, z); + } else if (Math.abs(D) < MINIMUM_RESOLUTION) { + //System.err.println(" Plane through origin case"); + // The general case is degenerate when the plane goes through the origin. + // Luckily there's a pretty good way to figure out the max and min for that case though. + // We find the two z values by computing the angle of the plane's inclination with the normal. + // E.g., if this.z == 1, then our z value is 0, and if this.z == 0, our z value is 1. + // Also if this.z == -1, then z value is 0 again. + // Another way of putting this is that our z = sqrt(this.x^2 + this.y^2). + // + // The only tricky part is computing x and y. + double z; + double x; + double y; + + final double denom = 1.0 / (A * A + B * B); + + z = Math.sqrt((A * A + B * B) / (A * A + B * B + C * C)); + y = -B * (C * z) * denom; + x = -A * (C * z) * denom; + addPoint(boundsInfo, bounds, x, y, z); + + z = -z; + y = -B * (C * z) * denom; + x = -A * (C * z) * denom; + addPoint(boundsInfo, bounds, x, y, z); - double cosRA; - double sinRA; - double cosHA; - double sinHA; - - double magnitude = magnitude(); - if (magnitude >= MINIMUM_RESOLUTION) { - final double denom = 1.0/magnitude; - A *= denom; - B *= denom; - C *= denom; - - // cos ra = A/sqrt(A^2+B^2+C^2) - // sin ra = -B/sqrt(A^2+B^2+C^2) - // cos (-ra) = A/sqrt(A^2+B^2+C^2) - // sin (-ra) = B/sqrt(A^2+B^2+C^2) - final double xyMagnitude = Math.sqrt(A*A + B*B); - if (xyMagnitude >= MINIMUM_RESOLUTION) { - final double xyDenom = 1.0/xyMagnitude; - cosRA = A * xyDenom; - sinRA = -B * xyDenom; - } else { - cosRA = 1.0; - sinRA = 0.0; - } - - // sin(ha) = cos(asin(C/sqrt(A^2+B^2+C^2))) = sqrt(1 - C^2/(A^2+B^2+C^2)) = sqrt(A^2+B^2)/sqrt(A^2+B^2+C^2) - // cos(ha) = sin(asin(C/sqrt(A^2+B^2+C^2))) = C/sqrt(A^2+B^2+C^2) - sinHA = xyMagnitude; - cosHA = C; } else { - cosRA = 1.0; - sinRA = 0.0; - cosHA = 1.0; - sinHA = 0.0; - } - - // Forward-translate the start and end points - final Vector modifiedStart = modify(start, transX, transY, transZ, sinRA, cosRA, sinHA, cosHA); - final Vector modifiedEnd = modify(end, transX, transY, transZ, sinRA, cosRA, sinHA, cosHA); - if (Math.abs(modifiedStart.z) >= MINIMUM_RESOLUTION) - throw new IllegalArgumentException("Start point was not on plane: "+modifiedStart.z); - if (Math.abs(modifiedEnd.z) >= MINIMUM_RESOLUTION) - throw new IllegalArgumentException("End point was not on plane: "+modifiedEnd.z); - - // Compute the angular distance between start and end point - final double startAngle = Math.atan2(modifiedStart.y, modifiedStart.x); - final double endAngle = Math.atan2(modifiedEnd.y, modifiedEnd.x); - - final double startMagnitude = Math.sqrt(modifiedStart.x * modifiedStart.x + modifiedStart.y * modifiedStart.y); - double delta; - double beginAngle; - - double newEndAngle = endAngle; - while (newEndAngle < startAngle) { - newEndAngle += Math.PI * 2.0; - } - - if (newEndAngle - startAngle <= Math.PI) { - delta = newEndAngle - startAngle; - beginAngle = startAngle; - } else { - double newStartAngle = startAngle; - while (newStartAngle < endAngle) { - newStartAngle += Math.PI * 2.0; + //System.err.println(" General latitude case"); + // We might be able to identify a specific new latitude maximum or minimum. + // + // cos (theta-phi) = cos(theta)cos(phi) + sin(theta)sin(phi) = D + // + // This is tricky. If cos(phi) = something, and we want to figure out + // what sin(phi) is, in order to capture all solutions we need to recognize + // that sin(phi) = +/- sqrt(1 - cos(phi)^2). Basically, this means that + // whatever solution we find we have to mirror it across the x-y plane, + // and include both +z and -z solutions. + // + // cos (phi) = +/- sqrt(1-sin(phi)^2) = +/- sqrt(1-z^2) + // cos (theta) = +/- sqrt(1-sin(theta)^2) = +/- sqrt(1-C^2) + // + // D = cos(theta)cos(phi) + sin(theta)sin(phi) + // Substitute: + // D = sqrt(1-C^2) * sqrt(1-z^2) -/+ C * z + // Solve for z... + // D +/- Cz = sqrt(1-C^2)*sqrt(1-z^2) = sqrt(1 - z^2 - C^2 + z^2*C^2) + // Square both sides. + // (D +/- Cz)^2 = 1 - z^2 - C^2 + z^2*C^2 + // D^2 +/- 2DCz + C^2*z^2 = 1 - z^2 - C^2 + z^2*C^2 + // D^2 +/- 2DCz = 1 - C^2 - z^2 + // 0 = z^2 +/- 2DCz + (C^2 +D^2-1) = 0 + // + // z = (+/- 2DC +/- sqrt(4*D^2*C^2 - 4*(C^2+D^2-1))) / (2) + // z = +/- DC +/- sqrt(D^2*C^2 + 1 - C^2 - D^2 ) + // = +/- DC +/- sqrt(D^2*C^2 + 1 - C^2 - D^2) + // + // NOTE WELL: The above is clearly degenerate when D = 0. So we'll have to + // code a different solution for that case! + + // To get x and y, we need to plug z into the equations, as follows: + // + // Ax + By = -Cz - D + // x^2 + y^2 = 1 - z^2 + // + // x = (-Cz -D -By) /A + // y = (-Cz -D -Ax) /B + // + // [(-Cz -D -By) /A]^2 + y^2 = 1 - z^2 + // [-Cz -D -By]^2 + A^2*y^2 = A^2 - A^2*z^2 + // C^2*z^2 + D^2 + B^2*y^2 + 2CDz + 2CBzy + 2DBy + A^2*y^2 - A^2 + A^2*z^2 = 0 + // y^2 [A^2 + B^2] + y [2DB + 2CBz] + [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] = 0 + // + // + // Use quadratic formula, where: + // a = [A^2 + B^2] + // b = [2BD + 2CBz] + // c = [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] + // + // y = (-[2BD + 2CBz] +/- sqrt([2BD + 2CBz]^2 - 4 * [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2]) ) / (2 * [A^2 + B^2]) + // Take out a 2: + // y = (-[DB +CBz] +/- sqrt([DB + CBz]^2 - [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2]) ) / [A^2 + B^2] + // + // The sqrt term simplifies: + // + // B^2*D^2 + C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] = ? + // B^2*D^2 + C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 + // + B^2 * C^2 * z^2 + B^2 * D^2 + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? + // C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 + // + B^2 * C^2 * z^2 + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? + // 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 + // + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? + // - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 + // - A^2 * B^2 + B^2 * A^2 * z^2] =? + // - A^2 * [C^2 * z^2 + D^2 + 2 * CDz - A^2 + A^2*z^2 + // - B^2 + B^2 * z^2] =? + // - A^2 * [z^2[A^2 + B^2 + C^2] - [A^2 + B^2 - D^2] + 2CDz] =? + // - A^2 * [z^2 - [A^2 + B^2 - D^2] + 2CDz] =? + // + // y = (-[DB +CBz] +/- A*sqrt([A^2 + B^2 - D^2] - z^2 - 2CDz) ) / [A^2 + B^2] + // + // correspondingly: + // x = (-[DA +CAz] +/- B*sqrt([A^2 + B^2 - D^2] - z^2 - 2CDz) ) / [A^2 + B^2] + // + // However, for the maximum or minimum we seek, the clause inside the sqrt should be zero. If + // it is NOT zero, then we aren't looking at the right z value. + + double z; + double x; + double y; + + double sqrtValue = D * D * C * C + 1.0 - C * C - D * D; + double denom = 1.0 / (A * A + B * B); + if (Math.abs(sqrtValue) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" One latitude solution"); + double insideValue; + double sqrtTerm; + + z = D * C; + // Since we squared both sides of the equation, we may have introduced spurious solutions, so we have to check. + // But the same check applies to BOTH solutions -- the +z one as well as the -z one. + insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; + if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { + y = -B * (D + C * z) * denom; + x = -A * (D + C * z) * denom; + if (evaluateIsZero(x, y, z)) { + addPoint(boundsInfo, bounds, x, y, z); + } } - delta = newStartAngle - endAngle; - beginAngle = endAngle; - } - - final GeoPoint[] returnValues = new GeoPoint[proportions.length]; - for (int i = 0; i < returnValues.length; i++) { - final double newAngle = startAngle + proportions[i] * delta; - final double sinNewAngle = Math.sin(newAngle); - final double cosNewAngle = Math.cos(newAngle); - final Vector newVector = new Vector(cosNewAngle * startMagnitude, sinNewAngle * startMagnitude, 0.0); - returnValues[i] = reverseModify(newVector, transX, transY, transZ, sinRA, cosRA, sinHA, cosHA); - } - - return returnValues; - } - - /** Modify a point to produce a vector in translated/rotated space. - */ - protected static Vector modify(final GeoPoint start, final double transX, final double transY, final double transZ, - final double sinRA, final double cosRA, final double sinHA, final double cosHA) { - return start.translate(transX, transY, transZ).rotateXY(sinRA, cosRA).rotateXZ(sinHA, cosHA); - } - - /** Reverse modify a point to produce a GeoPoint in normal space. - */ - protected static GeoPoint reverseModify(final Vector point, final double transX, final double transY, final double transZ, - final double sinRA, final double cosRA, final double sinHA, final double cosHA) { - final Vector result = point.rotateXZ(-sinHA, cosHA).rotateXY(-sinRA, cosRA).translate(-transX, -transY, -transZ); - return new GeoPoint(result.x, result.y, result.z); - } - - /** Find the intersection points between two planes, given a set of bounds. - *@param q is the plane to intersect with. - *@param bounds is the set of bounds. - *@param moreBounds is another set of bounds. - *@return the intersection point(s) on the unit sphere, if there are any. - */ - protected GeoPoint[] findIntersections(final Plane q, final Membership[] bounds, final Membership[] moreBounds) { - final Vector lineVector = new Vector(this,q); - if (Math.abs(lineVector.x) < MINIMUM_RESOLUTION && Math.abs(lineVector.y) < MINIMUM_RESOLUTION && Math.abs(lineVector.z) < MINIMUM_RESOLUTION) { - // Degenerate case: parallel planes - //System.err.println(" planes are parallel - no intersection"); - return NO_POINTS; - } - - // The line will have the equation: A t + A0 = x, B t + B0 = y, C t + C0 = z. - // We have A, B, and C. In order to come up with A0, B0, and C0, we need to find a point that is on both planes. - // To do this, we find the largest vector value (either x, y, or z), and look for a point that solves both plane equations - // simultaneous. For example, let's say that the vector is (0.5,0.5,1), and the two plane equations are: - // 0.7 x + 0.3 y + 0.1 z + 0.0 = 0 - // and - // 0.9 x - 0.1 y + 0.2 z + 4.0 = 0 - // Then we'd pick z = 0, so the equations to solve for x and y would be: - // 0.7 x + 0.3y = 0.0 - // 0.9 x - 0.1y = -4.0 - // ... which can readily be solved using standard linear algebra. Generally: - // Q0 x + R0 y = S0 - // Q1 x + R1 y = S1 - // ... can be solved by Cramer's rule: - // x = det(S0 R0 / S1 R1) / det(Q0 R0 / Q1 R1) - // y = det(Q0 S0 / Q1 S1) / det(Q0 R0 / Q1 R1) - // ... where det( a b / c d ) = ad - bc, so: - // x = (S0 * R1 - R0 * S1) / (Q0 * R1 - R0 * Q1) - // y = (Q0 * S1 - S0 * Q1) / (Q0 * R1 - R0 * Q1) - double x0; - double y0; - double z0; - // We try to maximize the determinant in the denominator - final double denomYZ = this.y*q.z - this.z*q.y; - final double denomXZ = this.x*q.z - this.z*q.x; - final double denomXY = this.x*q.y - this.y*q.x; - if (Math.abs(denomYZ) >= Math.abs(denomXZ) && Math.abs(denomYZ) >= Math.abs(denomXY)) { - // X is the biggest, so our point will have x0 = 0.0 - if (Math.abs(denomYZ) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" Denominator is zero: no intersection"); - return NO_POINTS; + // Check the solution on the other side of the x-y plane + z = -z; + insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; + if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { + y = -B * (D + C * z) * denom; + x = -A * (D + C * z) * denom; + if (evaluateIsZero(x, y, z)) { + addPoint(boundsInfo, bounds, x, y, z); + } } - final double denom = 1.0 / denomYZ; - x0 = 0.0; - y0 = (-this.D * q.z - this.z * -q.D) * denom; - z0 = (this.y * -q.D + this.D * q.y) * denom; - } else if (Math.abs(denomXZ) >= Math.abs(denomXY) && Math.abs(denomXZ) >= Math.abs(denomYZ)) { - // Y is the biggest, so y0 = 0.0 - if (Math.abs(denomXZ) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" Denominator is zero: no intersection"); - return NO_POINTS; + } else if (sqrtValue > 0.0) { + //System.err.println(" Two latitude solutions"); + double sqrtResult = Math.sqrt(sqrtValue); + + double insideValue; + double sqrtTerm; + + z = D * C + sqrtResult; + //System.out.println("z= "+z+" D-C*z = " + (D-C*z) + " Math.sqrt(1.0 - z*z - C*C + z*z*C*C) = "+(Math.sqrt(1.0 - z*z - C*C + z*z*C*C))); + // Since we squared both sides of the equation, we may have introduced spurios solutions, so we have to check. + // But the same check applies to BOTH solutions -- the +z one as well as the -z one. + insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; + //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); + if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { + y = -B * (D + C * z) * denom; + x = -A * (D + C * z) * denom; + if (evaluateIsZero(x, y, z)) { + addPoint(boundsInfo, bounds, x, y, z); + } } - final double denom = 1.0 / denomXZ; - x0 = (-this.D * q.z - this.z * -q.D) * denom; - y0 = 0.0; - z0 = (this.x * -q.D + this.D * q.x) * denom; - } else { - // Z is the biggest, so Z0 = 0.0 - if (Math.abs(denomXY) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" Denominator is zero: no intersection"); - return NO_POINTS; + // Check the solution on the other side of the x-y plane + z = -z; + insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; + //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); + if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { + y = -B * (D + C * z) * denom; + x = -A * (D + C * z) * denom; + if (evaluateIsZero(x, y, z)) { + addPoint(boundsInfo, bounds, x, y, z); + } } - final double denom = 1.0 / denomXY; - x0 = (-this.D * q.y - this.y * -q.D) * denom; - y0 = (this.x * -q.D + this.D * q.x) * denom; - z0 = 0.0; - } - - // Once an intersecting line is determined, the next step is to intersect that line with the unit sphere, which - // will yield zero, one, or two points. - // The equation of the sphere is: 1.0 = x^2 + y^2 + z^2. Plugging in the parameterized line values yields: - // 1.0 = (At+A0)^2 + (Bt+B0)^2 + (Ct+C0)^2 - // A^2 t^2 + 2AA0t + A0^2 + B^2 t^2 + 2BB0t + B0^2 + C^2 t^2 + 2CC0t + C0^2 - 1,0 = 0.0 - // [A^2 + B^2 + C^2] t^2 + [2AA0 + 2BB0 + 2CC0] t + [A0^2 + B0^2 + C0^2 - 1,0] = 0.0 - // Use the quadratic formula to determine t values and candidate point(s) - final double A = lineVector.x * lineVector.x + lineVector.y * lineVector.y + lineVector.z * lineVector.z; - final double B = 2.0*(lineVector.x * x0 + lineVector.y * y0 + lineVector.z * z0); - final double C = x0*x0 + y0*y0 + z0*z0 - 1.0; - - final double BsquaredMinus = B * B - 4.0 * A * C; - if (Math.abs(BsquaredMinus) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" One point of intersection"); - final double inverse2A = 1.0 / (2.0 * A); - // One solution only - final double t = -B * inverse2A; - GeoPoint point = new GeoPoint(lineVector.x * t + x0, lineVector.y * t + y0, lineVector.z * t + z0); - if (point.isWithin(bounds,moreBounds)) - return new GeoPoint[]{point}; - return NO_POINTS; - } else if (BsquaredMinus > 0.0) { - //System.err.println(" Two points of intersection"); - final double inverse2A = 1.0 / (2.0 * A); - // Two solutions - final double sqrtTerm = Math.sqrt(BsquaredMinus); - final double t1 = (-B + sqrtTerm) * inverse2A; - final double t2 = (-B - sqrtTerm) * inverse2A; - GeoPoint point1 = new GeoPoint(lineVector.x * t1 + x0, lineVector.y * t1 + y0, lineVector.z * t1 + z0); - GeoPoint point2 = new GeoPoint(lineVector.x * t2 + x0, lineVector.y * t2 + y0, lineVector.z * t2 + z0); - //System.err.println(" "+point1+" and "+point2); - if (point1.isWithin(bounds,moreBounds)) { - if (point2.isWithin(bounds,moreBounds)) - return new GeoPoint[]{point1,point2}; - return new GeoPoint[]{point1}; + z = D * C - sqrtResult; + //System.out.println("z= "+z+" D-C*z = " + (D-C*z) + " Math.sqrt(1.0 - z*z - C*C + z*z*C*C) = "+(Math.sqrt(1.0 - z*z - C*C + z*z*C*C))); + // Since we squared both sides of the equation, we may have introduced spurios solutions, so we have to check. + // But the same check applies to BOTH solutions -- the +z one as well as the -z one. + insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; + //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); + if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { + y = -B * (D + C * z) * denom; + x = -A * (D + C * z) * denom; + if (evaluateIsZero(x, y, z)) { + addPoint(boundsInfo, bounds, x, y, z); + } } - if (point2.isWithin(bounds,moreBounds)) - return new GeoPoint[]{point2}; - return NO_POINTS; - } else { - //System.err.println(" no solutions - no intersection"); - return NO_POINTS; - } - } - - /** Accumulate bounds information for this plane, intersected with another plane - * and with the unit sphere. - * Updates both latitude and longitude information, using max/min points found - * within the specified bounds. - *@param q is the plane to intersect with. - *@param boundsInfo is the info to update with additional bounding information. - *@param bounds are the surfaces delineating what's inside the shape. - */ - public void recordBounds(final Plane q, final Bounds boundsInfo, final Membership... bounds) { - final GeoPoint[] intersectionPoints = findIntersections(q,bounds,NO_BOUNDS); - for (GeoPoint intersectionPoint : intersectionPoints) { - boundsInfo.addPoint(intersectionPoint); - } - } - - /** Accumulate bounds information for this plane, intersected with the unit sphere. - * Updates both latitude and longitude information, using max/min points found - * within the specified bounds. - *@param boundsInfo is the info to update with additional bounding information. - *@param bounds are the surfaces delineating what's inside the shape. - */ - public void recordBounds(final Bounds boundsInfo, final Membership... bounds) { - // For clarity, load local variables with good names - final double A = this.x; - final double B = this.y; - final double C = this.z; - - // Now compute latitude min/max points - if (!boundsInfo.checkNoTopLatitudeBound() || !boundsInfo.checkNoBottomLatitudeBound()) { - //System.err.println("Looking at latitude for plane "+this); - if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) { - //System.out.println("A = "+A+" B = "+B+" C = "+C+" D = "+D); - // sin (phi) = z - // cos (theta - phi) = D - // sin (theta) = C (the dot product of (0,0,1) and (A,B,C) ) - // Q: what is z? - // - // cos (theta-phi) = cos(theta)cos(phi) + sin(theta)sin(phi) = D - - if (Math.abs(C) < MINIMUM_RESOLUTION) { - // Special case: circle is vertical. - //System.err.println(" Degenerate case; it's vertical circle"); - // cos(phi) = D, and we want sin(phi) = z - // There are two solutions for phi given cos(phi) = D: a positive solution and a negative solution. - // So, when we compute z = sqrt(1-D^2), it's really z = +/- sqrt(1-D^2) . - - double z; - double x; - double y; - - final double denom = 1.0 / (A*A + B*B); - - z = Math.sqrt(1.0 - D*D); - y = -B * D * denom; - x = -A * D * denom; - addPoint(boundsInfo, bounds, x, y, z); - - z = -z; - addPoint(boundsInfo, bounds, x, y, z); - } else if (Math.abs(D) < MINIMUM_RESOLUTION) { - //System.err.println(" Plane through origin case"); - // The general case is degenerate when the plane goes through the origin. - // Luckily there's a pretty good way to figure out the max and min for that case though. - // We find the two z values by computing the angle of the plane's inclination with the normal. - // E.g., if this.z == 1, then our z value is 0, and if this.z == 0, our z value is 1. - // Also if this.z == -1, then z value is 0 again. - // Another way of putting this is that our z = sqrt(this.x^2 + this.y^2). - // - // The only tricky part is computing x and y. - double z; - double x; - double y; - - final double denom = 1.0 / (A*A + B*B); - - z = Math.sqrt((A * A + B * B)/(A*A+B*B+C*C)); - y = -B * (C*z) * denom; - x = -A * (C*z) * denom; - addPoint(boundsInfo, bounds, x, y, z); - - z = -z; - y = -B * (C*z) * denom; - x = -A * (C*z) * denom; - addPoint(boundsInfo, bounds, x, y, z); - - } else { - //System.err.println(" General latitude case"); - // We might be able to identify a specific new latitude maximum or minimum. - // - // cos (theta-phi) = cos(theta)cos(phi) + sin(theta)sin(phi) = D - // - // This is tricky. If cos(phi) = something, and we want to figure out - // what sin(phi) is, in order to capture all solutions we need to recognize - // that sin(phi) = +/- sqrt(1 - cos(phi)^2). Basically, this means that - // whatever solution we find we have to mirror it across the x-y plane, - // and include both +z and -z solutions. - // - // cos (phi) = +/- sqrt(1-sin(phi)^2) = +/- sqrt(1-z^2) - // cos (theta) = +/- sqrt(1-sin(theta)^2) = +/- sqrt(1-C^2) - // - // D = cos(theta)cos(phi) + sin(theta)sin(phi) - // Substitute: - // D = sqrt(1-C^2) * sqrt(1-z^2) -/+ C * z - // Solve for z... - // D +/- Cz = sqrt(1-C^2)*sqrt(1-z^2) = sqrt(1 - z^2 - C^2 + z^2*C^2) - // Square both sides. - // (D +/- Cz)^2 = 1 - z^2 - C^2 + z^2*C^2 - // D^2 +/- 2DCz + C^2*z^2 = 1 - z^2 - C^2 + z^2*C^2 - // D^2 +/- 2DCz = 1 - C^2 - z^2 - // 0 = z^2 +/- 2DCz + (C^2 +D^2-1) = 0 - // - // z = (+/- 2DC +/- sqrt(4*D^2*C^2 - 4*(C^2+D^2-1))) / (2) - // z = +/- DC +/- sqrt(D^2*C^2 + 1 - C^2 - D^2 ) - // = +/- DC +/- sqrt(D^2*C^2 + 1 - C^2 - D^2) - // - // NOTE WELL: The above is clearly degenerate when D = 0. So we'll have to - // code a different solution for that case! - - // To get x and y, we need to plug z into the equations, as follows: - // - // Ax + By = -Cz - D - // x^2 + y^2 = 1 - z^2 - // - // x = (-Cz -D -By) /A - // y = (-Cz -D -Ax) /B - // - // [(-Cz -D -By) /A]^2 + y^2 = 1 - z^2 - // [-Cz -D -By]^2 + A^2*y^2 = A^2 - A^2*z^2 - // C^2*z^2 + D^2 + B^2*y^2 + 2CDz + 2CBzy + 2DBy + A^2*y^2 - A^2 + A^2*z^2 = 0 - // y^2 [A^2 + B^2] + y [2DB + 2CBz] + [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] = 0 - // - // - // Use quadratic formula, where: - // a = [A^2 + B^2] - // b = [2BD + 2CBz] - // c = [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] - // - // y = (-[2BD + 2CBz] +/- sqrt([2BD + 2CBz]^2 - 4 * [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2]) ) / (2 * [A^2 + B^2]) - // Take out a 2: - // y = (-[DB +CBz] +/- sqrt([DB + CBz]^2 - [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2]) ) / [A^2 + B^2] - // - // The sqrt term simplifies: - // - // B^2*D^2 + C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 + B^2] * [C^2*z^2 + D^2 + 2CDz - A^2 + A^2*z^2] = ? - // B^2*D^2 + C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // + B^2 * C^2 * z^2 + B^2 * D^2 + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? - // C^2*B^2*z^2 + 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // + B^2 * C^2 * z^2 + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? - // 2C*D*B^2*z - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // + 2 * B^2 * CDz - A^2 * B^2 + B^2 * A^2 * z^2] =? - // - [A^2 * C^2 * z^2 + A^2 * D^2 + 2 * A^2 * CDz - A^4 + A^4*z^2 - // - A^2 * B^2 + B^2 * A^2 * z^2] =? - // - A^2 * [C^2 * z^2 + D^2 + 2 * CDz - A^2 + A^2*z^2 - // - B^2 + B^2 * z^2] =? - // - A^2 * [z^2[A^2 + B^2 + C^2] - [A^2 + B^2 - D^2] + 2CDz] =? - // - A^2 * [z^2 - [A^2 + B^2 - D^2] + 2CDz] =? - // - // y = (-[DB +CBz] +/- A*sqrt([A^2 + B^2 - D^2] - z^2 - 2CDz) ) / [A^2 + B^2] - // - // correspondingly: - // x = (-[DA +CAz] +/- B*sqrt([A^2 + B^2 - D^2] - z^2 - 2CDz) ) / [A^2 + B^2] - // - // However, for the maximum or minimum we seek, the clause inside the sqrt should be zero. If - // it is NOT zero, then we aren't looking at the right z value. - - double z; - double x; - double y; - - double sqrtValue = D*D*C*C + 1.0 - C*C - D*D; - double denom = 1.0 / (A*A + B*B); - if (Math.abs(sqrtValue) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" One latitude solution"); - double insideValue; - double sqrtTerm; - - z = D*C; - // Since we squared both sides of the equation, we may have introduced spurious solutions, so we have to check. - // But the same check applies to BOTH solutions -- the +z one as well as the -z one. - insideValue = A * A + B * B - D * D - z*z - 2.0 * C * D * z; - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C*z) * denom; - x = -A * (D + C*z) * denom; - if (evaluateIsZero(x,y,z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - // Check the solution on the other side of the x-y plane - z = -z; - insideValue = A * A + B * B - D * D - z*z - 2.0 * C * D * z; - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C*z) * denom; - x = -A * (D + C*z) * denom; - if (evaluateIsZero(x,y,z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - } else if (sqrtValue > 0.0) { - //System.err.println(" Two latitude solutions"); - double sqrtResult = Math.sqrt(sqrtValue); - - double insideValue; - double sqrtTerm; - - z = D*C + sqrtResult; - //System.out.println("z= "+z+" D-C*z = " + (D-C*z) + " Math.sqrt(1.0 - z*z - C*C + z*z*C*C) = "+(Math.sqrt(1.0 - z*z - C*C + z*z*C*C))); - // Since we squared both sides of the equation, we may have introduced spurios solutions, so we have to check. - // But the same check applies to BOTH solutions -- the +z one as well as the -z one. - insideValue = A * A + B * B - D * D - z*z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C*z) * denom; - x = -A * (D + C*z) * denom; - if (evaluateIsZero(x,y,z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - // Check the solution on the other side of the x-y plane - z = -z; - insideValue = A * A + B * B - D * D - z*z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C*z) * denom; - x = -A * (D + C*z) * denom; - if (evaluateIsZero(x,y,z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - z = D*C - sqrtResult; - //System.out.println("z= "+z+" D-C*z = " + (D-C*z) + " Math.sqrt(1.0 - z*z - C*C + z*z*C*C) = "+(Math.sqrt(1.0 - z*z - C*C + z*z*C*C))); - // Since we squared both sides of the equation, we may have introduced spurios solutions, so we have to check. - // But the same check applies to BOTH solutions -- the +z one as well as the -z one. - insideValue = A * A + B * B - D * D - z*z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C*z) * denom; - x = -A * (D + C*z) * denom; - if (evaluateIsZero(x,y,z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - // Check the solution on the other side of the x-y plane - z = -z; - insideValue = A * A + B * B - D * D - z*z - 2.0 * C * D * z; - //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); - if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { - y = -B * (D + C*z) * denom; - x = -A * (D + C*z) * denom; - if (evaluateIsZero(x,y,z)) { - addPoint(boundsInfo, bounds, x, y, z); - } - } - } - } - } else { - // Horizontal circle. - // Since the recordBounds() method will be called ONLY for planes that constitute edges of a shape, - // we can be sure that some part of the horizontal circle will be part of the boundary, so we don't need - // to check Membership objects. - boundsInfo.addHorizontalCircle(-D * C); + // Check the solution on the other side of the x-y plane + z = -z; + insideValue = A * A + B * B - D * D - z * z - 2.0 * C * D * z; + //System.err.println(" z="+z+" C="+C+" D="+D+" inside value "+insideValue); + if (Math.abs(insideValue) < MINIMUM_RESOLUTION) { + y = -B * (D + C * z) * denom; + x = -A * (D + C * z) * denom; + if (evaluateIsZero(x, y, z)) { + addPoint(boundsInfo, bounds, x, y, z); + } } - //System.err.println("Done latitude bounds"); + } } - - // First, figure out our longitude bounds, unless we no longer need to consider that - if (!boundsInfo.checkNoLongitudeBound()) { - //System.err.println("Computing longitude bounds for "+this); - //System.out.println("A = "+A+" B = "+B+" C = "+C+" D = "+D); - // Compute longitude bounds - - double a; - double b; - double c; - - if (Math.abs(C) < MINIMUM_RESOLUTION) { - // Degenerate; the equation describes a line - //System.out.println("It's a zero-width ellipse"); - // Ax + By + D = 0 - if (Math.abs(D) >= MINIMUM_RESOLUTION) { - if (Math.abs(A) > Math.abs(B)) { - // Use equation suitable for A != 0 - // We need to find the endpoints of the zero-width ellipse. - // Geometrically, we have a line segment in x-y space. We need to locate the endpoints - // of that line. But luckily, we know some things: specifically, since it is a - // degenerate situation in projection, the C value had to have been 0. That - // means that our line's endpoints will coincide with the unit circle. All we - // need to do then is to find the intersection of the unit circle and the line - // equation: - // - // A x + B y + D = 0 - // - // Since A != 0: - // x = (-By - D)/A - // - // The unit circle: - // x^2 + y^2 - 1 = 0 - // Substitute: - // [(-By-D)/A]^2 + y^2 -1 = 0 - // Multiply through by A^2: - // [-By - D]^2 + A^2*y^2 - A^2 = 0 - // Multiply out: - // B^2*y^2 + 2BDy + D^2 + A^2*y^2 - A^2 = 0 - // Group: - // y^2 * [B^2 + A^2] + y [2BD] + [D^2-A^2] = 0 - - a = B * B + A * A; - b = 2.0 * B * D; - c = D * D - A * A; - - double sqrtClause = b * b - 4.0 * a * c; - - if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { - double y0 = -b / (2.0 * a); - double x0 = (-D - B * y0) / A; - double z0 = 0.0; - addPoint(boundsInfo, bounds, x0, y0, z0); - } else if (sqrtClause > 0.0) { - double sqrtResult = Math.sqrt(sqrtClause); - double denom = 1.0 / (2.0 * a); - double Hdenom = 1.0 / A; - - double y0a = (-b + sqrtResult ) * denom; - double y0b = (-b - sqrtResult ) * denom; + } else { + // Horizontal circle. + // Since the recordBounds() method will be called ONLY for planes that constitute edges of a shape, + // we can be sure that some part of the horizontal circle will be part of the boundary, so we don't need + // to check Membership objects. + boundsInfo.addHorizontalCircle(-D * C); + } + //System.err.println("Done latitude bounds"); + } - double x0a = (-D - B * y0a) * Hdenom; - double x0b = (-D - B * y0b) * Hdenom; - - double z0a = 0.0; - double z0b = 0.0; + // First, figure out our longitude bounds, unless we no longer need to consider that + if (!boundsInfo.checkNoLongitudeBound()) { + //System.err.println("Computing longitude bounds for "+this); + //System.out.println("A = "+A+" B = "+B+" C = "+C+" D = "+D); + // Compute longitude bounds - addPoint(boundsInfo, bounds, x0a, y0a, z0a); - addPoint(boundsInfo, bounds, x0b, y0b, z0b); - } + double a; + double b; + double c; - } else { - // Use equation suitable for B != 0 - // Since I != 0, we rewrite: - // y = (-Ax - D)/B - a = B * B + A * A; - b = 2.0 * A * D; - c = D * D - B * B; - - double sqrtClause = b * b - 4.0 * a * c; + if (Math.abs(C) < MINIMUM_RESOLUTION) { + // Degenerate; the equation describes a line + //System.out.println("It's a zero-width ellipse"); + // Ax + By + D = 0 + if (Math.abs(D) >= MINIMUM_RESOLUTION) { + if (Math.abs(A) > Math.abs(B)) { + // Use equation suitable for A != 0 + // We need to find the endpoints of the zero-width ellipse. + // Geometrically, we have a line segment in x-y space. We need to locate the endpoints + // of that line. But luckily, we know some things: specifically, since it is a + // degenerate situation in projection, the C value had to have been 0. That + // means that our line's endpoints will coincide with the unit circle. All we + // need to do then is to find the intersection of the unit circle and the line + // equation: + // + // A x + B y + D = 0 + // + // Since A != 0: + // x = (-By - D)/A + // + // The unit circle: + // x^2 + y^2 - 1 = 0 + // Substitute: + // [(-By-D)/A]^2 + y^2 -1 = 0 + // Multiply through by A^2: + // [-By - D]^2 + A^2*y^2 - A^2 = 0 + // Multiply out: + // B^2*y^2 + 2BDy + D^2 + A^2*y^2 - A^2 = 0 + // Group: + // y^2 * [B^2 + A^2] + y [2BD] + [D^2-A^2] = 0 - if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { - double x0 = -b / (2.0 * a); - double y0 = (- D - A * x0) / B; - double z0 = 0.0; - addPoint(boundsInfo, bounds, x0, y0, z0); - } else if (sqrtClause > 0.0) { - double sqrtResult = Math.sqrt(sqrtClause); - double denom = 1.0 / (2.0 * a); - double Idenom = 1.0 / B; - - double x0a = (-b + sqrtResult ) * denom; - double x0b = (-b - sqrtResult ) * denom; - double y0a = (- D - A * x0a) * Idenom; - double y0b = (- D - A * x0b) * Idenom; - double z0a = 0.0; - double z0b = 0.0; + a = B * B + A * A; + b = 2.0 * B * D; + c = D * D - A * A; - addPoint(boundsInfo, bounds, x0a, y0a, z0a); - addPoint(boundsInfo, bounds, x0b, y0b, z0b); - } - } - } - - } else { - //System.err.println("General longitude bounds..."); - - // NOTE WELL: The x,y,z values generated here are NOT on the unit sphere. - // They are for lat/lon calculation purposes only. x-y is meant to be used for longitude determination, - // and z for latitude, and that's all the values are good for. - - // (1) Intersect the plane and the unit sphere, and project the results into the x-y plane: - // From plane: - // z = (-Ax - By - D) / C - // From unit sphere: - // x^2 + y^2 + [(-Ax - By - D) / C]^2 = 1 - // Simplify/expand: - // C^2*x^2 + C^2*y^2 + (-Ax - By - D)^2 = C^2 - // - // x^2 * C^2 + y^2 * C^2 + x^2 * (A^2 + ABxy + ADx) + (ABxy + y^2 * B^2 + BDy) + (ADx + BDy + D^2) = C^2 - // Group: - // [A^2 + C^2] x^2 + [B^2 + C^2] y^2 + [2AB]xy + [2AD]x + [2BD]y + [D^2-C^2] = 0 - // For convenience, introduce post-projection coefficient variables to make life easier. - // E x^2 + F y^2 + G xy + H x + I y + J = 0 - double E = A * A + C * C; - double F = B * B + C * C; - double G = 2.0 * A * B; - double H = 2.0 * A * D; - double I = 2.0 * B * D; - double J = D * D - C * C; + double sqrtClause = b * b - 4.0 * a * c; - //System.err.println("E = " + E + " F = " + F + " G = " + G + " H = "+ H + " I = " + I + " J = " + J); - - double trialX = 2.0; - double trialY = 2.0; - - //System.err.println("Trial point evaluates to: "+(E*trialX*trialX + F*trialY*trialY + G*trialX*trialY + H*trialX + I*trialY + J)); - - // Check if the origin is within, by substituting x = 0, y = 0 and seeing if less than zero - if (Math.abs(J) >= MINIMUM_RESOLUTION && J > 0.0) { - // The derivative of the curve above is: - // 2Exdx + 2Fydy + G(xdy+ydx) + Hdx + Idy = 0 - // (2Ex + Gy + H)dx + (2Fy + Gx + I)dy = 0 - // dy/dx = - (2Ex + Gy + H) / (2Fy + Gx + I) - // - // The equation of a line going through the origin with the slope dy/dx is: - // y = dy/dx x - // y = - (2Ex + Gy + H) / (2Fy + Gx + I) x - // Rearrange: - // (2Fy + Gx + I) y + (2Ex + Gy + H) x = 0 - // 2Fy^2 + Gxy + Iy + 2Ex^2 + Gxy + Hx = 0 - // 2Ex^2 + 2Fy^2 + 2Gxy + Hx + Iy = 0 - // - // Multiply the original equation by 2: - // 2E x^2 + 2F y^2 + 2G xy + 2H x + 2I y + 2J = 0 - // Subtract one from the other, to remove the high-order terms: - // Hx + Iy + 2J = 0 - // Now, we can substitute either x = or y = into the derivative equation, or into the original equation. - // But we will need to base this on which coefficient is non-zero - - if (Math.abs(H) > Math.abs(I)) { - //System.err.println(" Using the y quadratic"); - // x = (-2J - Iy)/H - - // Plug into the original equation: - // E [(-2J - Iy)/H]^2 + F y^2 + G [(-2J - Iy)/H]y + H [(-2J - Iy)/H] + I y + J = 0 - // E [(-2J - Iy)/H]^2 + F y^2 + G [(-2J - Iy)/H]y - J = 0 - // Same equation as derivative equation, except for a factor of 2! So it doesn't matter which we pick. - - // Plug into derivative equation: - // 2E[(-2J - Iy)/H]^2 + 2Fy^2 + 2G[(-2J - Iy)/H]y + H[(-2J - Iy)/H] + Iy = 0 - // 2E[(-2J - Iy)/H]^2 + 2Fy^2 + 2G[(-2J - Iy)/H]y - 2J = 0 - // E[(-2J - Iy)/H]^2 + Fy^2 + G[(-2J - Iy)/H]y - J = 0 + if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { + double y0 = -b / (2.0 * a); + double x0 = (-D - B * y0) / A; + double z0 = 0.0; + addPoint(boundsInfo, bounds, x0, y0, z0); + } else if (sqrtClause > 0.0) { + double sqrtResult = Math.sqrt(sqrtClause); + double denom = 1.0 / (2.0 * a); + double Hdenom = 1.0 / A; - // Multiply by H^2 to make manipulation easier - // E[(-2J - Iy)]^2 + F*H^2*y^2 + GH[(-2J - Iy)]y - J*H^2 = 0 - // Do the square - // E[4J^2 + 4IJy + I^2*y^2] + F*H^2*y^2 + GH(-2Jy - I*y^2) - J*H^2 = 0 + double y0a = (-b + sqrtResult) * denom; + double y0b = (-b - sqrtResult) * denom; - // Multiply it out - // 4E*J^2 + 4EIJy + E*I^2*y^2 + H^2*Fy^2 - 2GHJy - GH*I*y^2 - J*H^2 = 0 - // Group: - // y^2 [E*I^2 - GH*I + F*H^2] + y [4EIJ - 2GHJ] + [4E*J^2 - J*H^2] = 0 + double x0a = (-D - B * y0a) * Hdenom; + double x0b = (-D - B * y0b) * Hdenom; - a = E * I * I - G * H * I + F*H*H; - b = 4.0 * E * I * J - 2.0 * G * H * J; - c = 4.0 * E * J * J - J * H * H; - - //System.out.println("a="+a+" b="+b+" c="+c); - double sqrtClause = b * b - 4.0 * a * c; - //System.out.println("sqrtClause="+sqrtClause); - - if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" One solution"); - double y0 = -b / (2.0 * a); - double x0 = (-2.0 * J - I * y0) / H; - double z0 = (-A*x0 - B*y0 - D)/C; + double z0a = 0.0; + double z0b = 0.0; - addPoint(boundsInfo, bounds, x0, y0, z0); - } else if (sqrtClause > 0.0) { - //System.err.println(" Two solutions"); - double sqrtResult = Math.sqrt(sqrtClause); - double denom = 1.0 / (2.0 * a); - double Hdenom = 1.0 / H; - double Cdenom = 1.0 / C; - - double y0a = (-b + sqrtResult ) * denom; - double y0b = (-b - sqrtResult ) * denom; - double x0a = (-2.0 * J - I * y0a) * Hdenom; - double x0b = (-2.0 * J - I * y0b) * Hdenom; - double z0a = (-A*x0a - B*y0a - D) * Cdenom; - double z0b = (-A*x0b - B*y0b - D) * Cdenom; - - addPoint(boundsInfo, bounds, x0a, y0a, z0a); - addPoint(boundsInfo, bounds, x0b, y0b, z0b); - } - - } else { - //System.err.println(" Using the x quadratic"); - // y = (-2J - Hx)/I - - // Plug into the original equation: - // E x^2 + F [(-2J - Hx)/I]^2 + G x[(-2J - Hx)/I] - J = 0 - - // Multiply by I^2 to make manipulation easier - // E * I^2 * x^2 + F [(-2J - Hx)]^2 + GIx[(-2J - Hx)] - J * I^2 = 0 - // Do the square - // E * I^2 * x^2 + F [ 4J^2 + 4JHx + H^2*x^2] + GI[(-2Jx - H*x^2)] - J * I^2 = 0 - - // Multiply it out - // E * I^2 * x^2 + 4FJ^2 + 4FJHx + F*H^2*x^2 - 2GIJx - HGI*x^2 - J * I^2 = 0 - // Group: - // x^2 [E*I^2 - GHI + F*H^2] + x [4FJH - 2GIJ] + [4FJ^2 - J*I^2] = 0 - - // E x^2 + F y^2 + G xy + H x + I y + J = 0 - - a = E * I * I - G * H * I + F*H*H; - b = 4.0 * F * H * J - 2.0 * G * I * J; - c = 4.0 * F * J * J - J * I * I; - - //System.out.println("a="+a+" b="+b+" c="+c); - double sqrtClause = b * b - 4.0 * a * c; - //System.out.println("sqrtClause="+sqrtClause); - if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { - //System.err.println(" One solution; sqrt clause was "+sqrtClause); - double x0 = -b / (2.0 * a); - double y0 = (-2.0 * J - H * x0) / I; - double z0 = (-A*x0 - B*y0 - D)/C; - // Verify that x&y fulfill the equation - // 2Ex^2 + 2Fy^2 + 2Gxy + Hx + Iy = 0 - addPoint(boundsInfo, bounds, x0, y0, z0); - } else if (sqrtClause > 0.0) { - //System.err.println(" Two solutions"); - double sqrtResult = Math.sqrt(sqrtClause); - double denom = 1.0 / (2.0 * a); - double Idenom = 1.0 / I; - double Cdenom = 1.0 / C; - - double x0a = (-b + sqrtResult ) * denom; - double x0b = (-b - sqrtResult ) * denom; - double y0a = (-2.0 * J - H * x0a) * Idenom; - double y0b = (-2.0 * J - H * x0b) * Idenom; - double z0a = (-A*x0a - B*y0a - D) * Cdenom; - double z0b = (-A*x0b - B*y0b - D) * Cdenom; - - addPoint(boundsInfo, bounds, x0a, y0a, z0a); - addPoint(boundsInfo, bounds, x0b, y0b, z0b); - } - } - } + addPoint(boundsInfo, bounds, x0a, y0a, z0a); + addPoint(boundsInfo, bounds, x0b, y0b, z0b); } - } - } - - protected static void addPoint(final Bounds boundsInfo, final Membership[] bounds, final double x, final double y, final double z) { - //System.err.println(" Want to add point x="+x+" y="+y+" z="+z); - // Make sure the discovered point is within the bounds - for (Membership bound : bounds) { - if (!bound.isWithin(x,y,z)) - return; - } - // Add the point - //System.err.println(" point added"); - //System.out.println("Adding point x="+x+" y="+y+" z="+z); - boundsInfo.addPoint(x,y,z); - } - - /** Determine whether the plane intersects another plane within the - * bounds provided. - *@param q is the other plane. - *@param notablePoints are points to look at to disambiguate cases when the two planes are identical. - *@param moreNotablePoints are additional points to look at to disambiguate cases when the two planes are identical. - *@param bounds is one part of the bounds. - *@param moreBounds are more bounds. - *@return true if there's an intersection. - */ - public boolean intersects(final Plane q, final GeoPoint[] notablePoints, final GeoPoint[] moreNotablePoints, final Membership[] bounds, final Membership... moreBounds) { - //System.err.println("Does plane "+this+" intersect with plane "+q); - // If the two planes are identical, then the math will find no points of intersection. - // So a special case of this is to check for plane equality. But that is not enough, because - // what we really need at that point is to determine whether overlap occurs between the two parts of the intersection - // of plane and circle. That is, are there *any* points on the plane that are within the bounds described? - if (isNumericallyIdentical(q)) { - //System.err.println(" Identical plane"); - // The only way to efficiently figure this out will be to have a list of trial points available to evaluate. - // We look for any point that fulfills all the bounds. - for (GeoPoint p : notablePoints) { - if (meetsAllBounds(p,bounds,moreBounds)) { - //System.err.println(" found a notable point in bounds, so intersects"); - return true; - } + } else { + // Use equation suitable for B != 0 + // Since I != 0, we rewrite: + // y = (-Ax - D)/B + a = B * B + A * A; + b = 2.0 * A * D; + c = D * D - B * B; + + double sqrtClause = b * b - 4.0 * a * c; + + if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { + double x0 = -b / (2.0 * a); + double y0 = (-D - A * x0) / B; + double z0 = 0.0; + addPoint(boundsInfo, bounds, x0, y0, z0); + } else if (sqrtClause > 0.0) { + double sqrtResult = Math.sqrt(sqrtClause); + double denom = 1.0 / (2.0 * a); + double Idenom = 1.0 / B; + + double x0a = (-b + sqrtResult) * denom; + double x0b = (-b - sqrtResult) * denom; + double y0a = (-D - A * x0a) * Idenom; + double y0b = (-D - A * x0b) * Idenom; + double z0a = 0.0; + double z0b = 0.0; + + addPoint(boundsInfo, bounds, x0a, y0a, z0a); + addPoint(boundsInfo, bounds, x0b, y0b, z0b); } - for (GeoPoint p : moreNotablePoints) { - if (meetsAllBounds(p,bounds,moreBounds)) { - //System.err.println(" found a notable point in bounds, so intersects"); - return true; - } + } + } + + } else { + //System.err.println("General longitude bounds..."); + + // NOTE WELL: The x,y,z values generated here are NOT on the unit sphere. + // They are for lat/lon calculation purposes only. x-y is meant to be used for longitude determination, + // and z for latitude, and that's all the values are good for. + + // (1) Intersect the plane and the unit sphere, and project the results into the x-y plane: + // From plane: + // z = (-Ax - By - D) / C + // From unit sphere: + // x^2 + y^2 + [(-Ax - By - D) / C]^2 = 1 + // Simplify/expand: + // C^2*x^2 + C^2*y^2 + (-Ax - By - D)^2 = C^2 + // + // x^2 * C^2 + y^2 * C^2 + x^2 * (A^2 + ABxy + ADx) + (ABxy + y^2 * B^2 + BDy) + (ADx + BDy + D^2) = C^2 + // Group: + // [A^2 + C^2] x^2 + [B^2 + C^2] y^2 + [2AB]xy + [2AD]x + [2BD]y + [D^2-C^2] = 0 + // For convenience, introduce post-projection coefficient variables to make life easier. + // E x^2 + F y^2 + G xy + H x + I y + J = 0 + double E = A * A + C * C; + double F = B * B + C * C; + double G = 2.0 * A * B; + double H = 2.0 * A * D; + double I = 2.0 * B * D; + double J = D * D - C * C; + + //System.err.println("E = " + E + " F = " + F + " G = " + G + " H = "+ H + " I = " + I + " J = " + J); + + double trialX = 2.0; + double trialY = 2.0; + + //System.err.println("Trial point evaluates to: "+(E*trialX*trialX + F*trialY*trialY + G*trialX*trialY + H*trialX + I*trialY + J)); + + // Check if the origin is within, by substituting x = 0, y = 0 and seeing if less than zero + if (Math.abs(J) >= MINIMUM_RESOLUTION && J > 0.0) { + // The derivative of the curve above is: + // 2Exdx + 2Fydy + G(xdy+ydx) + Hdx + Idy = 0 + // (2Ex + Gy + H)dx + (2Fy + Gx + I)dy = 0 + // dy/dx = - (2Ex + Gy + H) / (2Fy + Gx + I) + // + // The equation of a line going through the origin with the slope dy/dx is: + // y = dy/dx x + // y = - (2Ex + Gy + H) / (2Fy + Gx + I) x + // Rearrange: + // (2Fy + Gx + I) y + (2Ex + Gy + H) x = 0 + // 2Fy^2 + Gxy + Iy + 2Ex^2 + Gxy + Hx = 0 + // 2Ex^2 + 2Fy^2 + 2Gxy + Hx + Iy = 0 + // + // Multiply the original equation by 2: + // 2E x^2 + 2F y^2 + 2G xy + 2H x + 2I y + 2J = 0 + // Subtract one from the other, to remove the high-order terms: + // Hx + Iy + 2J = 0 + // Now, we can substitute either x = or y = into the derivative equation, or into the original equation. + // But we will need to base this on which coefficient is non-zero + + if (Math.abs(H) > Math.abs(I)) { + //System.err.println(" Using the y quadratic"); + // x = (-2J - Iy)/H + + // Plug into the original equation: + // E [(-2J - Iy)/H]^2 + F y^2 + G [(-2J - Iy)/H]y + H [(-2J - Iy)/H] + I y + J = 0 + // E [(-2J - Iy)/H]^2 + F y^2 + G [(-2J - Iy)/H]y - J = 0 + // Same equation as derivative equation, except for a factor of 2! So it doesn't matter which we pick. + + // Plug into derivative equation: + // 2E[(-2J - Iy)/H]^2 + 2Fy^2 + 2G[(-2J - Iy)/H]y + H[(-2J - Iy)/H] + Iy = 0 + // 2E[(-2J - Iy)/H]^2 + 2Fy^2 + 2G[(-2J - Iy)/H]y - 2J = 0 + // E[(-2J - Iy)/H]^2 + Fy^2 + G[(-2J - Iy)/H]y - J = 0 + + // Multiply by H^2 to make manipulation easier + // E[(-2J - Iy)]^2 + F*H^2*y^2 + GH[(-2J - Iy)]y - J*H^2 = 0 + // Do the square + // E[4J^2 + 4IJy + I^2*y^2] + F*H^2*y^2 + GH(-2Jy - I*y^2) - J*H^2 = 0 + + // Multiply it out + // 4E*J^2 + 4EIJy + E*I^2*y^2 + H^2*Fy^2 - 2GHJy - GH*I*y^2 - J*H^2 = 0 + // Group: + // y^2 [E*I^2 - GH*I + F*H^2] + y [4EIJ - 2GHJ] + [4E*J^2 - J*H^2] = 0 + + a = E * I * I - G * H * I + F * H * H; + b = 4.0 * E * I * J - 2.0 * G * H * J; + c = 4.0 * E * J * J - J * H * H; + + //System.out.println("a="+a+" b="+b+" c="+c); + double sqrtClause = b * b - 4.0 * a * c; + //System.out.println("sqrtClause="+sqrtClause); + + if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" One solution"); + double y0 = -b / (2.0 * a); + double x0 = (-2.0 * J - I * y0) / H; + double z0 = (-A * x0 - B * y0 - D) / C; + + addPoint(boundsInfo, bounds, x0, y0, z0); + } else if (sqrtClause > 0.0) { + //System.err.println(" Two solutions"); + double sqrtResult = Math.sqrt(sqrtClause); + double denom = 1.0 / (2.0 * a); + double Hdenom = 1.0 / H; + double Cdenom = 1.0 / C; + + double y0a = (-b + sqrtResult) * denom; + double y0b = (-b - sqrtResult) * denom; + double x0a = (-2.0 * J - I * y0a) * Hdenom; + double x0b = (-2.0 * J - I * y0b) * Hdenom; + double z0a = (-A * x0a - B * y0a - D) * Cdenom; + double z0b = (-A * x0b - B * y0b - D) * Cdenom; + + addPoint(boundsInfo, bounds, x0a, y0a, z0a); + addPoint(boundsInfo, bounds, x0b, y0b, z0b); } - //System.err.println(" no notable points inside found; no intersection"); - return false; + + } else { + //System.err.println(" Using the x quadratic"); + // y = (-2J - Hx)/I + + // Plug into the original equation: + // E x^2 + F [(-2J - Hx)/I]^2 + G x[(-2J - Hx)/I] - J = 0 + + // Multiply by I^2 to make manipulation easier + // E * I^2 * x^2 + F [(-2J - Hx)]^2 + GIx[(-2J - Hx)] - J * I^2 = 0 + // Do the square + // E * I^2 * x^2 + F [ 4J^2 + 4JHx + H^2*x^2] + GI[(-2Jx - H*x^2)] - J * I^2 = 0 + + // Multiply it out + // E * I^2 * x^2 + 4FJ^2 + 4FJHx + F*H^2*x^2 - 2GIJx - HGI*x^2 - J * I^2 = 0 + // Group: + // x^2 [E*I^2 - GHI + F*H^2] + x [4FJH - 2GIJ] + [4FJ^2 - J*I^2] = 0 + + // E x^2 + F y^2 + G xy + H x + I y + J = 0 + + a = E * I * I - G * H * I + F * H * H; + b = 4.0 * F * H * J - 2.0 * G * I * J; + c = 4.0 * F * J * J - J * I * I; + + //System.out.println("a="+a+" b="+b+" c="+c); + double sqrtClause = b * b - 4.0 * a * c; + //System.out.println("sqrtClause="+sqrtClause); + if (Math.abs(sqrtClause) < MINIMUM_RESOLUTION_SQUARED) { + //System.err.println(" One solution; sqrt clause was "+sqrtClause); + double x0 = -b / (2.0 * a); + double y0 = (-2.0 * J - H * x0) / I; + double z0 = (-A * x0 - B * y0 - D) / C; + // Verify that x&y fulfill the equation + // 2Ex^2 + 2Fy^2 + 2Gxy + Hx + Iy = 0 + addPoint(boundsInfo, bounds, x0, y0, z0); + } else if (sqrtClause > 0.0) { + //System.err.println(" Two solutions"); + double sqrtResult = Math.sqrt(sqrtClause); + double denom = 1.0 / (2.0 * a); + double Idenom = 1.0 / I; + double Cdenom = 1.0 / C; + + double x0a = (-b + sqrtResult) * denom; + double x0b = (-b - sqrtResult) * denom; + double y0a = (-2.0 * J - H * x0a) * Idenom; + double y0b = (-2.0 * J - H * x0b) * Idenom; + double z0a = (-A * x0a - B * y0a - D) * Cdenom; + double z0b = (-A * x0b - B * y0b - D) * Cdenom; + + addPoint(boundsInfo, bounds, x0a, y0a, z0a); + addPoint(boundsInfo, bounds, x0b, y0b, z0b); + } + } } - return findIntersections(q,bounds,moreBounds).length > 0; + } } - /** Returns true if this plane and the other plane are identical within the margin of error. - */ - protected boolean isNumericallyIdentical(final Plane p) { - // We can get the correlation by just doing a parallel plane check. If that passes, then compute a point on the plane - // (using D) and see if it also on the other plane. - if (Math.abs(this.y * p.z - this.z * p.y) >= MINIMUM_RESOLUTION) - return false; - if (Math.abs(this.z * p.x - this.x * p.z) >= MINIMUM_RESOLUTION) - return false; - if (Math.abs(this.x * p.y - this.y * p.x) >= MINIMUM_RESOLUTION) - return false; + } - // Now, see whether the parallel planes are in fact on top of one another. - // The math: - // We need a single point that fulfills: - // Ax + By + Cz + D = 0 - // Pick: - // x0 = -(A * D) / (A^2 + B^2 + C^2) - // y0 = -(B * D) / (A^2 + B^2 + C^2) - // z0 = -(C * D) / (A^2 + B^2 + C^2) - // Check: - // A (x0) + B (y0) + C (z0) + D =? 0 - // A (-(A * D) / (A^2 + B^2 + C^2)) + B (-(B * D) / (A^2 + B^2 + C^2)) + C (-(C * D) / (A^2 + B^2 + C^2)) + D ?= 0 - // -D [ A^2 / (A^2 + B^2 + C^2) + B^2 / (A^2 + B^2 + C^2) + C^2 / (A^2 + B^2 + C^2)] + D ?= 0 - // Yes. - final double denom = 1.0 / (p.x * p.x + p.y * p.y + p.z * p.z); - return evaluateIsZero(- p.x * p.D * denom, - p.y * p.D * denom, - p.z * p.D * denom); + protected static void addPoint(final Bounds boundsInfo, final Membership[] bounds, final double x, final double y, final double z) { + //System.err.println(" Want to add point x="+x+" y="+y+" z="+z); + // Make sure the discovered point is within the bounds + for (Membership bound : bounds) { + if (!bound.isWithin(x, y, z)) + return; } + // Add the point + //System.err.println(" point added"); + //System.out.println("Adding point x="+x+" y="+y+" z="+z); + boundsInfo.addPoint(x, y, z); + } - protected static boolean meetsAllBounds(final GeoPoint p, final Membership[] bounds, final Membership[] moreBounds) { - for (final Membership bound : bounds) { - if (!bound.isWithin(p)) - return false; + /** + * Determine whether the plane intersects another plane within the + * bounds provided. + * + * @param q is the other plane. + * @param notablePoints are points to look at to disambiguate cases when the two planes are identical. + * @param moreNotablePoints are additional points to look at to disambiguate cases when the two planes are identical. + * @param bounds is one part of the bounds. + * @param moreBounds are more bounds. + * @return true if there's an intersection. + */ + public boolean intersects(final Plane q, final GeoPoint[] notablePoints, final GeoPoint[] moreNotablePoints, final Membership[] bounds, final Membership... moreBounds) { + //System.err.println("Does plane "+this+" intersect with plane "+q); + // If the two planes are identical, then the math will find no points of intersection. + // So a special case of this is to check for plane equality. But that is not enough, because + // what we really need at that point is to determine whether overlap occurs between the two parts of the intersection + // of plane and circle. That is, are there *any* points on the plane that are within the bounds described? + if (isNumericallyIdentical(q)) { + //System.err.println(" Identical plane"); + // The only way to efficiently figure this out will be to have a list of trial points available to evaluate. + // We look for any point that fulfills all the bounds. + for (GeoPoint p : notablePoints) { + if (meetsAllBounds(p, bounds, moreBounds)) { + //System.err.println(" found a notable point in bounds, so intersects"); + return true; } - for (final Membership bound : moreBounds) { - if (!bound.isWithin(p)) - return false; + } + for (GeoPoint p : moreNotablePoints) { + if (meetsAllBounds(p, bounds, moreBounds)) { + //System.err.println(" found a notable point in bounds, so intersects"); + return true; } - return true; - } - - /** Find a sample point on the intersection between two planes and the unit sphere. - */ - public GeoPoint getSampleIntersectionPoint(final Plane q) { - final GeoPoint[] intersections = findIntersections(q, NO_BOUNDS, NO_BOUNDS); - if (intersections.length == 0) - return null; - return intersections[0]; - } - - @Override - public String toString() { - return "[A="+x+", B="+y+"; C="+z+"; D="+D+"]"; - } - - @Override - public boolean equals(Object o) { - if (!super.equals(o)) - return false; - if (!(o instanceof Plane)) - return false; - Plane other = (Plane)o; - return other.D == D; + } + //System.err.println(" no notable points inside found; no intersection"); + return false; } + return findIntersections(q, bounds, moreBounds).length > 0; + } - @Override - public int hashCode() { - int result = super.hashCode(); - long temp; - temp = Double.doubleToLongBits(D); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; + /** + * Returns true if this plane and the other plane are identical within the margin of error. + */ + protected boolean isNumericallyIdentical(final Plane p) { + // We can get the correlation by just doing a parallel plane check. If that passes, then compute a point on the plane + // (using D) and see if it also on the other plane. + if (Math.abs(this.y * p.z - this.z * p.y) >= MINIMUM_RESOLUTION) + return false; + if (Math.abs(this.z * p.x - this.x * p.z) >= MINIMUM_RESOLUTION) + return false; + if (Math.abs(this.x * p.y - this.y * p.x) >= MINIMUM_RESOLUTION) + return false; + + // Now, see whether the parallel planes are in fact on top of one another. + // The math: + // We need a single point that fulfills: + // Ax + By + Cz + D = 0 + // Pick: + // x0 = -(A * D) / (A^2 + B^2 + C^2) + // y0 = -(B * D) / (A^2 + B^2 + C^2) + // z0 = -(C * D) / (A^2 + B^2 + C^2) + // Check: + // A (x0) + B (y0) + C (z0) + D =? 0 + // A (-(A * D) / (A^2 + B^2 + C^2)) + B (-(B * D) / (A^2 + B^2 + C^2)) + C (-(C * D) / (A^2 + B^2 + C^2)) + D ?= 0 + // -D [ A^2 / (A^2 + B^2 + C^2) + B^2 / (A^2 + B^2 + C^2) + C^2 / (A^2 + B^2 + C^2)] + D ?= 0 + // Yes. + final double denom = 1.0 / (p.x * p.x + p.y * p.y + p.z * p.z); + return evaluateIsZero(-p.x * p.D * denom, -p.y * p.D * denom, -p.z * p.D * denom); + } + + protected static boolean meetsAllBounds(final GeoPoint p, final Membership[] bounds, final Membership[] moreBounds) { + for (final Membership bound : bounds) { + if (!bound.isWithin(p)) + return false; } + for (final Membership bound : moreBounds) { + if (!bound.isWithin(p)) + return false; + } + return true; + } + + /** + * Find a sample point on the intersection between two planes and the unit sphere. + */ + public GeoPoint getSampleIntersectionPoint(final Plane q) { + final GeoPoint[] intersections = findIntersections(q, NO_BOUNDS, NO_BOUNDS); + if (intersections.length == 0) + return null; + return intersections[0]; + } + + @Override + public String toString() { + return "[A=" + x + ", B=" + y + "; C=" + z + "; D=" + D + "]"; + } + + @Override + public boolean equals(Object o) { + if (!super.equals(o)) + return false; + if (!(o instanceof Plane)) + return false; + Plane other = (Plane) o; + return other.D == D; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + long temp; + temp = Double.doubleToLongBits(D); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java index 0e10ab0d833..389d669baea 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/SidedPlane.java @@ -17,95 +17,107 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Combination of a plane, and a sign value indicating what evaluation values are on the correct -* side of the plane. -*/ -public class SidedPlane extends Plane implements Membership -{ - public final double sigNum; +/** + * Combination of a plane, and a sign value indicating what evaluation values are on the correct + * side of the plane. + */ +public class SidedPlane extends Plane implements Membership { + public final double sigNum; - /** Construct a SidedPlane identical to an existing one, but reversed. - *@param sidedPlane is the existing plane. - */ - public SidedPlane(SidedPlane sidedPlane) { - super(sidedPlane,sidedPlane.D); - this.sigNum = -sidedPlane.sigNum; - } + /** + * Construct a SidedPlane identical to an existing one, but reversed. + * + * @param sidedPlane is the existing plane. + */ + public SidedPlane(SidedPlane sidedPlane) { + super(sidedPlane, sidedPlane.D); + this.sigNum = -sidedPlane.sigNum; + } - /** Construct a sided plane from a pair of vectors describing points, and including - * origin, plus a point p which describes the side. - *@param p point to evaluate - *@param A is the first in-plane point - *@param B is the second in-plane point - */ - public SidedPlane(Vector p, Vector A, Vector B) { - super(A,B); - sigNum = Math.signum(evaluate(p)); - } + /** + * Construct a sided plane from a pair of vectors describing points, and including + * origin, plus a point p which describes the side. + * + * @param p point to evaluate + * @param A is the first in-plane point + * @param B is the second in-plane point + */ + public SidedPlane(Vector p, Vector A, Vector B) { + super(A, B); + sigNum = Math.signum(evaluate(p)); + } - /** Construct a sided plane from a point and a Z coordinate. - *@param p point to evaluate. - *@param height is the Z coordinate of the plane. - */ - public SidedPlane(Vector p, double height) { - super(height); - sigNum = Math.signum(evaluate(p)); - } + /** + * Construct a sided plane from a point and a Z coordinate. + * + * @param p point to evaluate. + * @param height is the Z coordinate of the plane. + */ + public SidedPlane(Vector p, double height) { + super(height); + sigNum = Math.signum(evaluate(p)); + } - /** Construct a sided vertical plane from a point and specified x and y coordinates. - *@param p point to evaluate. - *@param x is the specified x. - *@param y is the specified y. - */ - public SidedPlane(Vector p, double x, double y) { - super(x,y); - sigNum = Math.signum(evaluate(p)); - } + /** + * Construct a sided vertical plane from a point and specified x and y coordinates. + * + * @param p point to evaluate. + * @param x is the specified x. + * @param y is the specified y. + */ + public SidedPlane(Vector p, double x, double y) { + super(x, y); + sigNum = Math.signum(evaluate(p)); + } - /** Construct a sided plane with a normal vector and offset. - *@param p point to evaluate. - *@param v is the normal vector. - *@param D is the origin offset for the plan. - */ - public SidedPlane(Vector p, Vector v, double D) { - super(v,D); - sigNum = Math.signum(evaluate(p)); - } + /** + * Construct a sided plane with a normal vector and offset. + * + * @param p point to evaluate. + * @param v is the normal vector. + * @param D is the origin offset for the plan. + */ + public SidedPlane(Vector p, Vector v, double D) { + super(v, D); + sigNum = Math.signum(evaluate(p)); + } - /** Check if a point is within this shape. - *@param point is the point to check. - *@return true if the point is within this shape - */ - @Override - public boolean isWithin(Vector point) - { - double evalResult = evaluate(point); - if (Math.abs(evalResult) < MINIMUM_RESOLUTION) - return true; - double sigNum = Math.signum(evalResult); - return sigNum == this.sigNum; - } + /** + * Check if a point is within this shape. + * + * @param point is the point to check. + * @return true if the point is within this shape + */ + @Override + public boolean isWithin(Vector point) { + double evalResult = evaluate(point); + if (Math.abs(evalResult) < MINIMUM_RESOLUTION) + return true; + double sigNum = Math.signum(evalResult); + return sigNum == this.sigNum; + } - /** Check if a point is within this shape. - *@param x is x coordinate of point to check. - *@param y is y coordinate of point to check. - *@param z is z coordinate of point to check. - *@return true if the point is within this shape - */ - @Override - public boolean isWithin(double x, double y, double z) - { - double evalResult = evaluate(x,y,z); - if (Math.abs(evalResult) < MINIMUM_RESOLUTION) - return true; - double sigNum = Math.signum(evalResult); - return sigNum == this.sigNum; - } - + /** + * Check if a point is within this shape. + * + * @param x is x coordinate of point to check. + * @param y is y coordinate of point to check. + * @param z is z coordinate of point to check. + * @return true if the point is within this shape + */ + @Override + public boolean isWithin(double x, double y, double z) { + double evalResult = evaluate(x, y, z); + if (Math.abs(evalResult) < MINIMUM_RESOLUTION) + return true; + double sigNum = Math.signum(evalResult); + return sigNum == this.sigNum; + } - @Override - public String toString() { - return "[A="+x+", B="+y+", C="+z+", D="+D+", side="+sigNum+"]"; - } + + @Override + public String toString() { + return "[A=" + x + ", B=" + y + ", C=" + z + ", D=" + D + ", side=" + sigNum + "]"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Tools.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Tools.java index 090e27bcf3e..7528de87cfc 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Tools.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Tools.java @@ -17,24 +17,24 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** Static methods globally useful for 3d geometric work. +/** + * Static methods globally useful for 3d geometric work. */ -public class Tools -{ - private Tools() - { - } - - /** Java acos yields a NAN if you take an arc-cos of an - * angle that's just a tiny bit greater than 1.0, so - * here's a more resilient version. - */ - public static double safeAcos(double value) { - if (value > 1.0) - value = 1.0; - else if (value < -1.0) - value = -1.0; - return Math.acos(value); - } +public class Tools { + private Tools() { + } + + /** + * Java acos yields a NAN if you take an arc-cos of an + * angle that's just a tiny bit greater than 1.0, so + * here's a more resilient version. + */ + public static double safeAcos(double value) { + if (value > 1.0) + value = 1.0; + else if (value < -1.0) + value = -1.0; + return Math.acos(value); + } } \ No newline at end of file diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java index 4a658b40426..f7c4e4e0ce1 100755 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/Vector.java @@ -17,270 +17,309 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -/** A 3d vector in space, not necessarily - * going through the origin. */ -public class Vector -{ - /** Values that are all considered to be essentially zero have a magnitude - * less than this. */ - public static final double MINIMUM_RESOLUTION = 1e-12; - /** For squared quantities, the bound is squared too. - */ - public static final double MINIMUM_RESOLUTION_SQUARED = MINIMUM_RESOLUTION * MINIMUM_RESOLUTION; - - public final double x; - public final double y; - public final double z; +/** + * A 3d vector in space, not necessarily + * going through the origin. + */ +public class Vector { + /** + * Values that are all considered to be essentially zero have a magnitude + * less than this. + */ + public static final double MINIMUM_RESOLUTION = 1e-12; + /** + * For squared quantities, the bound is squared too. + */ + public static final double MINIMUM_RESOLUTION_SQUARED = MINIMUM_RESOLUTION * MINIMUM_RESOLUTION; - /** Construct from (U.S.) x,y,z coordinates. - */ - public Vector(double x, double y, double z) - { - this.x = x; - this.y = y; - this.z = z; - } - - /** Construct a vector that is perpendicular to - * two other (non-zero) vectors. If the vectors are parallel, - * the result vector will have magnitude 0. - *@param A is the first vector - *@param B is the second - */ - public Vector(final Vector A, final Vector B) { - // x = u2v3 - u3v2 - // y = u3v1 - u1v3 - // z = u1v2 - u2v1 + public final double x; + public final double y; + public final double z; - this(A.y * B.z - A.z * B.y, - A.z * B.x - A.x * B.z, - A.x * B.y - A.y * B.x); - } + /** + * Construct from (U.S.) x,y,z coordinates. + */ + public Vector(double x, double y, double z) { + this.x = x; + this.y = y; + this.z = z; + } - /** Compute a normalized unit vector based on the current vector. - *@return the normalized vector, or null if the current vector has - * a magnitude of zero. - */ - public Vector normalize() { - double denom = magnitude(); - if (denom < MINIMUM_RESOLUTION) - // Degenerate, can't normalize - return null; - double normFactor = 1.0/denom; - return new Vector(x*normFactor,y*normFactor,z*normFactor); - } - - /** Do a dot product. - *@param v is the vector to multiply. - *@return the result. - */ - public double dotProduct(final Vector v) { - return this.x * v.x + this.y * v.y + this.z * v.z; - } + /** + * Construct a vector that is perpendicular to + * two other (non-zero) vectors. If the vectors are parallel, + * the result vector will have magnitude 0. + * + * @param A is the first vector + * @param B is the second + */ + public Vector(final Vector A, final Vector B) { + // x = u2v3 - u3v2 + // y = u3v1 - u1v3 + // z = u1v2 - u2v1 - /** Do a dot product. - *@param x is the x value of the vector to multiply. - *@param y is the y value of the vector to multiply. - *@param z is the z value of the vector to multiply. - *@return the result. - */ - public double dotProduct(final double x, final double y, final double z) { - return this.x * x + this.y * y + this.z * z; - } - - /** Determine if this vector, taken from the origin, - * describes a point within a set of planes. - *@param bounds is the first part of the set of planes. - *@param moreBounds is the second part of the set of planes. - *@return true if the point is within the bounds. - */ - public boolean isWithin(final Membership[] bounds, final Membership[] moreBounds) { - // Return true if the point described is within all provided bounds - for (Membership bound : bounds) { - if (bound != null && !bound.isWithin(this)) - return false; - } - for (Membership bound : moreBounds) { - if (bound != null && !bound.isWithin(this)) - return false; - } - return true; - } + this(A.y * B.z - A.z * B.y, + A.z * B.x - A.x * B.z, + A.x * B.y - A.y * B.x); + } - /** Translate vector. - */ - public Vector translate(final double xOffset, final double yOffset, final double zOffset) { - return new Vector(x - xOffset, y - yOffset, z - zOffset); - } - - /** Rotate vector counter-clockwise in x-y by an angle. - */ - public Vector rotateXY(final double angle) { - return rotateXY(Math.sin(angle),Math.cos(angle)); - } - - /** Rotate vector counter-clockwise in x-y by an angle, expressed as sin and cos. - */ - public Vector rotateXY(final double sinAngle, final double cosAngle) { - return new Vector(x * cosAngle - y * sinAngle, x * sinAngle + y * cosAngle, z); - } + /** + * Compute a normalized unit vector based on the current vector. + * + * @return the normalized vector, or null if the current vector has + * a magnitude of zero. + */ + public Vector normalize() { + double denom = magnitude(); + if (denom < MINIMUM_RESOLUTION) + // Degenerate, can't normalize + return null; + double normFactor = 1.0 / denom; + return new Vector(x * normFactor, y * normFactor, z * normFactor); + } - /** Rotate vector counter-clockwise in x-z by an angle. - */ - public Vector rotateXZ(final double angle) { - return rotateXZ(Math.sin(angle),Math.cos(angle)); - } - - /** Rotate vector counter-clockwise in x-z by an angle, expressed as sin and cos. - */ - public Vector rotateXZ(final double sinAngle, final double cosAngle) { - return new Vector(x * cosAngle - z * sinAngle, y, x * sinAngle + z * cosAngle); - } + /** + * Do a dot product. + * + * @param v is the vector to multiply. + * @return the result. + */ + public double dotProduct(final Vector v) { + return this.x * v.x + this.y * v.y + this.z * v.z; + } - /** Rotate vector counter-clockwise in z-y by an angle. - */ - public Vector rotateZY(final double angle) { - return rotateZY(Math.sin(angle),Math.cos(angle)); - } - - /** Rotate vector counter-clockwise in z-y by an angle, expressed as sin and cos. - */ - public Vector rotateZY(final double sinAngle, final double cosAngle) { - return new Vector(x, z * sinAngle + y * cosAngle, z * cosAngle - y * sinAngle); - } + /** + * Do a dot product. + * + * @param x is the x value of the vector to multiply. + * @param y is the y value of the vector to multiply. + * @param z is the z value of the vector to multiply. + * @return the result. + */ + public double dotProduct(final double x, final double y, final double z) { + return this.x * x + this.y * y + this.z * z; + } - /** Compute the square of a straight-line distance to a point described by the - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI. - *@param v is the vector to compute a distance to. - *@return the square of the linear distance. - */ - public double linearDistanceSquared(final Vector v) { - double deltaX = this.x - v.x; - double deltaY = this.y - v.y; - double deltaZ = this.z - v.z; - return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; + /** + * Determine if this vector, taken from the origin, + * describes a point within a set of planes. + * + * @param bounds is the first part of the set of planes. + * @param moreBounds is the second part of the set of planes. + * @return true if the point is within the bounds. + */ + public boolean isWithin(final Membership[] bounds, final Membership[] moreBounds) { + // Return true if the point described is within all provided bounds + for (Membership bound : bounds) { + if (bound != null && !bound.isWithin(this)) + return false; } - - /** Compute the square of a straight-line distance to a point described by the - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI. - *@param x is the x part of the vector to compute a distance to. - *@param y is the y part of the vector to compute a distance to. - *@param z is the z part of the vector to compute a distance to. - *@return the square of the linear distance. - */ - public double linearDistanceSquared(final double x, final double y, final double z) { - double deltaX = this.x - x; - double deltaY = this.y - y; - double deltaZ = this.z - z; - return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; + for (Membership bound : moreBounds) { + if (bound != null && !bound.isWithin(this)) + return false; } + return true; + } - /** Compute the straight-line distance to a point described by the - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI. - *@param v is the vector to compute a distance to. - *@return the linear distance. - */ - public double linearDistance(final Vector v) { - return Math.sqrt(linearDistanceSquared(v)); - } - - /** Compute the straight-line distance to a point described by the - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI. - *@param x is the x part of the vector to compute a distance to. - *@param y is the y part of the vector to compute a distance to. - *@param z is the z part of the vector to compute a distance to. - *@return the linear distance. - */ - public double linearDistance(final double x, final double y, final double z) { - return Math.sqrt(linearDistanceSquared(x,y,z)); - } - - /** Compute the square of the normal distance to a vector described by a - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI/2. - *@param v is the vector to compute a distance to. - *@return the square of the normal distance. - */ - public double normalDistanceSquared(final Vector v) { - double t = dotProduct(v); - double deltaX = this.x * t - v.x; - double deltaY = this.y * t - v.y; - double deltaZ = this.z * t - v.z; - return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; - } + /** + * Translate vector. + */ + public Vector translate(final double xOffset, final double yOffset, final double zOffset) { + return new Vector(x - xOffset, y - yOffset, z - zOffset); + } - /** Compute the square of the normal distance to a vector described by a - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI/2. - *@param x is the x part of the vector to compute a distance to. - *@param y is the y part of the vector to compute a distance to. - *@param z is the z part of the vector to compute a distance to. - *@return the square of the normal distance. - */ - public double normalDistanceSquared(final double x, final double y, final double z) { - double t = dotProduct(x,y,z); - double deltaX = this.x * t - x; - double deltaY = this.y * t - y; - double deltaZ = this.z * t - z; - return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; - } - - /** Compute the normal (perpendicular) distance to a vector described by a - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI/2. - *@param v is the vector to compute a distance to. - *@return the normal distance. - */ - public double normalDistance(final Vector v) { - return Math.sqrt(normalDistanceSquared(v)); - } + /** + * Rotate vector counter-clockwise in x-y by an angle. + */ + public Vector rotateXY(final double angle) { + return rotateXY(Math.sin(angle), Math.cos(angle)); + } - /** Compute the normal (perpendicular) distance to a vector described by a - * vector taken from the origin. - * Monotonically increasing for arc distances up to PI/2. - *@param x is the x part of the vector to compute a distance to. - *@param y is the y part of the vector to compute a distance to. - *@param z is the z part of the vector to compute a distance to. - *@return the normal distance. - */ - public double normalDistance(final double x, final double y, final double z) { - return Math.sqrt(normalDistanceSquared(x,y,z)); - } - - /** Compute the magnitude of this vector. - *@return the magnitude. - */ - public double magnitude() { - return Math.sqrt(x * x + y * y + z * z); - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Vector)) - return false; - Vector other = (Vector)o; - return (other.x == x && other.y == y && other.z == z); - } + /** + * Rotate vector counter-clockwise in x-y by an angle, expressed as sin and cos. + */ + public Vector rotateXY(final double sinAngle, final double cosAngle) { + return new Vector(x * cosAngle - y * sinAngle, x * sinAngle + y * cosAngle, z); + } - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(x); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(y); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(z); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } + /** + * Rotate vector counter-clockwise in x-z by an angle. + */ + public Vector rotateXZ(final double angle) { + return rotateXZ(Math.sin(angle), Math.cos(angle)); + } - @Override - public String toString() { - return "[X="+x+", Y="+y+", Z="+z+"]"; - } + /** + * Rotate vector counter-clockwise in x-z by an angle, expressed as sin and cos. + */ + public Vector rotateXZ(final double sinAngle, final double cosAngle) { + return new Vector(x * cosAngle - z * sinAngle, y, x * sinAngle + z * cosAngle); + } + + /** + * Rotate vector counter-clockwise in z-y by an angle. + */ + public Vector rotateZY(final double angle) { + return rotateZY(Math.sin(angle), Math.cos(angle)); + } + + /** + * Rotate vector counter-clockwise in z-y by an angle, expressed as sin and cos. + */ + public Vector rotateZY(final double sinAngle, final double cosAngle) { + return new Vector(x, z * sinAngle + y * cosAngle, z * cosAngle - y * sinAngle); + } + + /** + * Compute the square of a straight-line distance to a point described by the + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI. + * + * @param v is the vector to compute a distance to. + * @return the square of the linear distance. + */ + public double linearDistanceSquared(final Vector v) { + double deltaX = this.x - v.x; + double deltaY = this.y - v.y; + double deltaZ = this.z - v.z; + return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; + } + + /** + * Compute the square of a straight-line distance to a point described by the + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI. + * + * @param x is the x part of the vector to compute a distance to. + * @param y is the y part of the vector to compute a distance to. + * @param z is the z part of the vector to compute a distance to. + * @return the square of the linear distance. + */ + public double linearDistanceSquared(final double x, final double y, final double z) { + double deltaX = this.x - x; + double deltaY = this.y - y; + double deltaZ = this.z - z; + return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; + } + + /** + * Compute the straight-line distance to a point described by the + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI. + * + * @param v is the vector to compute a distance to. + * @return the linear distance. + */ + public double linearDistance(final Vector v) { + return Math.sqrt(linearDistanceSquared(v)); + } + + /** + * Compute the straight-line distance to a point described by the + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI. + * + * @param x is the x part of the vector to compute a distance to. + * @param y is the y part of the vector to compute a distance to. + * @param z is the z part of the vector to compute a distance to. + * @return the linear distance. + */ + public double linearDistance(final double x, final double y, final double z) { + return Math.sqrt(linearDistanceSquared(x, y, z)); + } + + /** + * Compute the square of the normal distance to a vector described by a + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI/2. + * + * @param v is the vector to compute a distance to. + * @return the square of the normal distance. + */ + public double normalDistanceSquared(final Vector v) { + double t = dotProduct(v); + double deltaX = this.x * t - v.x; + double deltaY = this.y * t - v.y; + double deltaZ = this.z * t - v.z; + return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; + } + + /** + * Compute the square of the normal distance to a vector described by a + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI/2. + * + * @param x is the x part of the vector to compute a distance to. + * @param y is the y part of the vector to compute a distance to. + * @param z is the z part of the vector to compute a distance to. + * @return the square of the normal distance. + */ + public double normalDistanceSquared(final double x, final double y, final double z) { + double t = dotProduct(x, y, z); + double deltaX = this.x * t - x; + double deltaY = this.y * t - y; + double deltaZ = this.z * t - z; + return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ; + } + + /** + * Compute the normal (perpendicular) distance to a vector described by a + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI/2. + * + * @param v is the vector to compute a distance to. + * @return the normal distance. + */ + public double normalDistance(final Vector v) { + return Math.sqrt(normalDistanceSquared(v)); + } + + /** + * Compute the normal (perpendicular) distance to a vector described by a + * vector taken from the origin. + * Monotonically increasing for arc distances up to PI/2. + * + * @param x is the x part of the vector to compute a distance to. + * @param y is the y part of the vector to compute a distance to. + * @param z is the z part of the vector to compute a distance to. + * @return the normal distance. + */ + public double normalDistance(final double x, final double y, final double z) { + return Math.sqrt(normalDistanceSquared(x, y, z)); + } + + /** + * Compute the magnitude of this vector. + * + * @return the magnitude. + */ + public double magnitude() { + return Math.sqrt(x * x + y * y + z * z); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Vector)) + return false; + Vector other = (Vector) o; + return (other.x == x && other.y == y && other.z == z); + } + + @Override + public int hashCode() { + int result; + long temp; + temp = Double.doubleToLongBits(x); + result = (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(y); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(z); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public String toString() { + return "[X=" + x + ", Y=" + y + ", Z=" + z + "]"; + } } diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/package-info.java b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/package-info.java index 6faf9fac0fd..c4c525bfa59 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/package-info.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/spatial4j/geo3d/package-info.java @@ -1,2 +1,4 @@ -/** Shapes implemented using 3D planar geometry. */ +/** + * Shapes implemented using 3D planar geometry. + */ package org.apache.lucene.spatial.spatial4j.geo3d; \ No newline at end of file diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java index 51b39030664..93a47325894 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dRptTest.java @@ -78,24 +78,8 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { } @Test - public void testFailure() throws IOException { + public void testFailure1() throws IOException { setupStrategy(); - // [junit4] > Throwable #1: java.lang.AssertionError: [Intersects] qIdx:25 Shouldn't match I#1:Rect(minX=-49.0,maxX=-45.0,minY=73.0,maxY=86.0) - // Q:Geo3dShape{GeoCompositeMembershipShape: {[GeoCompositeMembershipShape: {[GeoConvexPolygon: {[ - // [X=-0.8606462131055999, Y=0.4385211485883089, Z=-0.25881904510252074], - // [X=-0.4668467715008339, Y=0.28050984011500923, Z=-0.838670567945424], - // [X=-0.9702957262759965, Y=1.1882695554102554E-16, Z=0.24192189559966773]]}]}, - // GeoConvexPolygon: {[[X=0.8473975608908426, Y=-0.43177062311338915, Z=0.3090169943749474], - //[X=-0.4668467715008339, Y=0.28050984011500923, Z=-0.838670567945424], - // [X=-0.8606462131055999, Y=0.4385211485883089, Z=-0.25881904510252074]]}]}} - // - // Points in order (I think): - // - // [X=0.8473975608908426, Y=-0.43177062311338915, Z=0.3090169943749474], - //[X=-0.4668467715008339, Y=0.28050984011500923, Z=-0.838670567945424], - // [X=-0.9702957262759965, Y=1.1882695554102554E-16, Z=0.24192189559966773], - // [X=-0.8606462131055999, Y=0.4385211485883089, Z=-0.25881904510252074], - // Index: 0 final List points = new ArrayList(); points.add(new GeoPoint(18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS)); points.add(new GeoPoint(-57 * DEGREES_TO_RADIANS, 146 * DEGREES_TO_RADIANS)); @@ -108,8 +92,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase { } @Test - @Repeat(iterations = 2000) - //@Seed("B808B88D6F8E285C") + @Repeat(iterations = 10) public void testOperations() throws IOException { setupStrategy(); diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java index 02d38d85248..60278b2621b 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/Geo3dShapeRectRelationTest.java @@ -54,26 +54,7 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTest { protected final static double RADIANS_PER_DEGREE = Math.PI/180.0; @Test - public void testFailure() { - /* - [junit4] 1> S-R Rel: {}, Shape {}, Rectangle {} [WITHIN, Geo3dShape{GeoCompositeMembershipShape: {[ - GeoConvexPolygon: {points=[ - [X=0.35168818443386646, Y=-0.19637966197066342, Z=0.9152870857244183], - [X=0.5003343189532654, Y=0.522128543226148, Z=0.6906861469771293], - [X=0.8344549994139991, Y=0.216175219373972, Z=0.5069054433339593]] - edges={ - [A=-0.6135342247741855, B=0.21504338363863665, C=0.28188192383666794, D=0.0, side=-1.0] internal? false; - [A=0.11536057134002048, B=0.32272431860685813, C=-0.3275328920717585, D=0.0, side=-1.0] internal? false; - [A=0.29740830615965186, B=-0.5854932295360462, C=-0.2398962611358763, D=0.0, side=-1.0] internal? false; }}]}}, - Rect(minX=-30.0,maxX=62.0,minY=30.0,maxY=88.0)](no slf4j subst; sorry) - [junit4] FAILURE 1.85s J2 | Geo3dShapeRectRelationTest.testGeoPolygonRect <<< - [junit4] > Throwable #1: java.lang.AssertionError: Rect(minX=-30.0,maxX=62.0,minY=30.0,maxY=88.0) intersect Pt(x=82.75500168892472,y=34.2730264413182) - [junit4] > at __randomizedtesting.SeedInfo.seed([3EBD2127AF6641F7:3A64BDAC8843B64]:0) - [junit4] > at org.apache.lucene.spatial.spatial4j.RandomizedShapeTest._assertIntersect(RandomizedShapeTest.java:167) - [junit4] > at org.apache.lucene.spatial.spatial4j.RandomizedShapeTest.assertRelation(RandomizedShapeTest.java:152) - [junit4] > at org.apache.lucene.spatial.spatial4j.RectIntersectionTestHelper.testRelateWithRectangle(RectIntersectionTestHelper.java:105) - [junit4] > at org.apache.lucene.spatial.spatial4j.Geo3dShapeRectRelationTest.testGeoPolygonRect(Geo3dShapeRectRelationTest.java:219) - */ + public void testFailure1() { final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(88 * RADIANS_PER_DEGREE, 30 * RADIANS_PER_DEGREE, -30 * RADIANS_PER_DEGREE, 62 * RADIANS_PER_DEGREE); final List points = new ArrayList(); points.add(new GeoPoint(66.2465299717 * RADIANS_PER_DEGREE, -29.1786158537 * RADIANS_PER_DEGREE)); @@ -82,20 +63,7 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTest { final GeoShape path = GeoPolygonFactory.makeGeoPolygon(points,0); final GeoPoint point = new GeoPoint(34.2730264413182 * RADIANS_PER_DEGREE, 82.75500168892472 * RADIANS_PER_DEGREE); - - System.err.println("Rectangle = "+rect+"; path = "+path+"; point = "+point); - /* - [junit4] 2> Rectangle = GeoRectangle: {toplat=1.53588974175501(87.99999999999999), bottomlat=0.5235987755982988(29.999999999999996), leftlon=-0.5235987755982988(-29.999999999999996), rightlon=1.0821041362364843(62.0)}; - path = GeoCompositeMembershipShape: {[GeoConvexPolygon: {points=[ - [X=0.3516881844340107, Y=-0.1963796619709742, Z=0.9152870857242963], - [X=0.500334318953081, Y=0.5221285432268337, Z=0.6906861469767445], - [X=0.8344549994140144, Y=0.21617521937373424, Z=0.5069054433340355]] - edges={[A=-0.6135342247748885, B=0.21504338363844255, C=0.28188192383710364, D=0.0, side=-1.0] internal? false; - [A=0.1153605713406553, B=0.32272431860660283, C=-0.3275328920724975, D=0.0, side=-1.0] internal? false; - [A=0.29740830615958036, B=-0.5854932295358584, C=-0.2398962611360862, D=0.0, side=-1.0] internal? false; }}]}; - point = [X=0.10421465978661167, Y=0.8197657811637465, Z=0.5631370780889439] - */ // Apparently the rectangle thinks the polygon is completely within it... "shape inside rectangle" assertTrue(GeoArea.WITHIN == rect.getRelationship(path)); @@ -135,7 +103,6 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTest { } @Test - //@Seed("FAD1BAB12B6DCCFE") public void testGeoCircleRect() { new RectIntersectionTestHelper(ctx) { diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTest.java index 493d9585a3f..ab3633cc8ef 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/RandomizedShapeTest.java @@ -54,8 +54,9 @@ public abstract class RandomizedShapeTest extends RandomizedTest { this.ctx = ctx; } - public static void checkShapesImplementEquals( Class[] classes ) { - for( Class clazz : classes ) { + @SuppressWarnings("unchecked") + public static void checkShapesImplementEquals( Class[] classes ) { + for( Class clazz : classes ) { try { clazz.getDeclaredMethod( "equals", Object.class ); } catch (Exception e) { diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java index bb3e6648a78..a0c67dff92c 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoBBoxTest.java @@ -28,241 +28,242 @@ import static org.junit.Assert.assertTrue; public class GeoBBoxTest { - protected final double DEGREES_TO_RADIANS = Math.PI/180.0; - @Test - public void testBBoxDegenerate() { - GeoBBox box; - GeoConvexPolygon cp; - int relationship; - List points = new ArrayList(); - points.add(new GeoPoint(24*DEGREES_TO_RADIANS,-30*DEGREES_TO_RADIANS)); - points.add(new GeoPoint(-11*DEGREES_TO_RADIANS,101*DEGREES_TO_RADIANS)); - points.add(new GeoPoint(-49*DEGREES_TO_RADIANS,-176*DEGREES_TO_RADIANS)); - GeoMembershipShape shape = GeoPolygonFactory.makeGeoPolygon(points,0); - box = GeoBBoxFactory.makeGeoBBox(-64*DEGREES_TO_RADIANS,-64*DEGREES_TO_RADIANS,-180*DEGREES_TO_RADIANS,180*DEGREES_TO_RADIANS); - relationship = box.getRelationship(shape); - assertEquals(GeoArea.CONTAINS,relationship); - box = GeoBBoxFactory.makeGeoBBox(-61.85*DEGREES_TO_RADIANS,-67.5*DEGREES_TO_RADIANS,-180*DEGREES_TO_RADIANS,-168.75*DEGREES_TO_RADIANS); - System.out.println("Shape = "+shape+" Rect = "+box); - relationship = box.getRelationship(shape); - assertEquals(GeoArea.CONTAINS,relationship); - } - - @Test - public void testBBoxPointWithin() { - GeoBBox box; - GeoPoint gp; - // Standard normal Rect box, not crossing dateline - box = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, -1.0, 1.0); - gp = new GeoPoint(-0.1,0.0); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1,0.0); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5,0.0); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,1.1); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-1.1); - assertFalse(box.isWithin(gp)); - // Standard normal Rect box, crossing dateline - box = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, Math.PI-1.0, -Math.PI+1.0); - gp = new GeoPoint(-0.1,-Math.PI); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1,-Math.PI); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5,-Math.PI); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-Math.PI+1.1); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-Math.PI-1.1); - assertFalse(box.isWithin(gp)); - // Latitude zone rectangle - box = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, -Math.PI, Math.PI); - gp = new GeoPoint(-0.1,-Math.PI); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1,-Math.PI); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5,-Math.PI); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-Math.PI+1.1); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-Math.PI-1.1); - assertTrue(box.isWithin(gp)); - // World - box = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5,-Math.PI * 0.5, -Math.PI, Math.PI); - gp = new GeoPoint(-0.1,-Math.PI); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.1,-Math.PI); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.5,-Math.PI); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-Math.PI+1.1); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-Math.PI-1.1); - assertTrue(box.isWithin(gp)); + protected final double DEGREES_TO_RADIANS = Math.PI / 180.0; - } + @Test + public void testBBoxDegenerate() { + GeoBBox box; + GeoConvexPolygon cp; + int relationship; + List points = new ArrayList(); + points.add(new GeoPoint(24 * DEGREES_TO_RADIANS, -30 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(-11 * DEGREES_TO_RADIANS, 101 * DEGREES_TO_RADIANS)); + points.add(new GeoPoint(-49 * DEGREES_TO_RADIANS, -176 * DEGREES_TO_RADIANS)); + GeoMembershipShape shape = GeoPolygonFactory.makeGeoPolygon(points, 0); + box = GeoBBoxFactory.makeGeoBBox(-64 * DEGREES_TO_RADIANS, -64 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, 180 * DEGREES_TO_RADIANS); + relationship = box.getRelationship(shape); + assertEquals(GeoArea.CONTAINS, relationship); + box = GeoBBoxFactory.makeGeoBBox(-61.85 * DEGREES_TO_RADIANS, -67.5 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, -168.75 * DEGREES_TO_RADIANS); + System.out.println("Shape = " + shape + " Rect = " + box); + relationship = box.getRelationship(shape); + assertEquals(GeoArea.CONTAINS, relationship); + } - @Test - public void testBBoxExpand() { - GeoBBox box; - GeoPoint gp; - // Standard normal Rect box, not crossing dateline - box = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, -1.0, 1.0); - box = box.expand(0.1); - gp = new GeoPoint(0.05,0.0); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(0.15,0.0); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.25 - 0.05,0.0); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-Math.PI * 0.25 - 0.15,0.0); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-1.05); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1,-1.15); - assertFalse(box.isWithin(gp)); - gp = new GeoPoint(-0.1,1.05); - assertTrue(box.isWithin(gp)); - gp = new GeoPoint(-0.1,1.15); - assertFalse(box.isWithin(gp)); - } + @Test + public void testBBoxPointWithin() { + GeoBBox box; + GeoPoint gp; + // Standard normal Rect box, not crossing dateline + box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0); + gp = new GeoPoint(-0.1, 0.0); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(0.1, 0.0); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.5, 0.0); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, 1.1); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -1.1); + assertFalse(box.isWithin(gp)); + // Standard normal Rect box, crossing dateline + box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0); + gp = new GeoPoint(-0.1, -Math.PI); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(0.1, -Math.PI); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.5, -Math.PI); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -Math.PI + 1.1); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -Math.PI - 1.1); + assertFalse(box.isWithin(gp)); + // Latitude zone rectangle + box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -Math.PI, Math.PI); + gp = new GeoPoint(-0.1, -Math.PI); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(0.1, -Math.PI); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.5, -Math.PI); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -Math.PI + 1.1); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -Math.PI - 1.1); + assertTrue(box.isWithin(gp)); + // World + box = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, Math.PI); + gp = new GeoPoint(-0.1, -Math.PI); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(0.1, -Math.PI); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.5, -Math.PI); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -Math.PI + 1.1); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -Math.PI - 1.1); + assertTrue(box.isWithin(gp)); - @Test - public void testBBoxBounds() { - GeoBBox c; - Bounds b; - - c = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, -1.0, 1.0); + } - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-1.0,b.getLeftLongitude(),0.000001); - assertEquals(1.0,b.getRightLongitude(),0.000001); - assertEquals(-Math.PI * 0.25,b.getMinLatitude(),0.000001); - assertEquals(0.0,b.getMaxLatitude(),0.000001); + @Test + public void testBBoxExpand() { + GeoBBox box; + GeoPoint gp; + // Standard normal Rect box, not crossing dateline + box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0); + box = box.expand(0.1); + gp = new GeoPoint(0.05, 0.0); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(0.15, 0.0); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.25 - 0.05, 0.0); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.25 - 0.15, 0.0); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -1.05); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-0.1, -1.15); + assertFalse(box.isWithin(gp)); + gp = new GeoPoint(-0.1, 1.05); + assertTrue(box.isWithin(gp)); + gp = new GeoPoint(-0.1, 1.15); + assertFalse(box.isWithin(gp)); + } - c = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, 1.0, -1.0); + @Test + public void testBBoxBounds() { + GeoBBox c; + Bounds b; - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - //assertEquals(1.0,b.getLeftLongitude(),0.000001); - //assertEquals(-1.0,b.getRightLongitude(),0.000001); - assertEquals(-Math.PI * 0.25,b.getMinLatitude(),0.000001); - assertEquals(0.0,b.getMaxLatitude(),0.000001); + c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-1.0, b.getLeftLongitude(), 0.000001); + assertEquals(1.0, b.getRightLongitude(), 0.000001); + assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); + assertEquals(0.0, b.getMaxLatitude(), 0.000001); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - assertEquals(-1.0,b.getLeftLongitude(),0.000001); - assertEquals(1.0,b.getRightLongitude(),0.000001); + c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, 1.0, -1.0); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 1.0, -1.0); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + //assertEquals(1.0,b.getLeftLongitude(),0.000001); + //assertEquals(-1.0,b.getRightLongitude(),0.000001); + assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); + assertEquals(0.0, b.getMaxLatitude(), 0.000001); - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - //assertEquals(1.0,b.getLeftLongitude(),0.000001); - //assertEquals(-1.0,b.getRightLongitude(),0.000001); + c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0); - // Check wide variants of rectangle and longitude slice + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + assertEquals(-1.0, b.getLeftLongitude(), 0.000001); + assertEquals(1.0, b.getRightLongitude(), 0.000001); - c = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, -Math.PI+0.1, Math.PI-0.1); + c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 1.0, -1.0); - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001); - //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001); - assertEquals(-Math.PI * 0.25,b.getMinLatitude(),0.000001); - assertEquals(0.0,b.getMaxLatitude(),0.000001); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + //assertEquals(1.0,b.getLeftLongitude(),0.000001); + //assertEquals(-1.0,b.getRightLongitude(),0.000001); - c = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, Math.PI-0.1, -Math.PI+0.1); + // Check wide variants of rectangle and longitude slice - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(Math.PI-0.1,b.getLeftLongitude(),0.000001); - assertEquals(-Math.PI+0.1,b.getRightLongitude(),0.000001); - assertEquals(-Math.PI * 0.25,b.getMinLatitude(),0.000001); - assertEquals(0.0,b.getMaxLatitude(),0.000001); + c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -Math.PI + 0.1, Math.PI - 0.1); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI+0.1, Math.PI-0.1); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001); + //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001); + assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); + assertEquals(0.0, b.getMaxLatitude(), 0.000001); - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001); - //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001); + c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, Math.PI - 0.1, -Math.PI + 0.1); - c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, Math.PI-0.1, -Math.PI+0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001); + assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001); + assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001); + assertEquals(0.0, b.getMaxLatitude(), 0.000001); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - assertEquals(Math.PI-0.1,b.getLeftLongitude(),0.000001); - assertEquals(-Math.PI+0.1,b.getRightLongitude(),0.000001); - - // Check latitude zone - c = GeoBBoxFactory.makeGeoBBox(1.0, -1.0, -Math.PI, Math.PI); + c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI + 0.1, Math.PI - 0.1); - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-1.0,b.getMinLatitude(),0.000001); - assertEquals(1.0,b.getMaxLatitude(),0.000001); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001); + //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001); - // Now, combine a few things to test the bounds object - GeoBBox c1; - GeoBBox c2; - - c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); - c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); + c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, Math.PI - 0.1, -Math.PI + 0.1); - b = new Bounds(); - b = c1.getBounds(b); - b = c2.getBounds(b); - assertTrue(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001); + assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001); - c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); - c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI*0.5); + // Check latitude zone + c = GeoBBoxFactory.makeGeoBBox(1.0, -1.0, -Math.PI, Math.PI); - b = new Bounds(); - b = c1.getBounds(b); - b = c2.getBounds(b); - assertTrue(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - //assertEquals(-Math.PI,b.getLeftLongitude(),0.000001); - //assertEquals(Math.PI*0.5,b.getRightLongitude(),0.000001); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-1.0, b.getMinLatitude(), 0.000001); + assertEquals(1.0, b.getMaxLatitude(), 0.000001); - c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI * 0.5, 0.0); - c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); + // Now, combine a few things to test the bounds object + GeoBBox c1; + GeoBBox c2; - b = new Bounds(); - b = c1.getBounds(b); - b = c2.getBounds(b); - assertTrue(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - //assertEquals(-Math.PI * 0.5,b.getLeftLongitude(),0.000001); - //assertEquals(Math.PI,b.getRightLongitude(),0.000001); + c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); + c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); - } + b = new Bounds(); + b = c1.getBounds(b); + b = c2.getBounds(b); + assertTrue(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + + c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0); + c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI * 0.5); + + b = new Bounds(); + b = c1.getBounds(b); + b = c2.getBounds(b); + assertTrue(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + //assertEquals(-Math.PI,b.getLeftLongitude(),0.000001); + //assertEquals(Math.PI*0.5,b.getRightLongitude(),0.000001); + + c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI * 0.5, 0.0); + c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI); + + b = new Bounds(); + b = c1.getBounds(b); + b = c2.getBounds(b); + assertTrue(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + //assertEquals(-Math.PI * 0.5,b.getLeftLongitude(),0.000001); + //assertEquals(Math.PI,b.getRightLongitude(),0.000001); + + } } diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java index a2678d749e7..2e16be200c6 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoCircleTest.java @@ -17,201 +17,203 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -import static org.junit.Assert.*; - import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class GeoCircleTest { - @Test - public void testCircleDistance() { - GeoCircle c; - GeoPoint gp; - c = new GeoCircle(0.0,-0.5,0.1); - gp = new GeoPoint(0.0,0.0); - assertEquals(Double.MAX_VALUE, c.computeArcDistance(gp), 0.0); - assertEquals(Double.MAX_VALUE, c.computeLinearDistance(gp), 0.0); - assertEquals(Double.MAX_VALUE, c.computeNormalDistance(gp), 0.0); - gp = new GeoPoint(0.0,-0.5); - assertEquals(0.0, c.computeArcDistance(gp), 0.000001); - assertEquals(0.0, c.computeLinearDistance(gp), 0.000001); - assertEquals(0.0, c.computeNormalDistance(gp), 0.000001); - gp = new GeoPoint(0.05,-0.5); - assertEquals(0.05, c.computeArcDistance(gp), 0.000001); - assertEquals(0.049995, c.computeLinearDistance(gp), 0.000001); - assertEquals(0.049979, c.computeNormalDistance(gp), 0.000001); - } + @Test + public void testCircleDistance() { + GeoCircle c; + GeoPoint gp; + c = new GeoCircle(0.0, -0.5, 0.1); + gp = new GeoPoint(0.0, 0.0); + assertEquals(Double.MAX_VALUE, c.computeArcDistance(gp), 0.0); + assertEquals(Double.MAX_VALUE, c.computeLinearDistance(gp), 0.0); + assertEquals(Double.MAX_VALUE, c.computeNormalDistance(gp), 0.0); + gp = new GeoPoint(0.0, -0.5); + assertEquals(0.0, c.computeArcDistance(gp), 0.000001); + assertEquals(0.0, c.computeLinearDistance(gp), 0.000001); + assertEquals(0.0, c.computeNormalDistance(gp), 0.000001); + gp = new GeoPoint(0.05, -0.5); + assertEquals(0.05, c.computeArcDistance(gp), 0.000001); + assertEquals(0.049995, c.computeLinearDistance(gp), 0.000001); + assertEquals(0.049979, c.computeNormalDistance(gp), 0.000001); + } - @Test - public void testCirclePointWithin() { - GeoCircle c; - GeoPoint gp; - c = new GeoCircle(0.0,-0.5,0.1); - gp = new GeoPoint(0.0,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.55); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.45); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,Math.PI); - assertFalse(c.isWithin(gp)); - } + @Test + public void testCirclePointWithin() { + GeoCircle c; + GeoPoint gp; + c = new GeoCircle(0.0, -0.5, 0.1); + gp = new GeoPoint(0.0, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.55); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.45); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(Math.PI * 0.5, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, Math.PI); + assertFalse(c.isWithin(gp)); + } - @Test - public void testCircleBounds() { - GeoCircle c; - Bounds b; - + @Test + public void testCircleBounds() { + GeoCircle c; + Bounds b; - // Vertical circle cases - c = new GeoCircle(0.0,-0.5,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-0.6,b.getLeftLongitude(),0.000001); - assertEquals(-0.4,b.getRightLongitude(),0.000001); - assertEquals(-0.1,b.getMinLatitude(),0.000001); - assertEquals(0.1,b.getMaxLatitude(),0.000001); - c = new GeoCircle(0.0,0.5,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.4,b.getLeftLongitude(),0.000001); - assertEquals(0.6,b.getRightLongitude(),0.000001); - assertEquals(-0.1,b.getMinLatitude(),0.000001); - assertEquals(0.1,b.getMaxLatitude(),0.000001); - c = new GeoCircle(0.0,0.0,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-0.1,b.getLeftLongitude(),0.000001); - assertEquals(0.1,b.getRightLongitude(),0.000001); - assertEquals(-0.1,b.getMinLatitude(),0.000001); - assertEquals(0.1,b.getMaxLatitude(),0.000001); - c = new GeoCircle(0.0,Math.PI,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(Math.PI-0.1,b.getLeftLongitude(),0.000001); - assertEquals(-Math.PI+0.1,b.getRightLongitude(),0.000001); - assertEquals(-0.1,b.getMinLatitude(),0.000001); - assertEquals(0.1,b.getMaxLatitude(),0.000001); - // Horizontal circle cases - c = new GeoCircle(Math.PI * 0.5,0.0,0.1); - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertTrue(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(Math.PI * 0.5 - 0.1,b.getMinLatitude(),0.000001); - c = new GeoCircle(-Math.PI * 0.5,0.0,0.1); - b = c.getBounds(null); - assertTrue(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertTrue(b.checkNoBottomLatitudeBound()); - assertEquals(-Math.PI * 0.5 + 0.1,b.getMaxLatitude(),0.000001); - - // Now do a somewhat tilted plane, facing different directions. - c = new GeoCircle(0.01,0.0,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(-0.1,b.getLeftLongitude(),0.00001); - assertEquals(0.1,b.getRightLongitude(),0.00001); - c = new GeoCircle(0.01,Math.PI,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(Math.PI-0.1,b.getLeftLongitude(),0.00001); - assertEquals(-Math.PI+0.1,b.getRightLongitude(),0.00001); + // Vertical circle cases + c = new GeoCircle(0.0, -0.5, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-0.6, b.getLeftLongitude(), 0.000001); + assertEquals(-0.4, b.getRightLongitude(), 0.000001); + assertEquals(-0.1, b.getMinLatitude(), 0.000001); + assertEquals(0.1, b.getMaxLatitude(), 0.000001); + c = new GeoCircle(0.0, 0.5, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.4, b.getLeftLongitude(), 0.000001); + assertEquals(0.6, b.getRightLongitude(), 0.000001); + assertEquals(-0.1, b.getMinLatitude(), 0.000001); + assertEquals(0.1, b.getMaxLatitude(), 0.000001); + c = new GeoCircle(0.0, 0.0, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-0.1, b.getLeftLongitude(), 0.000001); + assertEquals(0.1, b.getRightLongitude(), 0.000001); + assertEquals(-0.1, b.getMinLatitude(), 0.000001); + assertEquals(0.1, b.getMaxLatitude(), 0.000001); + c = new GeoCircle(0.0, Math.PI, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001); + assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001); + assertEquals(-0.1, b.getMinLatitude(), 0.000001); + assertEquals(0.1, b.getMaxLatitude(), 0.000001); + // Horizontal circle cases + c = new GeoCircle(Math.PI * 0.5, 0.0, 0.1); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertTrue(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(Math.PI * 0.5 - 0.1, b.getMinLatitude(), 0.000001); + c = new GeoCircle(-Math.PI * 0.5, 0.0, 0.1); + b = c.getBounds(null); + assertTrue(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertTrue(b.checkNoBottomLatitudeBound()); + assertEquals(-Math.PI * 0.5 + 0.1, b.getMaxLatitude(), 0.000001); - c = new GeoCircle(0.01,Math.PI * 0.5,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(Math.PI * 0.5 - 0.1,b.getLeftLongitude(),0.00001); - assertEquals(Math.PI * 0.5 + 0.1,b.getRightLongitude(),0.00001); + // Now do a somewhat tilted plane, facing different directions. + c = new GeoCircle(0.01, 0.0, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(-0.1, b.getLeftLongitude(), 0.00001); + assertEquals(0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(0.01,-Math.PI * 0.5,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(-Math.PI * 0.5 - 0.1,b.getLeftLongitude(),0.00001); - assertEquals(-Math.PI * 0.5 + 0.1,b.getRightLongitude(),0.00001); + c = new GeoCircle(0.01, Math.PI, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.00001); - // Slightly tilted, PI/4 direction. - c = new GeoCircle(0.01,Math.PI * 0.25,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001); - assertEquals(Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001); + c = new GeoCircle(0.01, Math.PI * 0.5, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(0.01,-Math.PI * 0.25,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(-Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001); - assertEquals(-Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001); + c = new GeoCircle(0.01, -Math.PI * 0.5, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(-Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(-Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(-0.01,Math.PI * 0.25,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.09,b.getMaxLatitude(),0.000001); - assertEquals(-0.11,b.getMinLatitude(),0.000001); - assertEquals(Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001); - assertEquals(Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001); + // Slightly tilted, PI/4 direction. + c = new GeoCircle(0.01, Math.PI * 0.25, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); - c = new GeoCircle(-0.01,-Math.PI * 0.25,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.09,b.getMaxLatitude(),0.000001); - assertEquals(-0.11,b.getMinLatitude(),0.000001); - assertEquals(-Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001); - assertEquals(-Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001); + c = new GeoCircle(0.01, -Math.PI * 0.25, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(-Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); - // Now do a somewhat tilted plane. - c = new GeoCircle(0.01,-0.5,0.1); - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(0.11,b.getMaxLatitude(),0.000001); - assertEquals(-0.09,b.getMinLatitude(),0.000001); - assertEquals(-0.6,b.getLeftLongitude(),0.00001); - assertEquals(-0.4,b.getRightLongitude(),0.00001); + c = new GeoCircle(-0.01, Math.PI * 0.25, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.09, b.getMaxLatitude(), 0.000001); + assertEquals(-0.11, b.getMinLatitude(), 0.000001); + assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); - } + c = new GeoCircle(-0.01, -Math.PI * 0.25, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.09, b.getMaxLatitude(), 0.000001); + assertEquals(-0.11, b.getMinLatitude(), 0.000001); + assertEquals(-Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001); + assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001); + + // Now do a somewhat tilted plane. + c = new GeoCircle(0.01, -0.5, 0.1); + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(0.11, b.getMaxLatitude(), 0.000001); + assertEquals(-0.09, b.getMinLatitude(), 0.000001); + assertEquals(-0.6, b.getLeftLongitude(), 0.00001); + assertEquals(-0.4, b.getRightLongitude(), 0.00001); + + } } diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java index b2fef30f2dc..1df933e05c9 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoConvexPolygonTest.java @@ -26,63 +26,63 @@ import static org.junit.Assert.assertTrue; public class GeoConvexPolygonTest { - @Test - public void testPolygonPointWithin() { - GeoConvexPolygon c; - GeoPoint gp; - c = new GeoConvexPolygon(-0.1,-0.5); - c.addPoint(0.0,-0.6,false); - c.addPoint(0.1,-0.5,false); - c.addPoint(0.0,-0.4,false); - c.donePoints(false); - // Sample some points within - gp = new GeoPoint(0.0,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.55); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.45); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(-0.05,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.05,-0.5); - assertTrue(c.isWithin(gp)); - // Sample some nearby points outside - gp = new GeoPoint(0.0,-0.65); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.35); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(-0.15,-0.5); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.15,-0.5); - assertFalse(c.isWithin(gp)); - // Random points outside - gp = new GeoPoint(0.0,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,Math.PI); - assertFalse(c.isWithin(gp)); - } + @Test + public void testPolygonPointWithin() { + GeoConvexPolygon c; + GeoPoint gp; + c = new GeoConvexPolygon(-0.1, -0.5); + c.addPoint(0.0, -0.6, false); + c.addPoint(0.1, -0.5, false); + c.addPoint(0.0, -0.4, false); + c.donePoints(false); + // Sample some points within + gp = new GeoPoint(0.0, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.55); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.45); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(-0.05, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.05, -0.5); + assertTrue(c.isWithin(gp)); + // Sample some nearby points outside + gp = new GeoPoint(0.0, -0.65); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.35); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(-0.15, -0.5); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.15, -0.5); + assertFalse(c.isWithin(gp)); + // Random points outside + gp = new GeoPoint(0.0, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(Math.PI * 0.5, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, Math.PI); + assertFalse(c.isWithin(gp)); + } - @Test - public void testPolygonBounds() { - GeoConvexPolygon c; - Bounds b; - - c = new GeoConvexPolygon(-0.1,-0.5); - c.addPoint(0.0,-0.6,false); - c.addPoint(0.1,-0.5,false); - c.addPoint(0.0,-0.4,false); - c.donePoints(false); + @Test + public void testPolygonBounds() { + GeoConvexPolygon c; + Bounds b; - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-0.6,b.getLeftLongitude(),0.000001); - assertEquals(-0.4,b.getRightLongitude(),0.000001); - assertEquals(-0.1,b.getMinLatitude(),0.000001); - assertEquals(0.1,b.getMaxLatitude(),0.000001); - } + c = new GeoConvexPolygon(-0.1, -0.5); + c.addPoint(0.0, -0.6, false); + c.addPoint(0.1, -0.5, false); + c.addPoint(0.0, -0.4, false); + c.donePoints(false); + + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-0.6, b.getLeftLongitude(), 0.000001); + assertEquals(-0.4, b.getRightLongitude(), 0.000001); + assertEquals(-0.1, b.getMinLatitude(), 0.000001); + assertEquals(0.1, b.getMaxLatitude(), 0.000001); + } } diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java index 691295118d5..f6413ab545f 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPathTest.java @@ -25,160 +25,160 @@ import static org.junit.Assert.assertTrue; public class GeoPathTest { - @Test - public void testPathDistance() { - // Start with a really simple case - GeoPath p; - GeoPoint gp; - p = new GeoPath(0.1); - p.addPoint(0.0,0.0); - p.addPoint(0.0,0.1); - p.addPoint(0.0,0.2); - p.done(); - gp = new GeoPoint(Math.PI * 0.5,0.15); - assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); - gp = new GeoPoint(0.05,0.15); - assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0,0.12); - assertEquals(0.12 + 0.0, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(-0.15,0.05); - assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0,0.25); - assertEquals(0.20 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0,-0.05); - assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001); + @Test + public void testPathDistance() { + // Start with a really simple case + GeoPath p; + GeoPoint gp; + p = new GeoPath(0.1); + p.addPoint(0.0, 0.0); + p.addPoint(0.0, 0.1); + p.addPoint(0.0, 0.2); + p.done(); + gp = new GeoPoint(Math.PI * 0.5, 0.15); + assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); + gp = new GeoPoint(0.05, 0.15); + assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001); + gp = new GeoPoint(0.0, 0.12); + assertEquals(0.12 + 0.0, p.computeArcDistance(gp), 0.000001); + gp = new GeoPoint(-0.15, 0.05); + assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.000001); + gp = new GeoPoint(0.0, 0.25); + assertEquals(0.20 + 0.05, p.computeArcDistance(gp), 0.000001); + gp = new GeoPoint(0.0, -0.05); + assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001); - // Compute path distances now - p = new GeoPath(0.1); - p.addPoint(0.0,0.0); - p.addPoint(0.0,0.1); - p.addPoint(0.0,0.2); - p.done(); - gp = new GeoPoint(0.05,0.15); - assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(0.0,0.12); - assertEquals(0.12, p.computeArcDistance(gp), 0.000001); + // Compute path distances now + p = new GeoPath(0.1); + p.addPoint(0.0, 0.0); + p.addPoint(0.0, 0.1); + p.addPoint(0.0, 0.2); + p.done(); + gp = new GeoPoint(0.05, 0.15); + assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001); + gp = new GeoPoint(0.0, 0.12); + assertEquals(0.12, p.computeArcDistance(gp), 0.000001); - // Now try a vertical path, and make sure distances are as expected - p = new GeoPath(0.1); - p.addPoint(-Math.PI * 0.25,-0.5); - p.addPoint(Math.PI * 0.25,-0.5); - p.done(); - gp = new GeoPoint(0.0,0.0); - assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); - gp = new GeoPoint(-0.1,-1.0); - assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); - gp = new GeoPoint(Math.PI*0.25+0.05,-0.5); - assertEquals(Math.PI * 0.5 + 0.05, p.computeArcDistance(gp), 0.000001); - gp = new GeoPoint(-Math.PI*0.25-0.05,-0.5); - assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001); - } + // Now try a vertical path, and make sure distances are as expected + p = new GeoPath(0.1); + p.addPoint(-Math.PI * 0.25, -0.5); + p.addPoint(Math.PI * 0.25, -0.5); + p.done(); + gp = new GeoPoint(0.0, 0.0); + assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); + gp = new GeoPoint(-0.1, -1.0); + assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0); + gp = new GeoPoint(Math.PI * 0.25 + 0.05, -0.5); + assertEquals(Math.PI * 0.5 + 0.05, p.computeArcDistance(gp), 0.000001); + gp = new GeoPoint(-Math.PI * 0.25 - 0.05, -0.5); + assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001); + } - @Test - public void testPathPointWithin() { - // Tests whether we can properly detect whether a point is within a path or not - GeoPath p; - GeoPoint gp; - p = new GeoPath(0.1); - // Build a diagonal path crossing the equator - p.addPoint(-0.2,-0.2); - p.addPoint(0.2,0.2); - p.done(); - // Test points on the path - gp = new GeoPoint(-0.2,-0.2); - assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.0,0.0); - assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.1,0.1); - assertTrue(p.isWithin(gp)); - // Test points off the path - gp = new GeoPoint(-0.2,0.2); - assertFalse(p.isWithin(gp)); - gp = new GeoPoint(-Math.PI*0.5,0.0); - assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.2,-0.2); - assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.0,Math.PI); - assertFalse(p.isWithin(gp)); - // Repeat the test, but across the terminator - p = new GeoPath(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); - // Test points on the path - gp = new GeoPoint(-0.2,Math.PI-0.2); - assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.0,Math.PI); - assertTrue(p.isWithin(gp)); - gp = new GeoPoint(0.1,-Math.PI+0.1); - assertTrue(p.isWithin(gp)); - // Test points off the path - gp = new GeoPoint(-0.2,-Math.PI+0.2); - assertFalse(p.isWithin(gp)); - gp = new GeoPoint(-Math.PI*0.5,0.0); - assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.2,Math.PI-0.2); - assertFalse(p.isWithin(gp)); - gp = new GeoPoint(0.0,0.0); - assertFalse(p.isWithin(gp)); + @Test + public void testPathPointWithin() { + // Tests whether we can properly detect whether a point is within a path or not + GeoPath p; + GeoPoint gp; + p = new GeoPath(0.1); + // Build a diagonal path crossing the equator + p.addPoint(-0.2, -0.2); + p.addPoint(0.2, 0.2); + p.done(); + // Test points on the path + gp = new GeoPoint(-0.2, -0.2); + assertTrue(p.isWithin(gp)); + gp = new GeoPoint(0.0, 0.0); + assertTrue(p.isWithin(gp)); + gp = new GeoPoint(0.1, 0.1); + assertTrue(p.isWithin(gp)); + // Test points off the path + gp = new GeoPoint(-0.2, 0.2); + assertFalse(p.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.5, 0.0); + assertFalse(p.isWithin(gp)); + gp = new GeoPoint(0.2, -0.2); + assertFalse(p.isWithin(gp)); + gp = new GeoPoint(0.0, Math.PI); + assertFalse(p.isWithin(gp)); + // Repeat the test, but across the terminator + p = new GeoPath(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); + // Test points on the path + gp = new GeoPoint(-0.2, Math.PI - 0.2); + assertTrue(p.isWithin(gp)); + gp = new GeoPoint(0.0, Math.PI); + assertTrue(p.isWithin(gp)); + gp = new GeoPoint(0.1, -Math.PI + 0.1); + assertTrue(p.isWithin(gp)); + // Test points off the path + gp = new GeoPoint(-0.2, -Math.PI + 0.2); + assertFalse(p.isWithin(gp)); + gp = new GeoPoint(-Math.PI * 0.5, 0.0); + assertFalse(p.isWithin(gp)); + gp = new GeoPoint(0.2, Math.PI - 0.2); + assertFalse(p.isWithin(gp)); + gp = new GeoPoint(0.0, 0.0); + assertFalse(p.isWithin(gp)); - } + } - @Test - public void testGetRelationship() { - GeoArea rect; - GeoPath p; + @Test + public void testGetRelationship() { + GeoArea rect; + GeoPath p; - // Start by testing the basic kinds of relationship, increasing in order of difficulty. + // Start by testing the basic kinds of relationship, increasing in order of difficulty. - p = new GeoPath(0.1); - p.addPoint(-0.3,-0.3); - p.addPoint(0.3,0.3); - p.done(); - // Easiest: The path is wholly contains the georect - rect = new GeoRectangle(0.05,-0.05,-0.05,0.05); - assertEquals(GeoArea.CONTAINS, rect.getRelationship(p)); - // Next easiest: Some endpoints of the rectangle are inside, and some are outside. - rect = new GeoRectangle(0.05,-0.05,-0.05,0.5); - assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); - // Now, all points are outside, but the figures intersect - rect = new GeoRectangle(0.05,-0.05,-0.5,0.5); - assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); - // Finally, all points are outside, and the figures *do not* intersect - rect = new GeoRectangle(0.5,-0.5,-0.5,0.5); - assertEquals(GeoArea.WITHIN, rect.getRelationship(p)); - // Check that segment edge overlap detection works - rect = new GeoRectangle(0.1,0.0,-0.1,0.0); - assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); - rect = new GeoRectangle(0.2,0.1,-0.2,-0.1); - assertEquals(GeoArea.DISJOINT, rect.getRelationship(p)); - // Check if overlap at endpoints behaves as expected next - rect = new GeoRectangle(0.5,-0.5,-0.5,-0.35); - assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); - rect = new GeoRectangle(0.5,-0.5,-0.5,-0.45); - assertEquals(GeoArea.DISJOINT, rect.getRelationship(p)); + p = new GeoPath(0.1); + p.addPoint(-0.3, -0.3); + p.addPoint(0.3, 0.3); + p.done(); + // Easiest: The path is wholly contains the georect + rect = new GeoRectangle(0.05, -0.05, -0.05, 0.05); + assertEquals(GeoArea.CONTAINS, rect.getRelationship(p)); + // Next easiest: Some endpoints of the rectangle are inside, and some are outside. + rect = new GeoRectangle(0.05, -0.05, -0.05, 0.5); + assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); + // Now, all points are outside, but the figures intersect + rect = new GeoRectangle(0.05, -0.05, -0.5, 0.5); + assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); + // Finally, all points are outside, and the figures *do not* intersect + rect = new GeoRectangle(0.5, -0.5, -0.5, 0.5); + assertEquals(GeoArea.WITHIN, rect.getRelationship(p)); + // Check that segment edge overlap detection works + rect = new GeoRectangle(0.1, 0.0, -0.1, 0.0); + assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); + rect = new GeoRectangle(0.2, 0.1, -0.2, -0.1); + assertEquals(GeoArea.DISJOINT, rect.getRelationship(p)); + // Check if overlap at endpoints behaves as expected next + rect = new GeoRectangle(0.5, -0.5, -0.5, -0.35); + assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p)); + rect = new GeoRectangle(0.5, -0.5, -0.5, -0.45); + assertEquals(GeoArea.DISJOINT, rect.getRelationship(p)); - } - - @Test - public void testPathBounds() { - GeoPath c; - Bounds b; - - c = new GeoPath(0.1); - c.addPoint(-0.3,-0.3); - c.addPoint(0.3,0.3); - c.done(); - - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-0.4046919,b.getLeftLongitude(),0.000001); - assertEquals(0.4046919,b.getRightLongitude(),0.000001); - assertEquals(-0.3999999,b.getMinLatitude(),0.000001); - assertEquals(0.3999999,b.getMaxLatitude(),0.000001); + } - } + @Test + public void testPathBounds() { + GeoPath c; + Bounds b; + + c = new GeoPath(0.1); + c.addPoint(-0.3, -0.3); + c.addPoint(0.3, 0.3); + c.done(); + + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-0.4046919, b.getLeftLongitude(), 0.000001); + assertEquals(0.4046919, b.getRightLongitude(), 0.000001); + assertEquals(-0.3999999, b.getMinLatitude(), 0.000001); + assertEquals(0.3999999, b.getMaxLatitude(), 0.000001); + + } } diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java index 621271a4f0d..87b26e8f0d5 100755 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/GeoPolygonTest.java @@ -17,65 +17,68 @@ package org.apache.lucene.spatial.spatial4j.geo3d; * limitations under the License. */ -import java.util.*; - -import static org.junit.Assert.*; +import java.util.ArrayList; +import java.util.List; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + public class GeoPolygonTest { - @Test - public void testPolygonPointWithin() { - GeoMembershipShape c; - GeoPoint gp; - List points; - - points = new ArrayList(); - points.add(new GeoPoint(-0.1,-0.5)); - points.add(new GeoPoint(0.0,-0.6)); - points.add(new GeoPoint(0.1,-0.5)); - points.add(new GeoPoint(0.0,-0.4)); - - c = GeoPolygonFactory.makeGeoPolygon(points,0); - // Sample some points within - gp = new GeoPoint(0.0,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.55); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.45); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(-0.05,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.05,-0.5); - assertTrue(c.isWithin(gp)); - // Sample some nearby points outside - gp = new GeoPoint(0.0,-0.65); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.35); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(-0.15,-0.5); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.15,-0.5); - assertFalse(c.isWithin(gp)); - // Random points outside - gp = new GeoPoint(0.0,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,Math.PI); - assertFalse(c.isWithin(gp)); - - points = new ArrayList(); - points.add(new GeoPoint(-0.1,-0.5)); - points.add(new GeoPoint(-0.01,-0.6)); - points.add(new GeoPoint(-0.1,-0.7)); - points.add(new GeoPoint(0.0,-0.8)); - points.add(new GeoPoint(0.1,-0.7)); - points.add(new GeoPoint(0.01,-0.6)); - points.add(new GeoPoint(0.1,-0.5)); - points.add(new GeoPoint(0.0,-0.4)); + @Test + public void testPolygonPointWithin() { + GeoMembershipShape c; + GeoPoint gp; + List points; + + points = new ArrayList(); + points.add(new GeoPoint(-0.1, -0.5)); + points.add(new GeoPoint(0.0, -0.6)); + points.add(new GeoPoint(0.1, -0.5)); + points.add(new GeoPoint(0.0, -0.4)); + + c = GeoPolygonFactory.makeGeoPolygon(points, 0); + // Sample some points within + gp = new GeoPoint(0.0, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.55); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.45); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(-0.05, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.05, -0.5); + assertTrue(c.isWithin(gp)); + // Sample some nearby points outside + gp = new GeoPoint(0.0, -0.65); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.35); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(-0.15, -0.5); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.15, -0.5); + assertFalse(c.isWithin(gp)); + // Random points outside + gp = new GeoPoint(0.0, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(Math.PI * 0.5, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, Math.PI); + assertFalse(c.isWithin(gp)); + + points = new ArrayList(); + points.add(new GeoPoint(-0.1, -0.5)); + points.add(new GeoPoint(-0.01, -0.6)); + points.add(new GeoPoint(-0.1, -0.7)); + points.add(new GeoPoint(0.0, -0.8)); + points.add(new GeoPoint(0.1, -0.7)); + points.add(new GeoPoint(0.01, -0.6)); + points.add(new GeoPoint(0.1, -0.5)); + points.add(new GeoPoint(0.0, -0.4)); /* System.out.println("Points: "); @@ -83,60 +86,60 @@ public class GeoPolygonTest { System.out.println(" "+p); } */ - - c = GeoPolygonFactory.makeGeoPolygon(points,0); - // Sample some points within - gp = new GeoPoint(0.0,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.55); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.45); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(-0.05,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.05,-0.5); - assertTrue(c.isWithin(gp)); - gp = new GeoPoint(0.0,-0.7); - assertTrue(c.isWithin(gp)); - // Sample some nearby points outside - gp = new GeoPoint(0.0,-0.35); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(-0.15,-0.5); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.15,-0.5); - assertFalse(c.isWithin(gp)); - // Random points outside - gp = new GeoPoint(0.0,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(Math.PI * 0.5,0.0); - assertFalse(c.isWithin(gp)); - gp = new GeoPoint(0.0,Math.PI); - assertFalse(c.isWithin(gp)); - } + c = GeoPolygonFactory.makeGeoPolygon(points, 0); + // Sample some points within + gp = new GeoPoint(0.0, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.55); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.45); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(-0.05, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.05, -0.5); + assertTrue(c.isWithin(gp)); + gp = new GeoPoint(0.0, -0.7); + assertTrue(c.isWithin(gp)); + // Sample some nearby points outside + gp = new GeoPoint(0.0, -0.35); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(-0.15, -0.5); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.15, -0.5); + assertFalse(c.isWithin(gp)); + // Random points outside + gp = new GeoPoint(0.0, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(Math.PI * 0.5, 0.0); + assertFalse(c.isWithin(gp)); + gp = new GeoPoint(0.0, Math.PI); + assertFalse(c.isWithin(gp)); - @Test - public void testPolygonBounds() { - GeoMembershipShape c; - Bounds b; - List points; - - points = new ArrayList(); - points.add(new GeoPoint(-0.1,-0.5)); - points.add(new GeoPoint(0.0,-0.6)); - points.add(new GeoPoint(0.1,-0.5)); - points.add(new GeoPoint(0.0,-0.4)); - - c = GeoPolygonFactory.makeGeoPolygon(points,0); + } - b = c.getBounds(null); - assertFalse(b.checkNoLongitudeBound()); - assertFalse(b.checkNoTopLatitudeBound()); - assertFalse(b.checkNoBottomLatitudeBound()); - assertEquals(-0.6,b.getLeftLongitude(),0.000001); - assertEquals(-0.4,b.getRightLongitude(),0.000001); - assertEquals(-0.1,b.getMinLatitude(),0.000001); - assertEquals(0.1,b.getMaxLatitude(),0.000001); - } + @Test + public void testPolygonBounds() { + GeoMembershipShape c; + Bounds b; + List points; + + points = new ArrayList(); + points.add(new GeoPoint(-0.1, -0.5)); + points.add(new GeoPoint(0.0, -0.6)); + points.add(new GeoPoint(0.1, -0.5)); + points.add(new GeoPoint(0.0, -0.4)); + + c = GeoPolygonFactory.makeGeoPolygon(points, 0); + + b = c.getBounds(null); + assertFalse(b.checkNoLongitudeBound()); + assertFalse(b.checkNoTopLatitudeBound()); + assertFalse(b.checkNoBottomLatitudeBound()); + assertEquals(-0.6, b.getLeftLongitude(), 0.000001); + assertEquals(-0.4, b.getRightLongitude(), 0.000001); + assertEquals(-0.1, b.getMinLatitude(), 0.000001); + assertEquals(0.1, b.getMaxLatitude(), 0.000001); + } } diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java index 89a70032ed7..5020f6e0efc 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/spatial4j/geo3d/PlaneTest.java @@ -22,43 +22,44 @@ import org.junit.Test; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -/** Test basic plane functionality. -*/ +/** + * Test basic plane functionality. + */ public class PlaneTest { - @Test - public void testIdenticalPlanes() { - final GeoPoint p = new GeoPoint(0.123,-0.456); - final Plane plane1 = new Plane(p,0.0); - final Plane plane2 = new Plane(p,0.0); - assertTrue(plane1.isNumericallyIdentical(plane2)); - final Plane plane3 = new Plane(p,0.1); - assertFalse(plane1.isNumericallyIdentical(plane3)); - final Vector v1 = new Vector(0.1,-0.732,0.9); - final double constant = 0.432; - final Vector v2 = new Vector(v1.x*constant,v1.y*constant,v1.z*constant); - final Plane p1 = new Plane(v1,0.2); - final Plane p2 = new Plane(v2,0.2*constant); - assertTrue(p1.isNumericallyIdentical(p2)); - } - - @Test - public void testInterpolation() { - // [X=0.35168818443386646, Y=-0.19637966197066342, Z=0.9152870857244183], - // [X=0.5003343189532654, Y=0.522128543226148, Z=0.6906861469771293], - - final GeoPoint start = new GeoPoint(0.35168818443386646,-0.19637966197066342,0.9152870857244183); - final GeoPoint end = new GeoPoint(0.5003343189532654,0.522128543226148,0.6906861469771293); - - // [A=-0.6135342247741855, B=0.21504338363863665, C=0.28188192383666794, D=0.0, side=-1.0] internal? false; - final Plane p = new Plane(-0.6135342247741855,0.21504338363863665,0.28188192383666794,0.0); - - final GeoPoint[] points = p.interpolate(start,end,new double[]{0.25,0.50,0.75}); - - for (GeoPoint point : points) { - assertTrue(p.evaluateIsZero(point)); - } + @Test + public void testIdenticalPlanes() { + final GeoPoint p = new GeoPoint(0.123, -0.456); + final Plane plane1 = new Plane(p, 0.0); + final Plane plane2 = new Plane(p, 0.0); + assertTrue(plane1.isNumericallyIdentical(plane2)); + final Plane plane3 = new Plane(p, 0.1); + assertFalse(plane1.isNumericallyIdentical(plane3)); + final Vector v1 = new Vector(0.1, -0.732, 0.9); + final double constant = 0.432; + final Vector v2 = new Vector(v1.x * constant, v1.y * constant, v1.z * constant); + final Plane p1 = new Plane(v1, 0.2); + final Plane p2 = new Plane(v2, 0.2 * constant); + assertTrue(p1.isNumericallyIdentical(p2)); + } + + @Test + public void testInterpolation() { + // [X=0.35168818443386646, Y=-0.19637966197066342, Z=0.9152870857244183], + // [X=0.5003343189532654, Y=0.522128543226148, Z=0.6906861469771293], + + final GeoPoint start = new GeoPoint(0.35168818443386646, -0.19637966197066342, 0.9152870857244183); + final GeoPoint end = new GeoPoint(0.5003343189532654, 0.522128543226148, 0.6906861469771293); + + // [A=-0.6135342247741855, B=0.21504338363863665, C=0.28188192383666794, D=0.0, side=-1.0] internal? false; + final Plane p = new Plane(-0.6135342247741855, 0.21504338363863665, 0.28188192383666794, 0.0); + + final GeoPoint[] points = p.interpolate(start, end, new double[]{0.25, 0.50, 0.75}); + + for (GeoPoint point : points) { + assertTrue(p.evaluateIsZero(point)); } + } }