mirror of
https://github.com/apache/lucene.git
synced 2025-02-09 03:25:15 +00:00
LUCENE-6196: Reformat code. Removed System.err & legacy comments in test. Fixed test compile warning.
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6196@1677595 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
1b901cab9a
commit
55d46dc6d3
@ -17,288 +17,288 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** An object for accumulating bounds information.
|
/**
|
||||||
* The bounds object is initially empty. Bounding points
|
* An object for accumulating bounds information.
|
||||||
* are then applied by supplying (x,y,z) tuples. It is also
|
* The bounds object is initially empty. Bounding points
|
||||||
* possible to indicate the following edge cases:
|
* are then applied by supplying (x,y,z) tuples. It is also
|
||||||
* (1) No longitude bound possible
|
* possible to indicate the following edge cases:
|
||||||
* (2) No upper latitude bound possible
|
* (1) No longitude bound possible
|
||||||
* (3) No lower latitude bound possible
|
* (2) No upper latitude bound possible
|
||||||
* When any of these have been applied, further application of
|
* (3) No lower latitude bound possible
|
||||||
* points cannot override that decision.
|
* When any of these have been applied, further application of
|
||||||
|
* points cannot override that decision.
|
||||||
*/
|
*/
|
||||||
public class Bounds
|
public class Bounds {
|
||||||
{
|
protected boolean noLongitudeBound = false;
|
||||||
protected boolean noLongitudeBound = false;
|
protected boolean noTopLatitudeBound = false;
|
||||||
protected boolean noTopLatitudeBound = false;
|
protected boolean noBottomLatitudeBound = false;
|
||||||
protected boolean noBottomLatitudeBound = false;
|
|
||||||
|
|
||||||
protected Double minLatitude = null;
|
protected Double minLatitude = null;
|
||||||
protected Double maxLatitude = null;
|
protected Double maxLatitude = null;
|
||||||
|
|
||||||
// For longitude bounds, this class needs to worry about keeping track of the distinction
|
// 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
|
// 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
|
// 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
|
// to determine which point represents a left bound, and which point represents a right
|
||||||
// bound.
|
// bound.
|
||||||
//
|
//
|
||||||
// The next problem is how to compare two of the same kind of bound, e.g. two left bounds.
|
// 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,
|
// 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
|
// this is arbitrary. What we could try to do instead would be to find a pair of (left,right) bounds such
|
||||||
// that:
|
// that:
|
||||||
// (1) all other bounds are within, and
|
// (1) all other bounds are within, and
|
||||||
// (2) the left minus right distance is minimized
|
// (2) the left minus right distance is minimized
|
||||||
// Unfortunately, there are still shapes that cannot be summarized in this way correctly.
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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
|
// 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
|
// choice of longitude bounds. But even with this, we still can't always choose whether the actual shape
|
||||||
// goes right or left.
|
// goes right or left.
|
||||||
//
|
//
|
||||||
// We can make the specification truly general by submitting the following in order:
|
// We can make the specification truly general by submitting the following in order:
|
||||||
// addSide(PlaneSide side, Membership... constraints)
|
// addSide(PlaneSide side, Membership... constraints)
|
||||||
// ...
|
// ...
|
||||||
// This is unambiguous, but I still can't see yet how this would help compute the bounds. The plane
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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.
|
// 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
|
// 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.
|
// 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.
|
// 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
|
// 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,
|
// 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".)
|
// and can recognize that, we can set "unconstrained in longitude".)
|
||||||
|
|
||||||
protected Double leftLongitude = null;
|
|
||||||
protected Double rightLongitude = null;
|
|
||||||
|
|
||||||
public Bounds() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double getMaxLatitude() {
|
protected Double leftLongitude = null;
|
||||||
return maxLatitude;
|
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);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
public Double getMinLatitude() {
|
}
|
||||||
return minLatitude;
|
|
||||||
|
public Bounds addLatitudeZone(double latitude) {
|
||||||
|
if (!noTopLatitudeBound || !noBottomLatitudeBound) {
|
||||||
|
addLatitudeBound(latitude);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
public Double getLeftLongitude() {
|
}
|
||||||
return leftLongitude;
|
|
||||||
|
public Bounds addLongitudeSlice(double newLeftLongitude, double newRightLongitude) {
|
||||||
|
if (!noLongitudeBound) {
|
||||||
|
addLongitudeBound(newLeftLongitude, newRightLongitude);
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
public Double getRightLongitude() {
|
}
|
||||||
return rightLongitude;
|
|
||||||
}
|
protected void addLatitudeBound(double latitude) {
|
||||||
|
if (!noTopLatitudeBound && (maxLatitude == null || latitude > maxLatitude))
|
||||||
public boolean checkNoLongitudeBound() {
|
maxLatitude = latitude;
|
||||||
return noLongitudeBound;
|
if (!noBottomLatitudeBound && (minLatitude == null || latitude < minLatitude))
|
||||||
}
|
minLatitude = latitude;
|
||||||
|
}
|
||||||
public boolean checkNoTopLatitudeBound() {
|
|
||||||
return noTopLatitudeBound;
|
protected void addLongitudeBound(double newLeftLongitude, double newRightLongitude) {
|
||||||
}
|
if (leftLongitude == null && rightLongitude == null) {
|
||||||
|
leftLongitude = newLeftLongitude;
|
||||||
public boolean checkNoBottomLatitudeBound() {
|
rightLongitude = newRightLongitude;
|
||||||
return noBottomLatitudeBound;
|
} else {
|
||||||
}
|
// Map the current range to something monotonically increasing
|
||||||
|
double currentLeftLongitude = leftLongitude;
|
||||||
public Bounds addHorizontalCircle(double z) {
|
double currentRightLongitude = rightLongitude;
|
||||||
if (!noTopLatitudeBound || !noBottomLatitudeBound) {
|
if (currentRightLongitude < currentLeftLongitude)
|
||||||
// Compute a latitude value
|
currentRightLongitude += 2.0 * Math.PI;
|
||||||
double latitude = Math.asin(z);
|
double adjustedLeftLongitude = newLeftLongitude;
|
||||||
addLatitudeBound(latitude);
|
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;
|
if (currentRightLongitude < adjustedRightLongitude) {
|
||||||
}
|
// New right longitude needed
|
||||||
|
rightLongitude = newRightLongitude;
|
||||||
public Bounds addLatitudeZone(double latitude) {
|
|
||||||
if (!noTopLatitudeBound || !noBottomLatitudeBound) {
|
|
||||||
addLatitudeBound(latitude);
|
|
||||||
}
|
}
|
||||||
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) {
|
protected void addLongitudeBound(double longitude) {
|
||||||
if (!noLongitudeBound) {
|
// If this point is within the current bounds, we're done; otherwise
|
||||||
addLongitudeBound(newLeftLongitude,newRightLongitude);
|
// expand one side or the other.
|
||||||
}
|
if (leftLongitude == null && rightLongitude == null) {
|
||||||
return this;
|
leftLongitude = longitude;
|
||||||
}
|
rightLongitude = longitude;
|
||||||
|
} else {
|
||||||
protected void addLatitudeBound(double latitude) {
|
// Compute whether we're to the right of the left value. But the left value may be greater than
|
||||||
if (!noTopLatitudeBound && (maxLatitude == null || latitude > maxLatitude))
|
// the right value.
|
||||||
maxLatitude = latitude;
|
double currentLeftLongitude = leftLongitude;
|
||||||
if (!noBottomLatitudeBound && (minLatitude == null || latitude < minLatitude))
|
double currentRightLongitude = rightLongitude;
|
||||||
minLatitude = latitude;
|
if (currentRightLongitude < currentLeftLongitude)
|
||||||
}
|
currentRightLongitude += 2.0 * Math.PI;
|
||||||
|
// We have a range to look at that's going in the right way.
|
||||||
protected void addLongitudeBound(double newLeftLongitude, double newRightLongitude) {
|
// Now, do the same trick with the computed longitude.
|
||||||
if (leftLongitude == null && rightLongitude == null) {
|
if (longitude < currentLeftLongitude)
|
||||||
leftLongitude = newLeftLongitude;
|
longitude += 2.0 * Math.PI;
|
||||||
rightLongitude = newRightLongitude;
|
|
||||||
|
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 {
|
} else {
|
||||||
// Map the current range to something monotonically increasing
|
leftExtensionAmt = currentLeftLongitude + 2.0 * Math.PI - longitude;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
double testRightLongitude = rightLongitude;
|
if (longitude > currentRightLongitude) {
|
||||||
if (testRightLongitude < leftLongitude)
|
rightExtensionAmt = longitude - currentRightLongitude;
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
// Compute whether we're to the right of the left value. But the left value may be greater than
|
rightExtensionAmt = longitude + 2.0 * Math.PI - currentRightLongitude;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
double testRightLongitude = rightLongitude;
|
if (leftExtensionAmt < rightExtensionAmt) {
|
||||||
if (testRightLongitude < leftLongitude)
|
currentLeftLongitude = leftLongitude - leftExtensionAmt;
|
||||||
testRightLongitude += Math.PI * 2.0;
|
while (currentLeftLongitude <= -Math.PI) {
|
||||||
if (testRightLongitude - leftLongitude >= Math.PI) {
|
currentLeftLongitude += 2.0 * Math.PI;
|
||||||
noLongitudeBound = true;
|
}
|
||||||
leftLongitude = null;
|
leftLongitude = currentLeftLongitude;
|
||||||
rightLongitude = null;
|
} else {
|
||||||
|
currentRightLongitude = rightLongitude + rightExtensionAmt;
|
||||||
|
while (currentRightLongitude > Math.PI) {
|
||||||
|
currentRightLongitude -= 2.0 * Math.PI;
|
||||||
|
}
|
||||||
|
rightLongitude = currentRightLongitude;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
double testRightLongitude = rightLongitude;
|
||||||
public Bounds addPoint(Vector v) {
|
if (testRightLongitude < leftLongitude)
|
||||||
return addPoint(v.x, v.y, v.z);
|
testRightLongitude += Math.PI * 2.0;
|
||||||
}
|
if (testRightLongitude - leftLongitude >= Math.PI) {
|
||||||
|
noLongitudeBound = true;
|
||||||
public Bounds addPoint(double x, double y, double z) {
|
leftLongitude = null;
|
||||||
if (!noLongitudeBound) {
|
rightLongitude = null;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Bounds addPoint(double latitude, double longitude) {
|
public Bounds addPoint(Vector v) {
|
||||||
if (!noLongitudeBound) {
|
return addPoint(v.x, v.y, v.z);
|
||||||
// Get a longitude value
|
}
|
||||||
addLongitudeBound(longitude);
|
|
||||||
}
|
public Bounds addPoint(double x, double y, double z) {
|
||||||
if (!noTopLatitudeBound || !noBottomLatitudeBound) {
|
if (!noLongitudeBound) {
|
||||||
// Compute a latitude value
|
// Get a longitude value
|
||||||
addLatitudeBound(latitude);
|
double longitude = Math.atan2(y, x);
|
||||||
}
|
//System.err.println(" add longitude bound at "+longitude * 180.0/Math.PI);
|
||||||
return this;
|
addLongitudeBound(longitude);
|
||||||
}
|
}
|
||||||
|
if (!noTopLatitudeBound || !noBottomLatitudeBound) {
|
||||||
public Bounds noLongitudeBound() {
|
// Compute a latitude value
|
||||||
noLongitudeBound = true;
|
double latitude = Math.asin(z);
|
||||||
leftLongitude = null;
|
addLatitudeBound(latitude);
|
||||||
rightLongitude = null;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
return this;
|
||||||
public Bounds noTopLatitudeBound() {
|
}
|
||||||
noTopLatitudeBound = true;
|
|
||||||
maxLatitude = null;
|
public Bounds addPoint(double latitude, double longitude) {
|
||||||
return this;
|
if (!noLongitudeBound) {
|
||||||
|
// Get a longitude value
|
||||||
|
addLongitudeBound(longitude);
|
||||||
}
|
}
|
||||||
|
if (!noTopLatitudeBound || !noBottomLatitudeBound) {
|
||||||
public Bounds noBottomLatitudeBound() {
|
// Compute a latitude value
|
||||||
noBottomLatitudeBound = true;
|
addLatitudeBound(latitude);
|
||||||
minLatitude = null;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,33 +17,37 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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
|
* A GeoArea represents a standard 2-D breakdown of a part of sphere. It can
|
||||||
* bounded at all. The purpose of the interface is to describe bounding shapes used for
|
* be bounded in latitude, or bounded in both latitude and longitude, or not
|
||||||
* computation of geo hashes. */
|
* bounded at all. The purpose of the interface is to describe bounding shapes used for
|
||||||
|
* computation of geo hashes.
|
||||||
|
*/
|
||||||
public interface GeoArea extends Membership {
|
public interface GeoArea extends Membership {
|
||||||
// Since we don't know what each GeoArea's constraints are,
|
// Since we don't know what each GeoArea's constraints are,
|
||||||
// we put the onus on the GeoArea implementation to do the right thing.
|
// we put the onus on the GeoArea implementation to do the right thing.
|
||||||
// This will, of course, rely heavily on methods provided by
|
// This will, of course, rely heavily on methods provided by
|
||||||
// the underlying GeoShape class.
|
// 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.
|
public static final int CONTAINS = 0;
|
||||||
* Note: return value is how the GeoShape relates to the GeoArea, not the
|
public static final int WITHIN = 1;
|
||||||
* other way around. For example, if this GeoArea is entirely within the
|
public static final int OVERLAPS = 2;
|
||||||
* shape, then CONTAINS should be returned. If the shape is entirely enclosed
|
public static final int DISJOINT = 3;
|
||||||
* 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
|
* Find the spatial relationship between a shape and the current geo area.
|
||||||
* OVERLAPS and CONTAINS. In that case, OVERLAPS may be returned even
|
* Note: return value is how the GeoShape relates to the GeoArea, not the
|
||||||
* though the proper result would in fact be CONTAINS. Code accordingly.
|
* other way around. For example, if this GeoArea is entirely within the
|
||||||
*@param shape is the shape to consider.
|
* shape, then CONTAINS should be returned. If the shape is entirely enclosed
|
||||||
*@return the relationship, from the perspective of the shape.
|
* by this GeoArea, then WITHIN should be returned.
|
||||||
*/
|
* Note well: When a shape consists of multiple independent overlapping subshapes,
|
||||||
public int getRelationship(GeoShape shape);
|
* 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,20 +17,21 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class GeoAreaFactory
|
public class GeoAreaFactory {
|
||||||
{
|
private GeoAreaFactory() {
|
||||||
private GeoAreaFactory() {
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
/** Create a GeoArea of the right kind given the specified bounds.
|
* Create a GeoArea of the right kind given the specified bounds.
|
||||||
*@param topLat is the top latitude
|
*
|
||||||
*@param bottomLat is the bottom latitude
|
* @param topLat is the top latitude
|
||||||
*@param leftLon is the left longitude
|
* @param bottomLat is the bottom latitude
|
||||||
*@param rightLon is the right longitude
|
* @param leftLon is the left longitude
|
||||||
*@return a GeoArea corresponding to what was specified.
|
* @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 static GeoArea makeGeoArea(double topLat, double bottomLat, double leftLon, double rightLon) {
|
||||||
}
|
return GeoBBoxFactory.makeGeoBBox(topLat, bottomLat, leftLon, rightLon);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,16 +17,19 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** All bounding box shapes have this interface in common.
|
/**
|
||||||
* This describes methods that bounding boxes have above and beyond
|
* All bounding box shapes have this interface in common.
|
||||||
* GeoMembershipShape's.
|
* This describes methods that bounding boxes have above and beyond
|
||||||
*/
|
* GeoMembershipShape's.
|
||||||
|
*/
|
||||||
public interface GeoBBox extends GeoMembershipShape, GeoSizeable, GeoArea {
|
public interface GeoBBox extends GeoMembershipShape, GeoSizeable, GeoArea {
|
||||||
|
|
||||||
/** Expand box by specified angle.
|
/**
|
||||||
*@param angle is the angle amount to expand the GeoBBox by.
|
* Expand box by specified angle.
|
||||||
*@return a new GeoBBox.
|
*
|
||||||
*/
|
* @param angle is the angle amount to expand the GeoBBox by.
|
||||||
public GeoBBox expand(double angle);
|
* @return a new GeoBBox.
|
||||||
|
*/
|
||||||
|
public GeoBBox expand(double angle);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,39 +17,40 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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 {
|
public abstract class GeoBBoxBase implements GeoBBox {
|
||||||
|
|
||||||
protected final static GeoPoint NORTH_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);
|
protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0, 0.0, -1.0);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract boolean isWithin(final Vector point);
|
public abstract boolean isWithin(final Vector point);
|
||||||
|
|
||||||
protected final static int ALL_INSIDE = 0;
|
protected final static int ALL_INSIDE = 0;
|
||||||
protected final static int SOME_INSIDE = 1;
|
protected final static int SOME_INSIDE = 1;
|
||||||
protected final static int NONE_INSIDE = 2;
|
protected final static int NONE_INSIDE = 2;
|
||||||
|
|
||||||
protected int isShapeInsideBBox(final GeoShape path) {
|
protected int isShapeInsideBBox(final GeoShape path) {
|
||||||
final GeoPoint[] pathPoints = path.getEdgePoints();
|
final GeoPoint[] pathPoints = path.getEdgePoints();
|
||||||
boolean foundOutside = false;
|
boolean foundOutside = false;
|
||||||
boolean foundInside = false;
|
boolean foundInside = false;
|
||||||
for (GeoPoint p : pathPoints) {
|
for (GeoPoint p : pathPoints) {
|
||||||
if (isWithin(p)) {
|
if (isWithin(p)) {
|
||||||
foundInside = true;
|
foundInside = true;
|
||||||
} else {
|
} else {
|
||||||
foundOutside = true;
|
foundOutside = true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!foundInside && !foundOutside)
|
|
||||||
return NONE_INSIDE;
|
|
||||||
if (foundInside && !foundOutside)
|
|
||||||
return ALL_INSIDE;
|
|
||||||
if (foundOutside && !foundInside)
|
|
||||||
return NONE_INSIDE;
|
|
||||||
return SOME_INSIDE;
|
|
||||||
}
|
}
|
||||||
|
if (!foundInside && !foundOutside)
|
||||||
|
return NONE_INSIDE;
|
||||||
|
if (foundInside && !foundOutside)
|
||||||
|
return ALL_INSIDE;
|
||||||
|
if (foundOutside && !foundInside)
|
||||||
|
return NONE_INSIDE;
|
||||||
|
return SOME_INSIDE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,86 +17,87 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class GeoBBoxFactory
|
public class GeoBBoxFactory {
|
||||||
{
|
private 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);
|
|
||||||
|
|
||||||
if (extent >= Math.PI)
|
/**
|
||||||
return new GeoWideLongitudeSlice(leftLon, rightLon);
|
* Create a geobbox of the right kind given the specified bounds.
|
||||||
|
*
|
||||||
return new GeoLongitudeSlice(leftLon, rightLon);
|
* @param topLat is the top latitude
|
||||||
}
|
* @param bottomLat is the bottom latitude
|
||||||
//System.err.println(" not longitude slice");
|
* @param leftLon is the left longitude
|
||||||
if (leftLon == rightLon) {
|
* @param rightLon is the right longitude
|
||||||
if (topLat == bottomLat)
|
* @return a GeoBBox corresponding to what was specified.
|
||||||
return new GeoDegeneratePoint(topLat, leftLon);
|
*/
|
||||||
return new GeoDegenerateVerticalLine(topLat, bottomLat, leftLon);
|
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);
|
||||||
//System.err.println(" not vertical line");
|
if (topLat > Math.PI * 0.5)
|
||||||
if (extent >= Math.PI) {
|
topLat = Math.PI * 0.5;
|
||||||
if (topLat == bottomLat) {
|
if (bottomLat < -Math.PI * 0.5)
|
||||||
//System.err.println(" wide degenerate line");
|
bottomLat = -Math.PI * 0.5;
|
||||||
return new GeoWideDegenerateHorizontalLine(topLat, leftLon, rightLon);
|
if (leftLon < -Math.PI)
|
||||||
}
|
leftLon = -Math.PI;
|
||||||
if (topLat == Math.PI * 0.5) {
|
if (rightLon > Math.PI)
|
||||||
return new GeoWideNorthRectangle(bottomLat, leftLon, rightLon);
|
rightLon = Math.PI;
|
||||||
} else if (bottomLat == -Math.PI * 0.5) {
|
if (leftLon == -Math.PI && rightLon == Math.PI) {
|
||||||
return new GeoWideSouthRectangle(topLat, leftLon, rightLon);
|
if (topLat == Math.PI * 0.5 && bottomLat == -Math.PI * 0.5)
|
||||||
}
|
return new GeoWorld();
|
||||||
//System.err.println(" wide rect");
|
if (topLat == bottomLat) {
|
||||||
return new GeoWideRectangle(topLat, bottomLat, leftLon, rightLon);
|
if (topLat == Math.PI * 0.5 || topLat == -Math.PI * 0.5)
|
||||||
}
|
return new GeoDegeneratePoint(topLat, 0.0);
|
||||||
if (topLat == bottomLat) {
|
return new GeoDegenerateLatitudeZone(topLat);
|
||||||
//System.err.println(" horizontal line");
|
}
|
||||||
return new GeoDegenerateHorizontalLine(topLat, leftLon, rightLon);
|
if (topLat == Math.PI * 0.5)
|
||||||
}
|
return new GeoNorthLatitudeZone(bottomLat);
|
||||||
if (topLat == Math.PI * 0.5) {
|
else if (bottomLat == -Math.PI * 0.5)
|
||||||
return new GeoNorthRectangle(bottomLat, leftLon, rightLon);
|
return new GeoSouthLatitudeZone(topLat);
|
||||||
} else if (bottomLat == -Math.PI * 0.5) {
|
return new GeoLatitudeZone(topLat, bottomLat);
|
||||||
return new GeoSouthRectangle(topLat, leftLon, rightLon);
|
|
||||||
}
|
|
||||||
//System.err.println(" rectangle");
|
|
||||||
return new GeoRectangle(topLat, bottomLat, leftLon, rightLon);
|
|
||||||
}
|
}
|
||||||
|
//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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,68 +17,76 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Base extended shape object.
|
/**
|
||||||
*/
|
* Base extended shape object.
|
||||||
public abstract class GeoBaseExtendedShape implements GeoShape
|
*/
|
||||||
{
|
public abstract class GeoBaseExtendedShape implements GeoShape {
|
||||||
protected final static GeoPoint NORTH_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);
|
protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0, 0.0, -1.0);
|
||||||
|
|
||||||
public GeoBaseExtendedShape()
|
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();
|
||||||
}
|
}
|
||||||
|
if (isWithin(SOUTH_POLE)) {
|
||||||
/** Check if a point is within this shape.
|
bounds.noBottomLatitudeBound().noLongitudeBound();
|
||||||
*@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;
|
|
||||||
}
|
}
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,244 +17,241 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Circular area with a center and radius.
|
/**
|
||||||
*/
|
* Circular area with a center and radius.
|
||||||
public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, GeoSizeable
|
*/
|
||||||
{
|
public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape, GeoSizeable {
|
||||||
public final GeoPoint center;
|
public final GeoPoint center;
|
||||||
public final double cutoffAngle;
|
public final double cutoffAngle;
|
||||||
public final double cutoffNormalDistance;
|
public final double cutoffNormalDistance;
|
||||||
public final double cutoffLinearDistance;
|
public final double cutoffLinearDistance;
|
||||||
public final SidedPlane circlePlane;
|
public final SidedPlane circlePlane;
|
||||||
public final GeoPoint[] edgePoints;
|
public final GeoPoint[] edgePoints;
|
||||||
public static final GeoPoint[] circlePoints = new GeoPoint[0];
|
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
public GeoCircle(final double lat, final double lon, final double cutoffAngle) {
|
||||||
*@return the center.
|
super();
|
||||||
*/
|
if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5)
|
||||||
@Override
|
throw new IllegalArgumentException("Latitude out of bounds");
|
||||||
public GeoPoint getCenter() {
|
if (lon < -Math.PI || lon > Math.PI)
|
||||||
return center;
|
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.
|
// Compute a point on the circle boundary.
|
||||||
* A return value of Double.MAX_VALUE should be returned for
|
if (cutoffAngle == Math.PI)
|
||||||
* points outside of the shape.
|
this.edgePoints = new GeoPoint[0];
|
||||||
*/
|
else {
|
||||||
@Override
|
// Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also.
|
||||||
public double computeNormalDistance(final GeoPoint point)
|
double newLat = lat + cutoffAngle;
|
||||||
{
|
double newLon = lon;
|
||||||
double normalDistance = this.center.normalDistance(point);
|
if (newLat > Math.PI * 0.5) {
|
||||||
if (normalDistance > cutoffNormalDistance)
|
newLat = Math.PI - newLat;
|
||||||
return Double.MAX_VALUE;
|
newLon += Math.PI;
|
||||||
return normalDistance;
|
}
|
||||||
|
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.
|
@Override
|
||||||
* A return value of Double.MAX_VALUE should be returned for
|
public double getRadius() {
|
||||||
* points outside of the shape.
|
return cutoffAngle;
|
||||||
*/
|
}
|
||||||
@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 boolean isWithin(final Vector point)
|
* Returns the center of a circle into which the area will be inscribed.
|
||||||
{
|
*
|
||||||
// Fastest way of determining membership
|
* @return the center.
|
||||||
return circlePlane.isWithin(point);
|
*/
|
||||||
}
|
@Override
|
||||||
|
public GeoPoint getCenter() {
|
||||||
|
return center;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
* Compute an estimate of "distance" to the GeoPoint.
|
||||||
{
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
// Fastest way of determining membership
|
* points outside of the shape.
|
||||||
return circlePlane.isWithin(x,y,z);
|
*/
|
||||||
}
|
@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()
|
* Compute an estimate of "distance" to the GeoPoint.
|
||||||
{
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
return edgePoints;
|
* points outside of the shape.
|
||||||
}
|
*/
|
||||||
|
@Override
|
||||||
@Override
|
public double computeNormalDistance(final double x, final double y, final double z) {
|
||||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
double normalDistance = this.center.normalDistance(x, y, z);
|
||||||
{
|
if (normalDistance > cutoffNormalDistance)
|
||||||
return circlePlane.intersects(p, notablePoints, circlePoints, bounds);
|
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,
|
* Compute a squared estimate of the "distance" to the
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
* GeoPoint. Double.MAX_VALUE indicates a point outside of the
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
* shape.
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
*/
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
@Override
|
||||||
*/
|
public double computeSquaredNormalDistance(final GeoPoint point) {
|
||||||
@Override
|
double normalDistanceSquared = this.center.normalDistanceSquared(point);
|
||||||
public Bounds getBounds(Bounds bounds)
|
if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance)
|
||||||
{
|
return Double.MAX_VALUE;
|
||||||
bounds = super.getBounds(bounds);
|
return normalDistanceSquared;
|
||||||
bounds.addPoint(center);
|
}
|
||||||
circlePlane.recordBounds(bounds);
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean equals(Object o)
|
* Compute a squared estimate of the "distance" to the
|
||||||
{
|
* GeoPoint. Double.MAX_VALUE indicates a point outside of the
|
||||||
if (!(o instanceof GeoCircle))
|
* shape.
|
||||||
return false;
|
*/
|
||||||
GeoCircle other = (GeoCircle)o;
|
@Override
|
||||||
return other.center.equals(center) && other.cutoffAngle == cutoffAngle;
|
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() {
|
* Compute a linear distance to the vector.
|
||||||
int result;
|
* return Double.MAX_VALUE for points outside the shape.
|
||||||
long temp;
|
*/
|
||||||
result = center.hashCode();
|
@Override
|
||||||
temp = Double.doubleToLongBits(cutoffAngle);
|
public double computeLinearDistance(final GeoPoint point) {
|
||||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
double linearDistance = this.center.linearDistance(point);
|
||||||
return result;
|
if (linearDistance > cutoffLinearDistance)
|
||||||
}
|
return Double.MAX_VALUE;
|
||||||
|
return linearDistance;
|
||||||
@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 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 + ")}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,103 +20,99 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** GeoComposite is a set of GeoMembershipShape's, treated as a unit.
|
/**
|
||||||
*/
|
* GeoComposite is a set of GeoMembershipShape's, treated as a unit.
|
||||||
public class GeoCompositeMembershipShape implements GeoMembershipShape
|
*/
|
||||||
{
|
public class GeoCompositeMembershipShape implements GeoMembershipShape {
|
||||||
protected final List<GeoMembershipShape> shapes = new ArrayList<GeoMembershipShape>();
|
protected final List<GeoMembershipShape> shapes = new ArrayList<GeoMembershipShape>();
|
||||||
|
|
||||||
public GeoCompositeMembershipShape()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Add a shape to the composite.
|
|
||||||
*/
|
|
||||||
public void addShape(final GeoMembershipShape shape) {
|
|
||||||
shapes.add(shape);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public GeoCompositeMembershipShape() {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
* Add a shape to the composite.
|
||||||
{
|
*/
|
||||||
for (GeoMembershipShape shape : shapes) {
|
public void addShape(final GeoMembershipShape shape) {
|
||||||
if (shape.isWithin(x,y,z))
|
shapes.add(shape);
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeoPoint[] getEdgePoints()
|
public boolean isWithin(final Vector point) {
|
||||||
{
|
//System.err.println("Checking whether point "+point+" is within Composite");
|
||||||
return shapes.get(0).getEdgePoints();
|
for (GeoMembershipShape shape : shapes) {
|
||||||
}
|
if (shape.isWithin(point)) {
|
||||||
|
//System.err.println(" Point is within "+shape);
|
||||||
@Override
|
return true;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
for (GeoMembershipShape shape : shapes) {
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
if (shape.isWithin(x, y, z))
|
||||||
* 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public GeoPoint[] getEdgePoints() {
|
||||||
return shapes.hashCode();//TODO cache
|
return shapes.get(0).getEdgePoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||||
return "GeoCompositeMembershipShape: {" + shapes + '}';
|
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 + '}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,250 +21,251 @@ import java.util.ArrayList;
|
|||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.List;
|
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
|
* GeoConvexPolygon objects are generic building blocks of more complex structures.
|
||||||
* a maximum extent no larger than PI. Violating either one of these limits will
|
* The only restrictions on these objects are: (1) they must be convex; (2) they must have
|
||||||
* cause the logic to fail.
|
* 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
|
*/
|
||||||
{
|
public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembershipShape {
|
||||||
protected final List<GeoPoint> points;
|
protected final List<GeoPoint> points;
|
||||||
protected final BitSet isInternalEdges;
|
protected final BitSet isInternalEdges;
|
||||||
|
|
||||||
protected SidedPlane[] edges = null;
|
protected SidedPlane[] edges = null;
|
||||||
protected boolean[] internalEdges = null;
|
protected boolean[] internalEdges = null;
|
||||||
protected GeoPoint[][] notableEdgePoints = 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<GeoPoint> pointList) {
|
|
||||||
this.points = pointList;
|
|
||||||
this.isInternalEdges = null;
|
|
||||||
donePoints(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Create a convex polygon from a list of points, keeping track of which boundaries
|
protected GeoPoint[] edgePoints = null;
|
||||||
* are internal. This is used when creating a polygon as a building block for another shape.
|
|
||||||
*/
|
|
||||||
public GeoConvexPolygon(final List<GeoPoint> 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<GeoPoint>();
|
|
||||||
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.
|
protected double fullDistance = 0.0;
|
||||||
*/
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
* Create a convex polygon from a list of points. The first point must be on the
|
||||||
{
|
* external edge.
|
||||||
for (final SidedPlane edge : edges) {
|
*/
|
||||||
if (!edge.isWithin(x,y,z))
|
public GeoConvexPolygon(final List<GeoPoint> pointList) {
|
||||||
return false;
|
this.points = pointList;
|
||||||
}
|
this.isInternalEdges = null;
|
||||||
return true;
|
donePoints(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public GeoPoint[] getEdgePoints()
|
* 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.
|
||||||
return edgePoints;
|
*/
|
||||||
|
public GeoConvexPolygon(final List<GeoPoint> 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<GeoPoint>();
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
createCenterPoint();
|
||||||
@Override
|
}
|
||||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
|
||||||
{
|
protected void createCenterPoint() {
|
||||||
//System.err.println("Checking for polygon intersection with plane "+p+"...");
|
// In order to naively confirm that the polygon is convex, I would need to
|
||||||
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
// check every edge, and verify that every point (other than the edge endpoints)
|
||||||
final SidedPlane edge = edges[edgeIndex];
|
// is within the edge's sided plane. This is an order n^2 operation. That's still
|
||||||
final GeoPoint[] points = this.notableEdgePoints[edgeIndex];
|
// not wrong, though, because everything else about polygons has a similar cost.
|
||||||
if (!internalEdges[edgeIndex]) {
|
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
||||||
//System.err.println(" non-internal edge "+edge);
|
final SidedPlane edge = edges[edgeIndex];
|
||||||
// Edges flagged as 'internal only' are excluded from the matching
|
for (int pointIndex = 0; pointIndex < points.size(); pointIndex++) {
|
||||||
// Construct boundaries
|
if (pointIndex != edgeIndex && pointIndex != legalIndex(edgeIndex + 1)) {
|
||||||
final Membership[] membershipBounds = new Membership[edges.length-1];
|
if (!edge.isWithin(points.get(pointIndex)))
|
||||||
int count = 0;
|
throw new IllegalArgumentException("Polygon is not convex: Point " + points.get(pointIndex) + " Edge " + edge);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
//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 false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
for (final SidedPlane edge : edges) {
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
if (!edge.isWithin(x, y, z))
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
return false;
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
}
|
||||||
*/
|
return true;
|
||||||
@Override
|
}
|
||||||
public Bounds getBounds(Bounds bounds)
|
|
||||||
{
|
|
||||||
bounds = super.getBounds(bounds);
|
|
||||||
|
|
||||||
// Add all the points
|
@Override
|
||||||
for (final GeoPoint point : points) {
|
public GeoPoint[] getEdgePoints() {
|
||||||
bounds.addPoint(point);
|
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];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (edge.intersects(p, notablePoints, points, bounds, membershipBounds)) {
|
||||||
// Add planes with membership.
|
//System.err.println(" intersects!");
|
||||||
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
return true;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//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.
|
* Compute longitude/latitude bounds for the shape.
|
||||||
bounds.noLongitudeBound();
|
*
|
||||||
}
|
* @param bounds is the optional input bounds object. If this is null,
|
||||||
return bounds;
|
* 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
|
// Add planes with membership.
|
||||||
public boolean equals(Object o)
|
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
||||||
{
|
final SidedPlane edge = edges[edgeIndex];
|
||||||
if (!(o instanceof GeoConvexPolygon))
|
// Construct boundaries
|
||||||
return false;
|
final Membership[] membershipBounds = new Membership[edges.length - 1];
|
||||||
GeoConvexPolygon other = (GeoConvexPolygon)o;
|
int count = 0;
|
||||||
if (other.points.size() != points.size())
|
for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) {
|
||||||
return false;
|
if (otherIndex != edgeIndex) {
|
||||||
|
membershipBounds[count++] = edges[otherIndex];
|
||||||
for (int i = 0; i < points.size(); i++) {
|
|
||||||
if (!other.points.get(i).equals(points.get(i)))
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
|
edge.recordBounds(bounds, membershipBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (fullDistance >= Math.PI) {
|
||||||
public int hashCode() {
|
// We can't reliably assume that bounds did its longitude calculation right, so we force it to be unbounded.
|
||||||
return points.hashCode();
|
bounds.noLongitudeBound();
|
||||||
}
|
}
|
||||||
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public boolean equals(Object o) {
|
||||||
StringBuilder edgeString = new StringBuilder("{");
|
if (!(o instanceof GeoConvexPolygon))
|
||||||
for (int i = 0; i < edges.length; i++) {
|
return false;
|
||||||
edgeString.append(edges[i]).append(" internal? ").append(internalEdges[i]).append("; ");
|
GeoConvexPolygon other = (GeoConvexPolygon) o;
|
||||||
}
|
if (other.points.size() != points.size())
|
||||||
edgeString.append("}");
|
return false;
|
||||||
return "GeoConvexPolygon: {points=" + points + " edges="+edgeString+"}";
|
|
||||||
|
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 + "}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,188 +17,185 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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
|
* Degenerate bounding box limited on two sides (left lon, right lon).
|
||||||
* GeoWideDegenerateHorizontalLine.
|
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||||
*/
|
* GeoWideDegenerateHorizontalLine.
|
||||||
public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoDegenerateHorizontalLine extends GeoBBoxBase {
|
||||||
public final double latitude;
|
public final double latitude;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
public final double rightLon;
|
||||||
|
|
||||||
public final GeoPoint LHC;
|
|
||||||
public final GeoPoint RHC;
|
|
||||||
|
|
||||||
public final Plane plane;
|
|
||||||
public final SidedPlane leftPlane;
|
|
||||||
public final SidedPlane rightPlane;
|
|
||||||
|
|
||||||
public final GeoPoint[] planePoints;
|
public final GeoPoint LHC;
|
||||||
|
public final GeoPoint RHC;
|
||||||
|
|
||||||
public final GeoPoint centerPoint;
|
public final Plane plane;
|
||||||
public final GeoPoint[] edgePoints;
|
public final SidedPlane leftPlane;
|
||||||
|
public final SidedPlane rightPlane;
|
||||||
/** 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");
|
|
||||||
|
|
||||||
this.latitude = latitude;
|
public final GeoPoint[] planePoints;
|
||||||
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
|
public final GeoPoint centerPoint;
|
||||||
while (leftLon > rightLon) {
|
public final GeoPoint[] edgePoints;
|
||||||
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);
|
|
||||||
|
|
||||||
this.planePoints = new GeoPoint[]{LHC,RHC};
|
/**
|
||||||
|
* Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}
|
||||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
*/
|
||||||
|
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
|
this.latitude = latitude;
|
||||||
public GeoBBox expand(final double angle)
|
this.leftLon = leftLon;
|
||||||
{
|
this.rightLon = rightLon;
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
final double sinLatitude = Math.sin(latitude);
|
||||||
public boolean isWithin(final Vector point)
|
final double cosLatitude = Math.cos(latitude);
|
||||||
{
|
final double sinLeftLon = Math.sin(leftLon);
|
||||||
return plane.evaluateIsZero(point) &&
|
final double cosLeftLon = Math.cos(leftLon);
|
||||||
leftPlane.isWithin(point) &&
|
final double sinRightLon = Math.sin(rightLon);
|
||||||
rightPlane.isWithin(point);
|
final double cosRightLon = Math.cos(rightLon);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Now build the two points
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
this.LHC = new GeoPoint(sinLatitude, sinLeftLon, cosLatitude, cosLeftLon);
|
||||||
{
|
this.RHC = new GeoPoint(sinLatitude, sinRightLon, cosLatitude, cosRightLon);
|
||||||
return plane.evaluateIsZero(x,y,z) &&
|
|
||||||
leftPlane.isWithin(x,y,z) &&
|
|
||||||
rightPlane.isWithin(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
this.plane = new Plane(sinLatitude);
|
||||||
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.
|
// Normalize
|
||||||
*@return the center.
|
while (leftLon > rightLon) {
|
||||||
*/
|
rightLon += Math.PI * 2.0;
|
||||||
@Override
|
|
||||||
public GeoPoint getCenter() {
|
|
||||||
return centerPoint;
|
|
||||||
}
|
}
|
||||||
|
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||||
|
final double sinMiddleLon = Math.sin(middleLon);
|
||||||
|
final double cosMiddleLon = Math.cos(middleLon);
|
||||||
|
|
||||||
@Override
|
this.centerPoint = new GeoPoint(sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon);
|
||||||
public GeoPoint[] getEdgePoints()
|
this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
|
||||||
{
|
this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
|
||||||
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.
|
this.planePoints = new GeoPoint[]{LHC, RHC};
|
||||||
*@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
|
this.edgePoints = new GeoPoint[]{centerPoint};
|
||||||
public int getRelationship(final GeoShape path) {
|
}
|
||||||
if (path.intersects(plane,planePoints,leftPlane,rightPlane))
|
|
||||||
return OVERLAPS;
|
|
||||||
|
|
||||||
if (path.isWithin(centerPoint))
|
@Override
|
||||||
return CONTAINS;
|
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
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return plane.evaluateIsZero(x, y, z) &&
|
||||||
if (!(o instanceof GeoDegenerateHorizontalLine))
|
leftPlane.isWithin(x, y, z) &&
|
||||||
return false;
|
rightPlane.isWithin(x, y, z);
|
||||||
GeoDegenerateHorizontalLine other = (GeoDegenerateHorizontalLine)o;
|
}
|
||||||
return other.LHC.equals(LHC) && other.RHC.equals(RHC);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public double getRadius() {
|
||||||
int result = LHC.hashCode();
|
double topAngle = centerPoint.arcDistance(RHC);
|
||||||
result = 31 * result + RHC.hashCode();
|
double bottomAngle = centerPoint.arcDistance(LHC);
|
||||||
return result;
|
return Math.max(topAngle, bottomAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String toString() {
|
* Returns the center of a circle into which the area will be inscribed.
|
||||||
return "GeoDegenerateHorizontalLine: {latitude="+latitude+"("+latitude*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightLon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
*
|
||||||
}
|
* @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 + ")}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,130 +17,125 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This GeoBBox represents an area rectangle of one specific latitude with
|
/**
|
||||||
* no longitude bounds.
|
* This GeoBBox represents an area rectangle of one specific latitude with
|
||||||
*/
|
* no longitude bounds.
|
||||||
public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoDegenerateLatitudeZone extends GeoBBoxBase {
|
||||||
public final double latitude;
|
public final double latitude;
|
||||||
|
|
||||||
public final double sinLatitude;
|
public final double sinLatitude;
|
||||||
public final Plane plane;
|
public final Plane plane;
|
||||||
public final GeoPoint interiorPoint;
|
public final GeoPoint interiorPoint;
|
||||||
public final GeoPoint[] edgePoints;
|
public final GeoPoint[] edgePoints;
|
||||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||||
|
|
||||||
public GeoDegenerateLatitudeZone(final double latitude)
|
public GeoDegenerateLatitudeZone(final double latitude) {
|
||||||
{
|
this.latitude = latitude;
|
||||||
this.latitude = latitude;
|
|
||||||
|
this.sinLatitude = Math.sin(latitude);
|
||||||
this.sinLatitude = Math.sin(latitude);
|
double cosLatitude = Math.cos(latitude);
|
||||||
double cosLatitude = Math.cos(latitude);
|
this.plane = new Plane(sinLatitude);
|
||||||
this.plane = new Plane(sinLatitude);
|
// Compute an interior point.
|
||||||
// Compute an interior point.
|
interiorPoint = new GeoPoint(cosLatitude, 0.0, sinLatitude);
|
||||||
interiorPoint = new GeoPoint(cosLatitude,0.0,sinLatitude);
|
edgePoints = new GeoPoint[]{interiorPoint};
|
||||||
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
|
if (path.isWithin(interiorPoint)) {
|
||||||
public GeoBBox expand(final double angle)
|
return CONTAINS;
|
||||||
{
|
|
||||||
double newTopLat = latitude + angle;
|
|
||||||
double newBottomLat = latitude - angle;
|
|
||||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return DISJOINT;
|
||||||
public boolean isWithin(final Vector point)
|
}
|
||||||
{
|
|
||||||
return Math.abs(point.z - this.sinLatitude) < 1e-10;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
public boolean equals(Object o) {
|
||||||
{
|
if (!(o instanceof GeoDegenerateLatitudeZone))
|
||||||
return Math.abs(z - this.sinLatitude) < 1e-10;
|
return false;
|
||||||
}
|
GeoDegenerateLatitudeZone other = (GeoDegenerateLatitudeZone) o;
|
||||||
|
return other.latitude == latitude;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getRadius()
|
public int hashCode() {
|
||||||
{
|
long temp = Double.doubleToLongBits(latitude);
|
||||||
return Math.PI;
|
int result = (int) (temp ^ (temp >>> 32));
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
@Override
|
||||||
*@return the center.
|
public String toString() {
|
||||||
*/
|
return "GeoDegenerateLatitudeZone: {lat=" + latitude + "(" + latitude * 180.0 / Math.PI + ")}";
|
||||||
@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+")}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,144 +17,141 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Degenerate longitude slice.
|
/**
|
||||||
*/
|
* Degenerate longitude slice.
|
||||||
public class GeoDegenerateLongitudeSlice extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoDegenerateLongitudeSlice extends GeoBBoxBase {
|
||||||
public final double longitude;
|
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;
|
|
||||||
|
|
||||||
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 final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE};
|
||||||
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.
|
* Accepts only values in the following ranges: lon: {@code -PI -> PI}
|
||||||
this.interiorPoint = new GeoPoint(cosLongitude, sinLongitude, 0.0);
|
*/
|
||||||
this.boundingPlane = new SidedPlane(interiorPoint, -sinLongitude, cosLongitude);
|
public GeoDegenerateLongitudeSlice(final double longitude) {
|
||||||
this.edgePoints = new GeoPoint[]{interiorPoint};
|
// 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
|
@Override
|
||||||
public GeoBBox expand(final double angle)
|
public boolean isWithin(final Vector point) {
|
||||||
{
|
return plane.evaluateIsZero(point) &&
|
||||||
// Figuring out when we escalate to a special case requires some prefiguring
|
boundingPlane.isWithin(point);
|
||||||
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
|
@Override
|
||||||
public boolean isWithin(final Vector point)
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return plane.evaluateIsZero(x, y, z) &&
|
||||||
return plane.evaluateIsZero(point) &&
|
boundingPlane.isWithin(x, y, z);
|
||||||
boundingPlane.isWithin(point);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
public double getRadius() {
|
||||||
{
|
return Math.PI * 0.5;
|
||||||
return plane.evaluateIsZero(x,y,z) &&
|
}
|
||||||
boundingPlane.isWithin(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public double getRadius()
|
* Returns the center of a circle into which the area will be inscribed.
|
||||||
{
|
*
|
||||||
return Math.PI * 0.5;
|
* @return the center.
|
||||||
}
|
*/
|
||||||
|
@Override
|
||||||
|
public GeoPoint getCenter() {
|
||||||
|
return interiorPoint;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
@Override
|
||||||
*@return the center.
|
public GeoPoint[] getEdgePoints() {
|
||||||
*/
|
return edgePoints;
|
||||||
@Override
|
}
|
||||||
public GeoPoint getCenter() {
|
|
||||||
return interiorPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeoPoint[] getEdgePoints()
|
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||||
{
|
return p.intersects(plane, notablePoints, planePoints, bounds, boundingPlane);
|
||||||
return edgePoints;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@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,
|
* Compute longitude/latitude bounds for the shape.
|
||||||
* 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
|
* @param bounds is the optional input bounds object. If this is null,
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
* a bounds object will be created. Otherwise, the input object will be modified.
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
* @return a Bounds object describing the shape's bounds. If the bounds cannot
|
||||||
*/
|
* be computed, then return a Bounds object with noLongitudeBound,
|
||||||
@Override
|
* noTopLatitudeBound, and noBottomLatitudeBound.
|
||||||
public Bounds getBounds(Bounds bounds)
|
*/
|
||||||
{
|
@Override
|
||||||
if (bounds == null)
|
public Bounds getBounds(Bounds bounds) {
|
||||||
bounds = new Bounds();
|
if (bounds == null)
|
||||||
bounds.noTopLatitudeBound().noBottomLatitudeBound();
|
bounds = new Bounds();
|
||||||
bounds.addLongitudeSlice(longitude,longitude);
|
bounds.noTopLatitudeBound().noBottomLatitudeBound();
|
||||||
return bounds;
|
bounds.addLongitudeSlice(longitude, longitude);
|
||||||
}
|
return bounds;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public int getRelationship(final GeoShape path) {
|
||||||
// Look for intersections.
|
// Look for intersections.
|
||||||
if (path.intersects(plane,planePoints,boundingPlane))
|
if (path.intersects(plane, planePoints, boundingPlane))
|
||||||
return OVERLAPS;
|
return OVERLAPS;
|
||||||
|
|
||||||
if (path.isWithin(interiorPoint))
|
if (path.isWithin(interiorPoint))
|
||||||
return CONTAINS;
|
return CONTAINS;
|
||||||
|
|
||||||
return DISJOINT;
|
return DISJOINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public boolean equals(Object o) {
|
||||||
{
|
if (!(o instanceof GeoDegenerateLongitudeSlice))
|
||||||
if (!(o instanceof GeoDegenerateLongitudeSlice))
|
return false;
|
||||||
return false;
|
GeoDegenerateLongitudeSlice other = (GeoDegenerateLongitudeSlice) o;
|
||||||
GeoDegenerateLongitudeSlice other = (GeoDegenerateLongitudeSlice)o;
|
return other.longitude == longitude;
|
||||||
return other.longitude == longitude;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result;
|
int result;
|
||||||
long temp;
|
long temp;
|
||||||
temp = Double.doubleToLongBits(longitude);
|
temp = Double.doubleToLongBits(longitude);
|
||||||
result = (int) (temp ^ (temp >>> 32));
|
result = (int) (temp ^ (temp >>> 32));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "GeoDegenerateLongitudeSlice: {longitude="+longitude+"("+longitude*180.0/Math.PI+")}";
|
return "GeoDegenerateLongitudeSlice: {longitude=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,156 +17,175 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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.
|
* 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 class GeoDegeneratePoint extends GeoPoint implements GeoBBox {
|
||||||
public final double latitude;
|
public final double latitude;
|
||||||
public final double longitude;
|
public final double longitude;
|
||||||
public final GeoPoint[] edgePoints;
|
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Expand box by specified angle.
|
public GeoDegeneratePoint(final double lat, final double lon) {
|
||||||
*@param angle is the angle amount to expand the GeoBBox by.
|
super(lat, lon);
|
||||||
*@return a new GeoBBox.
|
this.latitude = lat;
|
||||||
*/
|
this.longitude = lon;
|
||||||
@Override
|
this.edgePoints = new GeoPoint[]{this};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return a sample point that is on the edge of the shape.
|
/**
|
||||||
*@return an interior point.
|
* Expand box by specified angle.
|
||||||
*/
|
*
|
||||||
@Override
|
* @param angle is the angle amount to expand the GeoBBox by.
|
||||||
public GeoPoint[] getEdgePoints() {
|
* @return a new GeoBBox.
|
||||||
return edgePoints;
|
*/
|
||||||
}
|
@Override
|
||||||
|
public GeoBBox expand(final double angle) {
|
||||||
/** Assess whether a plane, within the provided bounds, intersects
|
final double newTopLat = latitude + angle;
|
||||||
* with the shape.
|
final double newBottomLat = latitude - angle;
|
||||||
*@param plane is the plane to assess for intersection with the shape's edges or
|
final double newLeftLon = longitude - angle;
|
||||||
* bounding curves.
|
final double newRightLon = longitude + angle;
|
||||||
*@param bounds are a set of bounds that define an area that an
|
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon);
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
/**
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
* Return a sample point that is on the edge of the shape.
|
||||||
* 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
|
* @return an interior point.
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
*/
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
@Override
|
||||||
*/
|
public GeoPoint[] getEdgePoints() {
|
||||||
@Override
|
return edgePoints;
|
||||||
public Bounds getBounds(Bounds bounds) {
|
}
|
||||||
if (bounds == null)
|
|
||||||
bounds = new Bounds();
|
|
||||||
bounds.addPoint(latitude,longitude);
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Equals */
|
/**
|
||||||
@Override
|
* Assess whether a plane, within the provided bounds, intersects
|
||||||
public boolean equals(Object o) {
|
* with the shape.
|
||||||
if (!(o instanceof GeoDegeneratePoint))
|
*
|
||||||
return false;
|
* @param plane is the plane to assess for intersection with the shape's edges or
|
||||||
GeoDegeneratePoint other = (GeoDegeneratePoint)o;
|
* bounding curves.
|
||||||
return other.latitude == latitude && other.longitude == longitude;
|
* @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
|
for (Membership m : bounds) {
|
||||||
public int hashCode() {
|
if (!m.isWithin(this))
|
||||||
int result;
|
return false;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Check if a point is within this shape.
|
/**
|
||||||
*@param x is x coordinate of point to check.
|
* Compute longitude/latitude bounds for the shape.
|
||||||
*@param y is y coordinate of point to check.
|
*
|
||||||
*@param z is z coordinate of point to check.
|
* @param bounds is the optional input bounds object. If this is null,
|
||||||
*@return true if the point is within this shape
|
* 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
|
||||||
@Override
|
* be computed, then return a Bounds object with noLongitudeBound,
|
||||||
public boolean isWithin(final double x, final double y, final double z) {
|
* noTopLatitudeBound, and noBottomLatitudeBound.
|
||||||
return x == this.x && y == this.y && z == this.z;
|
*/
|
||||||
}
|
@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.
|
* Equals
|
||||||
*@return the radius.
|
*/
|
||||||
*/
|
@Override
|
||||||
@Override
|
public boolean equals(Object o) {
|
||||||
public double getRadius() {
|
if (!(o instanceof GeoDegeneratePoint))
|
||||||
return 0.0;
|
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.
|
@Override
|
||||||
*@return the center.
|
public int hashCode() {
|
||||||
*/
|
int result;
|
||||||
@Override
|
long temp;
|
||||||
public GeoPoint getCenter() {
|
temp = Double.doubleToLongBits(latitude);
|
||||||
return this;
|
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.
|
@Override
|
||||||
* Note: return value is how the GeoShape relates to the GeoArea, not the
|
public String toString() {
|
||||||
* other way around. For example, if this GeoArea is entirely within the
|
return "GeoDegeneratePoint: {lat=" + latitude + "(" + latitude * 180.0 / Math.PI + "), lon=" + longitude + "(" + longitude * 180.0 / Math.PI + ")}";
|
||||||
* 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;
|
/**
|
||||||
}
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,191 +17,188 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Degenerate bounding box limited on two sides (top lat, bottom lat).
|
/**
|
||||||
*/
|
* Degenerate bounding box limited on two sides (top lat, bottom lat).
|
||||||
public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoDegenerateVerticalLine extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double longitude;
|
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;
|
|
||||||
|
|
||||||
public final GeoPoint[] planePoints;
|
public final GeoPoint UHC;
|
||||||
|
public final GeoPoint LHC;
|
||||||
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");
|
|
||||||
|
|
||||||
this.topLat = topLat;
|
public final SidedPlane topPlane;
|
||||||
this.bottomLat = bottomLat;
|
public final SidedPlane bottomPlane;
|
||||||
this.longitude = longitude;
|
public final SidedPlane boundingPlane;
|
||||||
|
public final Plane plane;
|
||||||
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);
|
public final GeoPoint[] planePoints;
|
||||||
this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat);
|
|
||||||
|
|
||||||
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
|
if (path.isWithin(centerPoint)) {
|
||||||
public GeoBBox expand(final double angle)
|
//System.err.println(" contains");
|
||||||
{
|
return CONTAINS;
|
||||||
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
|
//System.err.println(" disjoint");
|
||||||
public boolean isWithin(final Vector point)
|
return DISJOINT;
|
||||||
{
|
}
|
||||||
return plane.evaluateIsZero(point) &&
|
|
||||||
boundingPlane.isWithin(point) &&
|
|
||||||
topPlane.isWithin(point) &&
|
|
||||||
bottomPlane.isWithin(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
public boolean equals(Object o) {
|
||||||
{
|
if (!(o instanceof GeoDegenerateVerticalLine))
|
||||||
return plane.evaluateIsZero(x,y,z) &&
|
return false;
|
||||||
boundingPlane.isWithin(x,y,z) &&
|
GeoDegenerateVerticalLine other = (GeoDegenerateVerticalLine) o;
|
||||||
topPlane.isWithin(x,y,z) &&
|
return other.UHC.equals(UHC) && other.LHC.equals(LHC);
|
||||||
bottomPlane.isWithin(x,y,z);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getRadius()
|
public int hashCode() {
|
||||||
{
|
int result = UHC.hashCode();
|
||||||
// Here we compute the distance from the middle point to one of the corners. However, we need to be careful
|
result = 31 * result + LHC.hashCode();
|
||||||
// to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and
|
return result;
|
||||||
// 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.
|
@Override
|
||||||
*@return the center.
|
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 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+")}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,116 +17,135 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Generic geo-distance-capable shape class description. An implementer
|
/**
|
||||||
* of this interface is capable of computing the described "distance" values,
|
* Generic geo-distance-capable shape class description. An implementer
|
||||||
* which are meant to provide both actual distance values, as well as
|
* of this interface is capable of computing the described "distance" values,
|
||||||
* distance estimates that can be computed more cheaply.
|
* 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 {
|
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
|
* Compute this shape's normal "distance" to the GeoPoint.
|
||||||
* points outside of the shape.
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
*@param point is the point to compute the distance to.
|
* points outside of the shape.
|
||||||
*@return the normal distance, defined as the perpendicular distance from
|
*
|
||||||
* from the point to one of the shape's bounding plane. Normal
|
* @param point is the point to compute the distance to.
|
||||||
* distances can therefore typically only go up to PI/2, except
|
* @return the normal distance, defined as the perpendicular distance from
|
||||||
* when they represent the sum of a sequence of normal distances.
|
* from the point to one of the shape's bounding plane. Normal
|
||||||
*/
|
* distances can therefore typically only go up to PI/2, except
|
||||||
public double computeNormalDistance(GeoPoint point);
|
* 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
|
* Compute this shape's normal "distance" to the GeoPoint.
|
||||||
* points outside of the shape.
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
*@param x is the point's unit x coordinate (using U.S. convention).
|
* points outside of the shape.
|
||||||
*@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).
|
* @param x is the point's unit x coordinate (using U.S. convention).
|
||||||
*@return the normal distance, defined as the perpendicular distance from
|
* @param y is the point's unit y coordinate (using U.S. convention).
|
||||||
* from the point to one of the shape's bounding plane. Normal
|
* @param z is the point's unit z coordinate (using U.S. convention).
|
||||||
* distances can therefore typically only go up to PI/2, except
|
* @return the normal distance, defined as the perpendicular distance from
|
||||||
* when they represent the sum of a sequence of normal distances.
|
* from the point to one of the shape's bounding plane. Normal
|
||||||
*/
|
* distances can therefore typically only go up to PI/2, except
|
||||||
public double computeNormalDistance(double x, double y, double z);
|
* when they represent the sum of a sequence of normal distances.
|
||||||
|
*/
|
||||||
/** Compute the square of this shape's normal "distance" to the GeoPoint.
|
public double computeNormalDistance(double x, double y, double z);
|
||||||
* 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 linear "distance" to the GeoPoint.
|
/**
|
||||||
* A return value of Double.MAX_VALUE should be returned for
|
* Compute the square of this shape's normal "distance" to the GeoPoint.
|
||||||
* points outside of the shape.
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
*@param point is the point to compute the distance to.
|
* points outside of the shape.
|
||||||
*@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
|
* @param point is the point to compute the distance to.
|
||||||
* bounding planes. Linear distances can therefore typically go up to PI,
|
* @return the square of the normal distance, defined as the perpendicular
|
||||||
* except when they represent the sum of a sequence of linear distances.
|
* distance from
|
||||||
*/
|
* from the point to one of the shape's bounding plane. Normal
|
||||||
public double computeLinearDistance(GeoPoint point);
|
* 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
|
* Compute the square of this shape's normal "distance" to the GeoPoint.
|
||||||
* points outside of the shape.
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
*@param x is the point's unit x coordinate (using U.S. convention).
|
* points outside of the shape.
|
||||||
*@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).
|
* @param x is the point's unit x coordinate (using U.S. convention).
|
||||||
*@return the linear (or chord) distance, defined as the distance from
|
* @param y is the point's unit y coordinate (using U.S. convention).
|
||||||
* from the point to the nearest point on the unit sphere and on one of the shape's
|
* @param z is the point's unit z coordinate (using U.S. convention).
|
||||||
* bounding planes. Linear distances can therefore typically go up to PI,
|
* @return the square of the normal distance, defined as the perpendicular
|
||||||
* except when they represent the sum of a sequence of linear distances.
|
* distance from
|
||||||
*/
|
* from the point to one of the shape's bounding plane. Normal
|
||||||
public double computeLinearDistance(double x, double y, double z);
|
* 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
|
* Compute this shape's linear "distance" to the GeoPoint.
|
||||||
* points outside of the shape.
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
*@param point is the point to compute the distance to.
|
* points outside of the shape.
|
||||||
*@return the square of the linear (or chord) distance, defined as the
|
*
|
||||||
* distance from
|
* @param point is the point to compute the distance to.
|
||||||
* from the point to the nearest point on the unit sphere and on one of the shape's
|
* @return the linear (or chord) distance, defined as the distance from
|
||||||
* bounding planes. Linear distances can therefore typically go up to PI,
|
* from the point to the nearest point on the unit sphere and on one of the shape's
|
||||||
* except when they represent the sum of a sequence of linear distances.
|
* 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);
|
*/
|
||||||
|
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
|
* Compute this shape's linear "distance" to the GeoPoint.
|
||||||
* points outside of the shape.
|
* A return value of Double.MAX_VALUE should be returned for
|
||||||
*@param x is the point's unit x coordinate (using U.S. convention).
|
* points outside of the shape.
|
||||||
*@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).
|
* @param x is the point's unit x coordinate (using U.S. convention).
|
||||||
*@return the square of the linear (or chord) distance, defined as the distance from
|
* @param y is the point's unit y coordinate (using U.S. convention).
|
||||||
* from the point to the nearest point on the unit sphere and on one of the shape's
|
* @param z is the point's unit z coordinate (using U.S. convention).
|
||||||
* bounding planes. Linear distances can therefore typically go up to PI,
|
* @return the linear (or chord) distance, defined as the distance from
|
||||||
* except when they represent the sum of a sequence of linear distances.
|
* 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,
|
||||||
public double computeSquaredLinearDistance(double x, double y, double z);
|
* 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);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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).
|
* computation (which also includes point membership determination).
|
||||||
*/
|
*/
|
||||||
public interface GeoDistanceShape extends GeoMembershipShape, GeoDistance {
|
public interface GeoDistanceShape extends GeoMembershipShape, GeoDistance {
|
||||||
|
@ -17,185 +17,180 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This GeoBBox represents an area rectangle limited only in latitude.
|
/**
|
||||||
*/
|
* This GeoBBox represents an area rectangle limited only in latitude.
|
||||||
public class GeoLatitudeZone extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoLatitudeZone extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double cosTopLat;
|
public final double cosTopLat;
|
||||||
public final double cosBottomLat;
|
public final double cosBottomLat;
|
||||||
public final SidedPlane topPlane;
|
public final SidedPlane topPlane;
|
||||||
public final SidedPlane bottomPlane;
|
public final SidedPlane bottomPlane;
|
||||||
public final GeoPoint interiorPoint;
|
public final GeoPoint interiorPoint;
|
||||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
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
|
// 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.
|
// special case that most GeoBBox's do not have.
|
||||||
public final GeoPoint topBoundaryPoint;
|
public final GeoPoint topBoundaryPoint;
|
||||||
public final GeoPoint bottomBoundaryPoint;
|
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);
|
|
||||||
|
|
||||||
// Compute an interior point. Pick one whose lat is between top and bottom.
|
// Edge points
|
||||||
final double middleLat = (topLat + bottomLat) * 0.5;
|
public final GeoPoint[] edgePoints;
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public GeoLatitudeZone(final double topLat, final double bottomLat) {
|
||||||
public GeoBBox expand(final double angle)
|
this.topLat = topLat;
|
||||||
{
|
this.bottomLat = bottomLat;
|
||||||
final double newTopLat = topLat + angle;
|
|
||||||
final double newBottomLat = bottomLat - angle;
|
|
||||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
final double sinTopLat = Math.sin(topLat);
|
||||||
public boolean isWithin(final Vector point)
|
final double sinBottomLat = Math.sin(bottomLat);
|
||||||
{
|
this.cosTopLat = Math.cos(topLat);
|
||||||
return topPlane.isWithin(point) &&
|
this.cosBottomLat = Math.cos(bottomLat);
|
||||||
bottomPlane.isWithin(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Construct sample points, so we get our sidedness right
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
final Vector topPoint = new Vector(0.0, 0.0, sinTopLat);
|
||||||
{
|
final Vector bottomPoint = new Vector(0.0, 0.0, sinBottomLat);
|
||||||
return topPlane.isWithin(x,y,z) &&
|
|
||||||
bottomPlane.isWithin(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Compute an interior point. Pick one whose lat is between top and bottom.
|
||||||
public double getRadius()
|
final double middleLat = (topLat + bottomLat) * 0.5;
|
||||||
{
|
final double sinMiddleLat = Math.sin(middleLat);
|
||||||
// This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that
|
this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat);
|
||||||
// would contain all the bounding box points, when starting in the "center".
|
this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat), 0.0, sinTopLat);
|
||||||
if (topLat > 0.0 && bottomLat < 0.0)
|
this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 0.0, sinBottomLat);
|
||||||
return Math.PI;
|
|
||||||
double maxCosLat = cosTopLat;
|
|
||||||
if (maxCosLat < cosBottomLat)
|
|
||||||
maxCosLat = cosBottomLat;
|
|
||||||
return maxCosLat * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
this.topPlane = new SidedPlane(interiorPoint, sinTopLat);
|
||||||
*@return the center.
|
this.bottomPlane = new SidedPlane(interiorPoint, sinBottomLat);
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public GeoPoint getCenter() {
|
|
||||||
// This is totally arbitrary and only a cartesian could agree with it.
|
|
||||||
return interiorPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
this.edgePoints = new GeoPoint[]{topBoundaryPoint, bottomBoundaryPoint};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public GeoBBox expand(final double angle) {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
final double newTopLat = topLat + angle;
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
final double newBottomLat = bottomLat - angle;
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
}
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Bounds getBounds(Bounds bounds)
|
|
||||||
{
|
|
||||||
if (bounds == null)
|
|
||||||
bounds = new Bounds();
|
|
||||||
bounds.noLongitudeBound().addLatitudeZone(topLat).addLatitudeZone(bottomLat);
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public boolean isWithin(final Vector point) {
|
||||||
final int insideRectangle = isShapeInsideBBox(path);
|
return topPlane.isWithin(point) &&
|
||||||
if (insideRectangle == SOME_INSIDE)
|
bottomPlane.isWithin(point);
|
||||||
return OVERLAPS;
|
}
|
||||||
|
|
||||||
final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint);
|
@Override
|
||||||
final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint);
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
|
return topPlane.isWithin(x, y, z) &&
|
||||||
if (topBoundaryInsideShape && !bottomBoundaryInsideShape ||
|
bottomPlane.isWithin(x, y, z);
|
||||||
!topBoundaryInsideShape && bottomBoundaryInsideShape)
|
}
|
||||||
return OVERLAPS;
|
|
||||||
|
|
||||||
final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape;
|
|
||||||
|
|
||||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
@Override
|
||||||
return OVERLAPS;
|
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) ||
|
@Override
|
||||||
path.intersects(bottomPlane,planePoints,topPlane))
|
public GeoPoint[] getEdgePoints() {
|
||||||
return OVERLAPS;
|
return edgePoints;
|
||||||
|
}
|
||||||
|
|
||||||
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
|
@Override
|
||||||
// within the zone, but the shape includes areas outside the zone crossing a pole.
|
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||||
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
|
return p.intersects(topPlane, notablePoints, planePoints, bounds, bottomPlane) ||
|
||||||
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
|
p.intersects(bottomPlane, notablePoints, planePoints, bounds, topPlane);
|
||||||
// one such point is within, then OVERLAPS is the right answer.
|
}
|
||||||
|
|
||||||
if (insideShape)
|
|
||||||
return CONTAINS;
|
|
||||||
|
|
||||||
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
|
final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint);
|
||||||
public boolean equals(Object o)
|
final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint);
|
||||||
{
|
|
||||||
if (!(o instanceof GeoLatitudeZone))
|
|
||||||
return false;
|
|
||||||
GeoLatitudeZone other = (GeoLatitudeZone)o;
|
|
||||||
return other.topPlane.equals(topPlane) && other.bottomPlane.equals(bottomPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (topBoundaryInsideShape && !bottomBoundaryInsideShape ||
|
||||||
public int hashCode() {
|
!topBoundaryInsideShape && bottomBoundaryInsideShape)
|
||||||
int result = topPlane.hashCode();
|
return OVERLAPS;
|
||||||
result = 31 * result + bottomPlane.hashCode();
|
|
||||||
return result;
|
final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape;
|
||||||
}
|
|
||||||
|
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||||
@Override
|
return OVERLAPS;
|
||||||
public String toString() {
|
|
||||||
return "GeoLatitudeZone: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/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.
|
||||||
|
|
||||||
|
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 + ")}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,187 +17,184 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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
|
* Bounding box limited on left and right.
|
||||||
* GeoWideLongitudeSlice.
|
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||||
*/
|
* GeoWideLongitudeSlice.
|
||||||
public class GeoLongitudeSlice extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoLongitudeSlice extends GeoBBoxBase {
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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;
|
|
||||||
|
|
||||||
public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
public final SidedPlane leftPlane;
|
||||||
|
public final SidedPlane rightPlane;
|
||||||
/** 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;
|
public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE};
|
||||||
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
|
public final GeoPoint centerPoint;
|
||||||
while (leftLon > rightLon) {
|
|
||||||
rightLon += Math.PI * 2.0;
|
public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
||||||
}
|
|
||||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
/**
|
||||||
this.centerPoint = new GeoPoint(0.0,middleLon);
|
* Accepts only values in the following ranges: lon: {@code -PI -> PI}
|
||||||
|
*/
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
public GeoLongitudeSlice(final double leftLon, double rightLon) {
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
// 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
|
if (insideRectangle == ALL_INSIDE) {
|
||||||
public GeoBBox expand(final double angle)
|
return WITHIN;
|
||||||
{
|
|
||||||
// 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
|
if (insideShape) {
|
||||||
public boolean isWithin(final Vector point)
|
return CONTAINS;
|
||||||
{
|
|
||||||
return leftPlane.isWithin(point) &&
|
|
||||||
rightPlane.isWithin(point);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
return DISJOINT;
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
}
|
||||||
{
|
|
||||||
return leftPlane.isWithin(x,y,z) &&
|
|
||||||
rightPlane.isWithin(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getRadius()
|
public boolean equals(Object o) {
|
||||||
{
|
if (!(o instanceof GeoLongitudeSlice))
|
||||||
// Compute the extent and divide by two
|
return false;
|
||||||
double extent = rightLon - leftLon;
|
GeoLongitudeSlice other = (GeoLongitudeSlice) o;
|
||||||
if (extent < 0.0)
|
return other.leftLon == leftLon && other.rightLon == rightLon;
|
||||||
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.
|
@Override
|
||||||
*@return the center.
|
public int hashCode() {
|
||||||
*/
|
int result;
|
||||||
@Override
|
long temp;
|
||||||
public GeoPoint getCenter() {
|
temp = Double.doubleToLongBits(leftLon);
|
||||||
return centerPoint;
|
result = (int) (temp ^ (temp >>> 32));
|
||||||
}
|
temp = Double.doubleToLongBits(rightLon);
|
||||||
|
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GeoPoint[] getEdgePoints()
|
public String toString() {
|
||||||
{
|
return "GeoLongitudeSlice: {leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
|
||||||
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+")}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Membership shapes have capabilities of both geohashing and membership
|
/**
|
||||||
|
* Membership shapes have capabilities of both geohashing and membership
|
||||||
* determination.
|
* determination.
|
||||||
*/
|
*/
|
||||||
public interface GeoMembershipShape extends GeoShape, Membership {
|
public interface GeoMembershipShape extends GeoShape, Membership {
|
||||||
|
@ -17,163 +17,158 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This GeoBBox represents an area rectangle limited only in south latitude.
|
/**
|
||||||
*/
|
* This GeoBBox represents an area rectangle limited only in south latitude.
|
||||||
public class GeoNorthLatitudeZone extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoNorthLatitudeZone extends GeoBBoxBase {
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double cosBottomLat;
|
public final double cosBottomLat;
|
||||||
public final SidedPlane bottomPlane;
|
public final SidedPlane bottomPlane;
|
||||||
public final GeoPoint interiorPoint;
|
public final GeoPoint interiorPoint;
|
||||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||||
|
|
||||||
public final GeoPoint bottomBoundaryPoint;
|
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);
|
|
||||||
|
|
||||||
// Compute an interior point. Pick one whose lat is between top and bottom.
|
// Edge points
|
||||||
final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5;
|
public final GeoPoint[] edgePoints;
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public GeoNorthLatitudeZone(final double bottomLat) {
|
||||||
public GeoBBox expand(final double angle)
|
this.bottomLat = bottomLat;
|
||||||
{
|
|
||||||
final double newTopLat = Math.PI * 0.5;
|
|
||||||
final double newBottomLat = bottomLat - angle;
|
|
||||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
final double sinBottomLat = Math.sin(bottomLat);
|
||||||
public boolean isWithin(final Vector point)
|
this.cosBottomLat = Math.cos(bottomLat);
|
||||||
{
|
|
||||||
return
|
|
||||||
bottomPlane.isWithin(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Construct sample points, so we get our sidedness right
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
final Vector bottomPoint = new Vector(0.0, 0.0, sinBottomLat);
|
||||||
{
|
|
||||||
return
|
|
||||||
bottomPlane.isWithin(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Compute an interior point. Pick one whose lat is between top and bottom.
|
||||||
public double getRadius()
|
final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5;
|
||||||
{
|
final double sinMiddleLat = Math.sin(middleLat);
|
||||||
// This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that
|
this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat);
|
||||||
// would contain all the bounding box points, when starting in the "center".
|
this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat), 0.0, sinBottomLat);
|
||||||
if (bottomLat < 0.0)
|
|
||||||
return Math.PI;
|
|
||||||
double maxCosLat = cosBottomLat;
|
|
||||||
return maxCosLat * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
this.bottomPlane = new SidedPlane(interiorPoint, sinBottomLat);
|
||||||
*@return the center.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public GeoPoint getCenter() {
|
|
||||||
return interiorPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
this.edgePoints = new GeoPoint[]{bottomBoundaryPoint};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public GeoBBox expand(final double angle) {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
final double newTopLat = Math.PI * 0.5;
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
final double newBottomLat = bottomLat - angle;
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
}
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Bounds getBounds(Bounds bounds)
|
|
||||||
{
|
|
||||||
if (bounds == null)
|
|
||||||
bounds = new Bounds();
|
|
||||||
bounds.noLongitudeBound().noTopLatitudeBound().addLatitudeZone(bottomLat);
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public boolean isWithin(final Vector point) {
|
||||||
final int insideRectangle = isShapeInsideBBox(path);
|
return
|
||||||
if (insideRectangle == SOME_INSIDE)
|
bottomPlane.isWithin(point);
|
||||||
return OVERLAPS;
|
}
|
||||||
|
|
||||||
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)
|
@Override
|
||||||
return OVERLAPS;
|
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 (
|
@Override
|
||||||
path.intersects(bottomPlane,planePoints))
|
public GeoPoint[] getEdgePoints() {
|
||||||
return OVERLAPS;
|
return edgePoints;
|
||||||
|
}
|
||||||
|
|
||||||
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
|
@Override
|
||||||
// within the zone, but the shape includes areas outside the zone crossing a pole.
|
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||||
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
|
return
|
||||||
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
|
p.intersects(bottomPlane, notablePoints, planePoints, bounds);
|
||||||
// one such point is within, then OVERLAPS is the right answer.
|
}
|
||||||
|
|
||||||
if (insideShape)
|
|
||||||
return CONTAINS;
|
|
||||||
|
|
||||||
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
|
final boolean insideShape = path.isWithin(bottomBoundaryPoint);
|
||||||
public boolean equals(Object o)
|
|
||||||
{
|
|
||||||
if (!(o instanceof GeoNorthLatitudeZone))
|
|
||||||
return false;
|
|
||||||
GeoNorthLatitudeZone other = (GeoNorthLatitudeZone)o;
|
|
||||||
return other.bottomPlane.equals(bottomPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||||
public int hashCode() {
|
return OVERLAPS;
|
||||||
int result = bottomPlane.hashCode();
|
|
||||||
return result;
|
// 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
|
if (
|
||||||
public String toString() {
|
path.intersects(bottomPlane, planePoints))
|
||||||
return "GeoNorthLatitudeZone: {bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}";
|
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 + ")}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,235 +17,230 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box limited on three sides (bottom lat, left lon, right lon), including
|
/**
|
||||||
* the north pole.
|
* Bounding box limited on three sides (bottom lat, left lon, right lon), including
|
||||||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
* the north pole.
|
||||||
* GeoWideNorthRectangle.
|
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||||
*/
|
* GeoWideNorthRectangle.
|
||||||
public class GeoNorthRectangle extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoNorthRectangle extends GeoBBoxBase {
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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;
|
|
||||||
|
|
||||||
public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
public final double cosMiddleLat;
|
||||||
|
|
||||||
/** 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;
|
public final GeoPoint LRHC;
|
||||||
this.leftLon = leftLon;
|
public final GeoPoint LLHC;
|
||||||
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);
|
public final SidedPlane bottomPlane;
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
public final SidedPlane leftPlane;
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
public final SidedPlane rightPlane;
|
||||||
|
|
||||||
this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC};
|
public final GeoPoint[] bottomPlanePoints;
|
||||||
this.leftPlanePoints = new GeoPoint[]{NORTH_POLE,LLHC};
|
public final GeoPoint[] leftPlanePoints;
|
||||||
this.rightPlanePoints = new GeoPoint[]{NORTH_POLE,LRHC};
|
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
|
final boolean insideShape = path.isWithin(NORTH_POLE);
|
||||||
public GeoBBox expand(final double angle)
|
|
||||||
{
|
if (insideRectangle == ALL_INSIDE && insideShape) {
|
||||||
final double newTopLat = Math.PI * 0.5;
|
//System.err.println(" inside of each other");
|
||||||
final double newBottomLat = bottomLat - angle;
|
return OVERLAPS;
|
||||||
// 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
|
if (
|
||||||
public boolean isWithin(final Vector point)
|
path.intersects(bottomPlane, bottomPlanePoints, leftPlane, rightPlane) ||
|
||||||
{
|
path.intersects(leftPlane, leftPlanePoints, bottomPlane, rightPlane) ||
|
||||||
return
|
path.intersects(rightPlane, rightPlanePoints, leftPlane, bottomPlane)) {
|
||||||
bottomPlane.isWithin(point) &&
|
//System.err.println(" edges intersect");
|
||||||
leftPlane.isWithin(point) &&
|
return OVERLAPS;
|
||||||
rightPlane.isWithin(point);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (insideRectangle == ALL_INSIDE) {
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
//System.err.println(" shape inside rectangle");
|
||||||
{
|
return WITHIN;
|
||||||
return
|
|
||||||
bottomPlane.isWithin(x,y,z) &&
|
|
||||||
leftPlane.isWithin(x,y,z) &&
|
|
||||||
rightPlane.isWithin(x,y,z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (insideShape) {
|
||||||
public double getRadius()
|
//System.err.println(" shape contains rectangle");
|
||||||
{
|
return CONTAINS;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
//System.err.println(" disjoint");
|
||||||
|
return DISJOINT;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
@Override
|
||||||
*@return the center.
|
public boolean equals(Object o) {
|
||||||
*/
|
if (!(o instanceof GeoNorthRectangle))
|
||||||
@Override
|
return false;
|
||||||
public GeoPoint getCenter() {
|
GeoNorthRectangle other = (GeoNorthRectangle) o;
|
||||||
return centerPoint;
|
return other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@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.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public int hashCode() {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
int result = LLHC.hashCode();
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
result = 31 * result + LRHC.hashCode();
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
return result;
|
||||||
* 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
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public String toString() {
|
||||||
//System.err.println(this+" getrelationship with "+path);
|
return "GeoNorthRectangle: {bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
|
||||||
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+")}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,28 +17,24 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This class represents a point on the surface of a unit sphere.
|
/**
|
||||||
*/
|
* This class represents a point on the surface of a unit sphere.
|
||||||
public class GeoPoint extends Vector
|
*/
|
||||||
{
|
public class GeoPoint extends Vector {
|
||||||
public GeoPoint(final double sinLat, final double sinLon, final double cosLat, final double cosLon)
|
public GeoPoint(final double sinLat, final double sinLon, final double cosLat, final double cosLon) {
|
||||||
{
|
super(cosLat * cosLon, cosLat * sinLon, sinLat);
|
||||||
super(cosLat*cosLon,cosLat*sinLon,sinLat);
|
}
|
||||||
}
|
|
||||||
|
public GeoPoint(final double lat, final double lon) {
|
||||||
public GeoPoint(final double lat, final double lon)
|
this(Math.sin(lat), Math.sin(lon), Math.cos(lat), Math.cos(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 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));
|
||||||
|
}
|
||||||
public double arcDistance(final GeoPoint v)
|
|
||||||
{
|
|
||||||
return Tools.safeAcos(dotProduct(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,146 +21,148 @@ import java.util.ArrayList;
|
|||||||
import java.util.BitSet;
|
import java.util.BitSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Class which constructs a GeoMembershipShape representing an arbitrary polygon.
|
/**
|
||||||
*/
|
* Class which constructs a GeoMembershipShape representing an arbitrary polygon.
|
||||||
public class GeoPolygonFactory
|
*/
|
||||||
{
|
public class GeoPolygonFactory {
|
||||||
private 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<GeoPoint> 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<GeoPoint> pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge, boolean isInternalEdge) {
|
/**
|
||||||
// Algorithm as follows:
|
* Create a GeoMembershipShape of the right kind given the specified bounds.
|
||||||
// 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.
|
* @param pointList is a list of the GeoPoints to build an arbitrary polygon out of.
|
||||||
// Once we detect a point that is within, if there are points put aside for recursion, then call recursively.
|
* @param convexPointIndex is the index of a single convex point whose conformation with
|
||||||
|
* its neighbors determines inside/outside for the entire polygon.
|
||||||
// Current composite. This is what we'll actually be returning.
|
* @return a GeoMembershipShape corresponding to what was specified.
|
||||||
final GeoCompositeMembershipShape rval = new GeoCompositeMembershipShape();
|
*/
|
||||||
|
public static GeoMembershipShape makeGeoPolygon(List<GeoPoint> pointList, int convexPointIndex) {
|
||||||
final List<GeoPoint> recursionList = new ArrayList<GeoPoint>();
|
// The basic operation uses a set of points, two points determining one particular edge, and a sided plane
|
||||||
final List<GeoPoint> currentList = new ArrayList<GeoPoint>();
|
// describing membership.
|
||||||
final BitSet internalEdgeList = new BitSet();
|
return buildPolygonShape(pointList, convexPointIndex, getLegalIndex(convexPointIndex + 1, pointList.size()),
|
||||||
final List<SidedPlane> currentPlanes = new ArrayList<SidedPlane>();
|
new SidedPlane(pointList.get(getLegalIndex(convexPointIndex - 1, pointList.size())),
|
||||||
|
pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex + 1, pointList.size()))),
|
||||||
// Initialize the current list and current planes
|
false);
|
||||||
currentList.add(pointsList.get(startPointIndex));
|
}
|
||||||
currentList.add(pointsList.get(endPointIndex));
|
|
||||||
internalEdgeList.set(currentPlanes.size(),isInternalEdge);
|
public static GeoMembershipShape buildPolygonShape(List<GeoPoint> pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge, boolean isInternalEdge) {
|
||||||
currentPlanes.add(startingEdge);
|
// 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.
|
||||||
// Now, scan all remaining points, in order. We'll use an index and just add to it.
|
// If not, put it into a list of points for recursion. If it is within, add new edge and keep going.
|
||||||
for (int i = 0; i < pointsList.size() - 2; i++) {
|
// Once we detect a point that is within, if there are points put aside for recursion, then call recursively.
|
||||||
GeoPoint newPoint = pointsList.get(getLegalIndex(i + endPointIndex + 1, pointsList.size()));
|
|
||||||
if (isWithin(newPoint, currentPlanes)) {
|
// Current composite. This is what we'll actually be returning.
|
||||||
// Construct a sided plane based on the last two points, and the previous point
|
final GeoCompositeMembershipShape rval = new GeoCompositeMembershipShape();
|
||||||
SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size()-2),newPoint,currentList.get(currentList.size()-1));
|
|
||||||
// Construct a sided plane based on the return trip
|
final List<GeoPoint> recursionList = new ArrayList<GeoPoint>();
|
||||||
SidedPlane returnBoundary = new SidedPlane(currentList.get(currentList.size()-1),currentList.get(0),newPoint);
|
final List<GeoPoint> currentList = new ArrayList<GeoPoint>();
|
||||||
// Verify that none of the points beyond the new point in the list are inside the polygon we'd
|
final BitSet internalEdgeList = new BitSet();
|
||||||
// be creating if we stopped making the current polygon right now.
|
final List<SidedPlane> currentPlanes = new ArrayList<SidedPlane>();
|
||||||
boolean pointInside = false;
|
|
||||||
for (int j = i + 1; j < pointsList.size() - 2; j++) {
|
// Initialize the current list and current planes
|
||||||
GeoPoint checkPoint = pointsList.get(getLegalIndex(j + endPointIndex + 1, pointsList.size()));
|
currentList.add(pointsList.get(startPointIndex));
|
||||||
boolean isInside = true;
|
currentList.add(pointsList.get(endPointIndex));
|
||||||
if (isInside && !newBoundary.isWithin(checkPoint))
|
internalEdgeList.set(currentPlanes.size(), isInternalEdge);
|
||||||
isInside = false;
|
currentPlanes.add(startingEdge);
|
||||||
if (isInside && !returnBoundary.isWithin(checkPoint))
|
|
||||||
isInside = false;
|
// Now, scan all remaining points, in order. We'll use an index and just add to it.
|
||||||
if (isInside) {
|
for (int i = 0; i < pointsList.size() - 2; i++) {
|
||||||
for (SidedPlane plane : currentPlanes) {
|
GeoPoint newPoint = pointsList.get(getLegalIndex(i + endPointIndex + 1, pointsList.size()));
|
||||||
if (!plane.isWithin(checkPoint)) {
|
if (isWithin(newPoint, currentPlanes)) {
|
||||||
isInside = false;
|
// Construct a sided plane based on the last two points, and the previous point
|
||||||
break;
|
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
|
||||||
if (isInside) {
|
// be creating if we stopped making the current polygon right now.
|
||||||
pointInside = true;
|
boolean pointInside = false;
|
||||||
break;
|
for (int j = i + 1; j < pointsList.size() - 2; j++) {
|
||||||
}
|
GeoPoint checkPoint = pointsList.get(getLegalIndex(j + endPointIndex + 1, pointsList.size()));
|
||||||
}
|
boolean isInside = true;
|
||||||
if (!pointInside) {
|
if (isInside && !newBoundary.isWithin(checkPoint))
|
||||||
// Any excluded points?
|
isInside = false;
|
||||||
boolean isInternalBoundary = recursionList.size() > 0;
|
if (isInside && !returnBoundary.isWithin(checkPoint))
|
||||||
if (isInternalBoundary) {
|
isInside = false;
|
||||||
// Handle exclusion
|
if (isInside) {
|
||||||
recursionList.add(newPoint);
|
for (SidedPlane plane : currentPlanes) {
|
||||||
recursionList.add(currentList.get(currentList.size()-1));
|
if (!plane.isWithin(checkPoint)) {
|
||||||
if (recursionList.size() == pointsList.size()) {
|
isInside = false;
|
||||||
// We are trying to recurse with a list the same size as the one we started with.
|
break;
|
||||||
// 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);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (isInside) {
|
||||||
|
pointInside = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!pointInside) {
|
||||||
boolean returnEdgeInternalBoundary = recursionList.size() > 0;
|
// Any excluded points?
|
||||||
if (returnEdgeInternalBoundary) {
|
boolean isInternalBoundary = recursionList.size() > 0;
|
||||||
// The last step back to the start point had a recursion, so take care of that before we complete our work
|
if (isInternalBoundary) {
|
||||||
recursionList.add(currentList.get(0));
|
// Handle exclusion
|
||||||
recursionList.add(currentList.get(currentList.size()-1));
|
recursionList.add(newPoint);
|
||||||
|
recursionList.add(currentList.get(currentList.size() - 1));
|
||||||
if (recursionList.size() == pointsList.size()) {
|
if (recursionList.size() == pointsList.size()) {
|
||||||
// We are trying to recurse with a list the same size as the one we started with.
|
// We are trying to recurse with a list the same size as the one we started with.
|
||||||
// Clearly, the polygon cannot be constructed
|
// Clearly, the polygon cannot be constructed
|
||||||
throw new IllegalArgumentException("Polygon is illegal; cannot be decomposed into convex parts");
|
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
|
// We want the other side for the recursion
|
||||||
SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary);
|
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();
|
recursionList.clear();
|
||||||
|
}
|
||||||
|
currentList.add(newPoint);
|
||||||
|
internalEdgeList.set(currentPlanes.size(), isInternalBoundary);
|
||||||
|
currentPlanes.add(newBoundary);
|
||||||
|
} else {
|
||||||
|
recursionList.add(newPoint);
|
||||||
}
|
}
|
||||||
// Now, add in the current shape.
|
} else {
|
||||||
rval.addShape(new GeoConvexPolygon(currentList,internalEdgeList,returnEdgeInternalBoundary));
|
recursionList.add(newPoint);
|
||||||
//System.out.println("Done creating polygon");
|
}
|
||||||
return rval;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static boolean isWithin(GeoPoint newPoint, List<SidedPlane> currentPlanes) {
|
boolean returnEdgeInternalBoundary = recursionList.size() > 0;
|
||||||
for (SidedPlane p : currentPlanes) {
|
if (returnEdgeInternalBoundary) {
|
||||||
if (!p.isWithin(newPoint))
|
// The last step back to the start point had a recursion, so take care of that before we complete our work
|
||||||
return false;
|
recursionList.add(currentList.get(0));
|
||||||
}
|
recursionList.add(currentList.get(currentList.size() - 1));
|
||||||
return true;
|
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) {
|
protected static boolean isWithin(GeoPoint newPoint, List<SidedPlane> currentPlanes) {
|
||||||
while (index < 0) {
|
for (SidedPlane p : currentPlanes) {
|
||||||
index += size;
|
if (!p.isWithin(newPoint))
|
||||||
}
|
return false;
|
||||||
while (index >= size) {
|
|
||||||
index -= size;
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static int getLegalIndex(int index, int size) {
|
||||||
|
while (index < 0) {
|
||||||
|
index += size;
|
||||||
|
}
|
||||||
|
while (index >= size) {
|
||||||
|
index -= size;
|
||||||
|
}
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,250 +17,246 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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
|
* Bounding box limited on four sides (top lat, bottom lat, left lon, right lon).
|
||||||
* GeoWideRectangle.
|
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||||
*/
|
* GeoWideRectangle.
|
||||||
public class GeoRectangle extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoRectangle extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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;
|
|
||||||
|
|
||||||
public final GeoPoint[] edgePoints;
|
public final double cosMiddleLat;
|
||||||
|
|
||||||
/** 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;
|
public final GeoPoint ULHC;
|
||||||
this.bottomLat = bottomLat;
|
public final GeoPoint URHC;
|
||||||
this.leftLon = leftLon;
|
public final GeoPoint LRHC;
|
||||||
this.rightLon = rightLon;
|
public final GeoPoint LLHC;
|
||||||
|
|
||||||
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);
|
public final SidedPlane topPlane;
|
||||||
this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat);
|
public final SidedPlane bottomPlane;
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
public final SidedPlane leftPlane;
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
public final SidedPlane rightPlane;
|
||||||
|
|
||||||
this.topPlanePoints = new GeoPoint[]{ULHC,URHC};
|
public final GeoPoint[] topPlanePoints;
|
||||||
this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC};
|
public final GeoPoint[] bottomPlanePoints;
|
||||||
this.leftPlanePoints = new GeoPoint[]{ULHC,LLHC};
|
public final GeoPoint[] leftPlanePoints;
|
||||||
this.rightPlanePoints = new GeoPoint[]{URHC,LRHC};
|
public final GeoPoint[] rightPlanePoints;
|
||||||
|
|
||||||
this.edgePoints = new GeoPoint[]{ULHC};
|
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
|
final boolean insideShape = path.isWithin(ULHC);
|
||||||
public GeoBBox expand(final double angle)
|
|
||||||
{
|
if (insideRectangle == ALL_INSIDE && insideShape) {
|
||||||
final double newTopLat = topLat + angle;
|
//System.err.println(" inside of each other");
|
||||||
final double newBottomLat = bottomLat - angle;
|
return OVERLAPS;
|
||||||
// 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
|
if (path.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) ||
|
||||||
public boolean isWithin(final Vector point)
|
path.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) ||
|
||||||
{
|
path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane, rightPlane) ||
|
||||||
return topPlane.isWithin(point) &&
|
path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane, bottomPlane)) {
|
||||||
bottomPlane.isWithin(point) &&
|
//System.err.println(" edges intersect");
|
||||||
leftPlane.isWithin(point) &&
|
return OVERLAPS;
|
||||||
rightPlane.isWithin(point);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (insideRectangle == ALL_INSIDE) {
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
//System.err.println(" shape inside rectangle");
|
||||||
{
|
return WITHIN;
|
||||||
return topPlane.isWithin(x,y,z) &&
|
|
||||||
bottomPlane.isWithin(x,y,z) &&
|
|
||||||
leftPlane.isWithin(x,y,z) &&
|
|
||||||
rightPlane.isWithin(x,y,z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (insideShape) {
|
||||||
public double getRadius()
|
//System.err.println(" shape contains rectangle");
|
||||||
{
|
return CONTAINS;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
//System.err.println(" disjoint");
|
||||||
|
return DISJOINT;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
@Override
|
||||||
*@return the center.
|
public boolean equals(Object o) {
|
||||||
*/
|
if (!(o instanceof GeoRectangle))
|
||||||
@Override
|
return false;
|
||||||
public GeoPoint getCenter() {
|
GeoRectangle other = (GeoRectangle) o;
|
||||||
return centerPoint;
|
return other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
public int hashCode() {
|
||||||
{
|
int result = ULHC.hashCode();
|
||||||
return p.intersects(topPlane,notablePoints,topPlanePoints,bounds,bottomPlane,leftPlane,rightPlane) ||
|
result = 31 * result + LRHC.hashCode();
|
||||||
p.intersects(bottomPlane,notablePoints,bottomPlanePoints,bounds,topPlane,leftPlane,rightPlane) ||
|
return result;
|
||||||
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.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public String toString() {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
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 + ")}";
|
||||||
*@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+")}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,44 +17,53 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* 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
|
* Generic shape. This describes methods that help GeoAreas figure out
|
||||||
* set of geo hash values.
|
* how they interact with a shape, for the purposes of coming up with a
|
||||||
*/
|
* set of geo hash values.
|
||||||
|
*/
|
||||||
public interface GeoShape extends Membership {
|
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
|
* Return a sample point that is on the outside edge/boundary of the shape.
|
||||||
* is returned, but zero or two are also possible.
|
*
|
||||||
*/
|
* @return samples of all edge points from distinct edge sections. Typically one point
|
||||||
public GeoPoint[] getEdgePoints();
|
* is returned, but zero or two are also possible.
|
||||||
|
*/
|
||||||
/** Assess whether a plane, within the provided bounds, intersects
|
public GeoPoint[] getEdgePoints();
|
||||||
* 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);
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
/**
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
* Assess whether a plane, within the provided bounds, intersects
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
* with the shape. Note well that this method is allowed to return "true"
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
* if there are internal edges of a composite shape which intersect the plane.
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
* Doing this can cause getRelationship() for most GeoBBox shapes to return
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
* OVERLAPS rather than the more correct CONTAINS, but that cannot be
|
||||||
*/
|
* helped for some complex shapes that are built out of overlapping parts.
|
||||||
public Bounds getBounds(final Bounds bounds);
|
*
|
||||||
|
* @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);
|
||||||
}
|
}
|
||||||
|
@ -17,19 +17,23 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Some shapes can compute radii of a geocircle in which they are inscribed.
|
/**
|
||||||
*/
|
* Some shapes can compute radii of a geocircle in which they are inscribed.
|
||||||
public interface GeoSizeable
|
*/
|
||||||
{
|
public interface GeoSizeable {
|
||||||
/** Returns the radius of a circle into which the GeoSizeable area can
|
/**
|
||||||
* be inscribed.
|
* Returns the radius of a circle into which the GeoSizeable area can
|
||||||
*@return the radius.
|
* be inscribed.
|
||||||
*/
|
*
|
||||||
public double getRadius();
|
* @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();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,159 +17,154 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** This GeoBBox represents an area rectangle limited only in north latitude.
|
/**
|
||||||
*/
|
* This GeoBBox represents an area rectangle limited only in north latitude.
|
||||||
public class GeoSouthLatitudeZone extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoSouthLatitudeZone extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double cosTopLat;
|
public final double cosTopLat;
|
||||||
public final SidedPlane topPlane;
|
public final SidedPlane topPlane;
|
||||||
public final GeoPoint interiorPoint;
|
public final GeoPoint interiorPoint;
|
||||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||||
|
|
||||||
public final GeoPoint topBoundaryPoint;
|
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);
|
|
||||||
|
|
||||||
// Compute an interior point. Pick one whose lat is between top and bottom.
|
// Edge points
|
||||||
final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
|
public final GeoPoint[] edgePoints;
|
||||||
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};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public GeoSouthLatitudeZone(final double topLat) {
|
||||||
public GeoBBox expand(final double angle)
|
this.topLat = topLat;
|
||||||
{
|
|
||||||
final double newTopLat = topLat + angle;
|
|
||||||
final double newBottomLat = -Math.PI * 0.5;
|
|
||||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
final double sinTopLat = Math.sin(topLat);
|
||||||
public boolean isWithin(final Vector point)
|
this.cosTopLat = Math.cos(topLat);
|
||||||
{
|
|
||||||
return topPlane.isWithin(point);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Construct sample points, so we get our sidedness right
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
final Vector topPoint = new Vector(0.0, 0.0, sinTopLat);
|
||||||
{
|
|
||||||
return topPlane.isWithin(x,y,z);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
// Compute an interior point. Pick one whose lat is between top and bottom.
|
||||||
public double getRadius()
|
final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
|
||||||
{
|
final double sinMiddleLat = Math.sin(middleLat);
|
||||||
// This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that
|
this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 0.0, sinMiddleLat);
|
||||||
// would contain all the bounding box points, when starting in the "center".
|
this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat), 0.0, sinTopLat);
|
||||||
if (topLat > 0.0)
|
|
||||||
return Math.PI;
|
|
||||||
double maxCosLat = cosTopLat;
|
|
||||||
return maxCosLat * Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
this.topPlane = new SidedPlane(interiorPoint, sinTopLat);
|
||||||
*@return the center.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public GeoPoint getCenter() {
|
|
||||||
return interiorPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
this.edgePoints = new GeoPoint[]{topBoundaryPoint};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public GeoBBox expand(final double angle) {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
final double newTopLat = topLat + angle;
|
||||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
final double newBottomLat = -Math.PI * 0.5;
|
||||||
* be computed, then return a Bounds object with noLongitudeBound,
|
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
||||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
}
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Bounds getBounds(Bounds bounds)
|
|
||||||
{
|
|
||||||
if (bounds == null)
|
|
||||||
bounds = new Bounds();
|
|
||||||
bounds.noLongitudeBound().addLatitudeZone(topLat).noBottomLatitudeBound();
|
|
||||||
return bounds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public boolean isWithin(final Vector point) {
|
||||||
final int insideRectangle = isShapeInsideBBox(path);
|
return topPlane.isWithin(point);
|
||||||
if (insideRectangle == SOME_INSIDE)
|
}
|
||||||
return OVERLAPS;
|
|
||||||
|
|
||||||
final boolean insideShape = path.isWithin(topBoundaryPoint);
|
@Override
|
||||||
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
return topPlane.isWithin(x, y, z);
|
||||||
return OVERLAPS;
|
}
|
||||||
|
|
||||||
// Second, the shortcut of seeing whether endpoints are in/out is not going to
|
@Override
|
||||||
// work with no area endpoints. So we rely entirely on intersections.
|
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
|
@Override
|
||||||
// within the zone, but the shape includes areas outside the zone crossing a pole.
|
public GeoPoint[] getEdgePoints() {
|
||||||
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
|
return edgePoints;
|
||||||
// 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)
|
@Override
|
||||||
return WITHIN;
|
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
|
@Override
|
||||||
public boolean equals(Object o)
|
public int getRelationship(final GeoShape path) {
|
||||||
{
|
final int insideRectangle = isShapeInsideBBox(path);
|
||||||
if (!(o instanceof GeoSouthLatitudeZone))
|
if (insideRectangle == SOME_INSIDE)
|
||||||
return false;
|
return OVERLAPS;
|
||||||
GeoSouthLatitudeZone other = (GeoSouthLatitudeZone)o;
|
|
||||||
return other.topPlane.equals(topPlane);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
final boolean insideShape = path.isWithin(topBoundaryPoint);
|
||||||
public int hashCode() {
|
|
||||||
int result = topPlane.hashCode();
|
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||||
return result;
|
return OVERLAPS;
|
||||||
}
|
|
||||||
|
// Second, the shortcut of seeing whether endpoints are in/out is not going to
|
||||||
@Override
|
// work with no area endpoints. So we rely entirely on intersections.
|
||||||
public String toString() {
|
|
||||||
return "GeoSouthLatitudeZone: {toplat="+topLat+"("+topLat*180.0/Math.PI+")}";
|
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 + ")}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,230 +17,225 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box limited on three sides (top lat, left lon, right lon). The
|
/**
|
||||||
* other corner is the south pole.
|
* Bounding box limited on three sides (top lat, left lon, right lon). The
|
||||||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
* other corner is the south pole.
|
||||||
* GeoWideSouthRectangle.
|
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||||
*/
|
* GeoWideSouthRectangle.
|
||||||
public class GeoSouthRectangle extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoSouthRectangle extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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;
|
|
||||||
|
|
||||||
public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE};
|
public final double cosMiddleLat;
|
||||||
|
|
||||||
/** 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;
|
public final GeoPoint ULHC;
|
||||||
this.leftLon = leftLon;
|
public final GeoPoint URHC;
|
||||||
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);
|
public final SidedPlane topPlane;
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
public final SidedPlane leftPlane;
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
public final SidedPlane rightPlane;
|
||||||
|
|
||||||
this.topPlanePoints = new GeoPoint[]{ULHC,URHC};
|
public final GeoPoint[] topPlanePoints;
|
||||||
this.leftPlanePoints = new GeoPoint[]{ULHC,SOUTH_POLE};
|
public final GeoPoint[] leftPlanePoints;
|
||||||
this.rightPlanePoints = new GeoPoint[]{URHC,SOUTH_POLE};
|
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
|
final boolean insideShape = path.isWithin(SOUTH_POLE);
|
||||||
public GeoBBox expand(final double angle)
|
|
||||||
{
|
if (insideRectangle == ALL_INSIDE && insideShape) {
|
||||||
final double newTopLat = topLat + angle;
|
//System.err.println(" inside of each other");
|
||||||
final double newBottomLat = -Math.PI * 0.5;
|
return OVERLAPS;
|
||||||
// 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
|
if (path.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) ||
|
||||||
public boolean isWithin(final Vector point)
|
path.intersects(leftPlane, leftPlanePoints, topPlane, rightPlane) ||
|
||||||
{
|
path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane)) {
|
||||||
return topPlane.isWithin(point) &&
|
//System.err.println(" edges intersect");
|
||||||
leftPlane.isWithin(point) &&
|
return OVERLAPS;
|
||||||
rightPlane.isWithin(point);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (insideRectangle == ALL_INSIDE) {
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
//System.err.println(" shape inside rectangle");
|
||||||
{
|
return WITHIN;
|
||||||
return topPlane.isWithin(x,y,z) &&
|
|
||||||
leftPlane.isWithin(x,y,z) &&
|
|
||||||
rightPlane.isWithin(x,y,z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
if (insideShape) {
|
||||||
public double getRadius()
|
//System.err.println(" shape contains rectangle");
|
||||||
{
|
return CONTAINS;
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
//System.err.println(" disjoint");
|
||||||
|
return DISJOINT;
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
@Override
|
||||||
*@return the center.
|
public boolean equals(Object o) {
|
||||||
*/
|
if (!(o instanceof GeoSouthRectangle))
|
||||||
@Override
|
return false;
|
||||||
public GeoPoint getCenter() {
|
GeoSouthRectangle other = (GeoSouthRectangle) o;
|
||||||
return centerPoint;
|
return other.ULHC.equals(ULHC) && other.URHC.equals(URHC);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
public int hashCode() {
|
||||||
{
|
int result = ULHC.hashCode();
|
||||||
return p.intersects(topPlane,notablePoints,topPlanePoints,bounds,leftPlane,rightPlane) ||
|
result = 31 * result + URHC.hashCode();
|
||||||
p.intersects(leftPlane,notablePoints,leftPlanePoints,bounds,rightPlane,topPlane) ||
|
return result;
|
||||||
p.intersects(rightPlane,notablePoints,rightPlanePoints,bounds,leftPlane,topPlane);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public String toString() {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
return "GeoSouthRectangle: {toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
|
||||||
*@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+")}";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,219 +17,215 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Degenerate bounding box wider than PI and limited on two sides (left lon, right lon).
|
/**
|
||||||
*/
|
* Degenerate bounding box wider than PI and limited on two sides (left lon, right lon).
|
||||||
public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase {
|
||||||
public final double latitude;
|
public final double latitude;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
public final double rightLon;
|
||||||
|
|
||||||
public final GeoPoint LHC;
|
|
||||||
public final GeoPoint RHC;
|
|
||||||
|
|
||||||
public final Plane plane;
|
|
||||||
public final SidedPlane leftPlane;
|
|
||||||
public final SidedPlane rightPlane;
|
|
||||||
|
|
||||||
public final GeoPoint[] planePoints;
|
public final GeoPoint LHC;
|
||||||
|
public final GeoPoint RHC;
|
||||||
public final GeoPoint centerPoint;
|
|
||||||
|
|
||||||
public final EitherBound eitherBound;
|
public final Plane plane;
|
||||||
|
public final SidedPlane leftPlane;
|
||||||
public final GeoPoint[] edgePoints;
|
public final SidedPlane rightPlane;
|
||||||
|
|
||||||
/** 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;
|
public final GeoPoint[] planePoints;
|
||||||
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
|
public final GeoPoint centerPoint;
|
||||||
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);
|
public final EitherBound eitherBound;
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
|
||||||
|
|
||||||
this.planePoints = new GeoPoint[]{LHC,RHC};
|
public final GeoPoint[] edgePoints;
|
||||||
|
|
||||||
this.eitherBound = new EitherBound();
|
/**
|
||||||
|
* Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}.
|
||||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
* 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
|
@Override
|
||||||
public GeoBBox expand(final double angle)
|
public boolean isWithin(final Vector v) {
|
||||||
{
|
return leftPlane.isWithin(v) || rightPlane.isWithin(v);
|
||||||
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
|
@Override
|
||||||
public boolean isWithin(final Vector point)
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,186 +17,182 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box wider than PI but limited on left and right sides (
|
/**
|
||||||
* left lon, right lon).
|
* Bounding box wider than PI but limited on left and right sides (
|
||||||
*/
|
* left lon, right lon).
|
||||||
public class GeoWideLongitudeSlice extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoWideLongitudeSlice extends GeoBBoxBase {
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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;
|
|
||||||
|
|
||||||
public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
public final SidedPlane leftPlane;
|
||||||
|
public final SidedPlane rightPlane;
|
||||||
/** 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");
|
|
||||||
|
|
||||||
this.leftLon = leftLon;
|
public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE, SOUTH_POLE};
|
||||||
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
|
public final GeoPoint centerPoint;
|
||||||
while (leftLon > rightLon) {
|
|
||||||
rightLon += Math.PI * 2.0;
|
public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
||||||
}
|
|
||||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
/**
|
||||||
this.centerPoint = new GeoPoint(0.0,middleLon);
|
* Accepts only values in the following ranges: lon: {@code -PI -> PI}.
|
||||||
|
* Horizantal angle must be greater than or equal to PI.
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
*/
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
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
|
this.leftLon = leftLon;
|
||||||
public GeoBBox expand(final double angle)
|
this.rightLon = rightLon;
|
||||||
{
|
|
||||||
// Figuring out when we escalate to a special case requires some prefiguring
|
final double sinLeftLon = Math.sin(leftLon);
|
||||||
double currentLonSpan = rightLon - leftLon;
|
final double cosLeftLon = Math.cos(leftLon);
|
||||||
if (currentLonSpan < 0.0)
|
final double sinRightLon = Math.sin(rightLon);
|
||||||
currentLonSpan += Math.PI * 2.0;
|
final double cosRightLon = Math.cos(rightLon);
|
||||||
double newLeftLon = leftLon - angle;
|
|
||||||
double newRightLon = rightLon + angle;
|
// Normalize
|
||||||
if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
|
while (leftLon > rightLon) {
|
||||||
newLeftLon = -Math.PI;
|
rightLon += Math.PI * 2.0;
|
||||||
newRightLon = Math.PI;
|
|
||||||
}
|
|
||||||
return GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5,-Math.PI * 0.5,newLeftLon,newRightLon);
|
|
||||||
}
|
}
|
||||||
|
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||||
|
this.centerPoint = new GeoPoint(0.0, middleLon);
|
||||||
|
|
||||||
@Override
|
this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
|
||||||
public boolean isWithin(final Vector point)
|
this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
|
||||||
{
|
}
|
||||||
return leftPlane.isWithin(point) ||
|
|
||||||
rightPlane.isWithin(point);
|
@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
|
@Override
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
public boolean isWithin(final Vector point) {
|
||||||
{
|
return leftPlane.isWithin(point) ||
|
||||||
return leftPlane.isWithin(x,y,z) ||
|
rightPlane.isWithin(point);
|
||||||
rightPlane.isWithin(x,y,z);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getRadius()
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return leftPlane.isWithin(x, y, z) ||
|
||||||
// Compute the extent and divide by two
|
rightPlane.isWithin(x, y, z);
|
||||||
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.
|
@Override
|
||||||
*@return the center.
|
public double getRadius() {
|
||||||
*/
|
// Compute the extent and divide by two
|
||||||
@Override
|
double extent = rightLon - leftLon;
|
||||||
public GeoPoint getCenter() {
|
if (extent < 0.0)
|
||||||
return centerPoint;
|
extent += Math.PI * 2.0;
|
||||||
}
|
return Math.max(Math.PI * 0.5, extent * 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public GeoPoint[] getEdgePoints()
|
* Returns the center of a circle into which the area will be inscribed.
|
||||||
{
|
*
|
||||||
return edgePoints;
|
* @return the center.
|
||||||
}
|
*/
|
||||||
|
@Override
|
||||||
@Override
|
public GeoPoint getCenter() {
|
||||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
return centerPoint;
|
||||||
{
|
}
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public GeoPoint[] getEdgePoints() {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
return edgePoints;
|
||||||
*@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
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||||
final int insideRectangle = isShapeInsideBBox(path);
|
// Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
|
||||||
if (insideRectangle == SOME_INSIDE)
|
// requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
|
||||||
return OVERLAPS;
|
return p.intersects(leftPlane, notablePoints, planePoints, bounds) ||
|
||||||
|
p.intersects(rightPlane, notablePoints, planePoints, bounds);
|
||||||
|
}
|
||||||
|
|
||||||
final boolean insideShape = path.isWithin(NORTH_POLE);
|
/**
|
||||||
|
* Compute longitude/latitude bounds for the shape.
|
||||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
*
|
||||||
return OVERLAPS;
|
* @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) ||
|
@Override
|
||||||
path.intersects(rightPlane,planePoints))
|
public int getRelationship(final GeoShape path) {
|
||||||
return OVERLAPS;
|
final int insideRectangle = isShapeInsideBBox(path);
|
||||||
|
if (insideRectangle == SOME_INSIDE)
|
||||||
|
return OVERLAPS;
|
||||||
|
|
||||||
if (insideRectangle == ALL_INSIDE)
|
final boolean insideShape = path.isWithin(NORTH_POLE);
|
||||||
return WITHIN;
|
|
||||||
|
|
||||||
if (insideShape)
|
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||||
return CONTAINS;
|
return OVERLAPS;
|
||||||
|
|
||||||
return DISJOINT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (path.intersects(leftPlane, planePoints) ||
|
||||||
public boolean equals(Object o)
|
path.intersects(rightPlane, planePoints))
|
||||||
{
|
return OVERLAPS;
|
||||||
if (!(o instanceof GeoWideLongitudeSlice))
|
|
||||||
return false;
|
|
||||||
GeoWideLongitudeSlice other = (GeoWideLongitudeSlice)o;
|
|
||||||
return other.leftLon == leftLon && other.rightLon == rightLon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
if (insideRectangle == ALL_INSIDE)
|
||||||
public int hashCode() {
|
return WITHIN;
|
||||||
int result;
|
|
||||||
long temp;
|
if (insideShape)
|
||||||
temp = Double.doubleToLongBits(leftLon);
|
return CONTAINS;
|
||||||
result = (int) (temp ^ (temp >>> 32));
|
|
||||||
temp = Double.doubleToLongBits(rightLon);
|
return DISJOINT;
|
||||||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
}
|
||||||
return result;
|
|
||||||
}
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
@Override
|
if (!(o instanceof GeoWideLongitudeSlice))
|
||||||
public String toString() {
|
return false;
|
||||||
return "GeoWideLongitudeSlice: {leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
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 + ")}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,255 +17,250 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box wider than PI but limited on three sides (
|
/**
|
||||||
* bottom lat, left lon, right lon).
|
* Bounding box wider than PI but limited on three sides (
|
||||||
*/
|
* bottom lat, left lon, right lon).
|
||||||
public class GeoWideNorthRectangle extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoWideNorthRectangle extends GeoBBoxBase {
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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 double cosMiddleLat;
|
||||||
public final GeoPoint[] leftPlanePoints;
|
|
||||||
public final GeoPoint[] rightPlanePoints;
|
|
||||||
|
|
||||||
public final GeoPoint centerPoint;
|
public final GeoPoint LRHC;
|
||||||
|
public final GeoPoint LLHC;
|
||||||
|
|
||||||
public final EitherBound eitherBound;
|
public final SidedPlane bottomPlane;
|
||||||
|
public final SidedPlane leftPlane;
|
||||||
public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
public final SidedPlane rightPlane;
|
||||||
|
|
||||||
/** 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;
|
public final GeoPoint[] bottomPlanePoints;
|
||||||
this.leftLon = leftLon;
|
public final GeoPoint[] leftPlanePoints;
|
||||||
this.rightLon = rightLon;
|
public final GeoPoint[] rightPlanePoints;
|
||||||
|
|
||||||
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);
|
public final GeoPoint centerPoint;
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
|
||||||
|
|
||||||
this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC};
|
public final EitherBound eitherBound;
|
||||||
this.leftPlanePoints = new GeoPoint[]{NORTH_POLE,LLHC};
|
|
||||||
this.rightPlanePoints = new GeoPoint[]{NORTH_POLE,LRHC};
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public GeoBBox expand(final double angle)
|
public boolean isWithin(final Vector v) {
|
||||||
{
|
return leftPlane.isWithin(v) || rightPlane.isWithin(v);
|
||||||
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
|
@Override
|
||||||
public boolean isWithin(final Vector point)
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,273 +17,268 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box wider than PI but limited on four sides (top lat,
|
/**
|
||||||
* bottom lat, left lon, right lon).
|
* Bounding box wider than PI but limited on four sides (top lat,
|
||||||
*/
|
* bottom lat, left lon, right lon).
|
||||||
public class GeoWideRectangle extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoWideRectangle extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double bottomLat;
|
public final double bottomLat;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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 double cosMiddleLat;
|
||||||
public final GeoPoint[] bottomPlanePoints;
|
|
||||||
public final GeoPoint[] leftPlanePoints;
|
|
||||||
public final GeoPoint[] rightPlanePoints;
|
|
||||||
|
|
||||||
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 SidedPlane topPlane;
|
||||||
|
public final SidedPlane bottomPlane;
|
||||||
public final GeoPoint[] edgePoints;
|
public final SidedPlane leftPlane;
|
||||||
|
public final SidedPlane rightPlane;
|
||||||
/** 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;
|
public final GeoPoint[] topPlanePoints;
|
||||||
this.bottomLat = bottomLat;
|
public final GeoPoint[] bottomPlanePoints;
|
||||||
this.leftLon = leftLon;
|
public final GeoPoint[] leftPlanePoints;
|
||||||
this.rightLon = rightLon;
|
public final GeoPoint[] rightPlanePoints;
|
||||||
|
|
||||||
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);
|
public final GeoPoint centerPoint;
|
||||||
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};
|
public final EitherBound eitherBound;
|
||||||
this.bottomPlanePoints = new GeoPoint[]{LLHC,LRHC};
|
|
||||||
this.leftPlanePoints = new GeoPoint[]{ULHC,LLHC};
|
|
||||||
this.rightPlanePoints = new GeoPoint[]{URHC,LRHC};
|
|
||||||
|
|
||||||
this.eitherBound = new EitherBound();
|
public final GeoPoint[] edgePoints;
|
||||||
|
|
||||||
this.edgePoints = new GeoPoint[]{ULHC};
|
/**
|
||||||
|
* 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
|
@Override
|
||||||
public GeoBBox expand(final double angle)
|
public boolean isWithin(final Vector v) {
|
||||||
{
|
return leftPlane.isWithin(v) || rightPlane.isWithin(v);
|
||||||
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
|
@Override
|
||||||
public boolean isWithin(final Vector point)
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,251 +17,246 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box wider than PI but limited on three sides (top lat,
|
/**
|
||||||
* left lon, right lon).
|
* Bounding box wider than PI but limited on three sides (top lat,
|
||||||
*/
|
* left lon, right lon).
|
||||||
public class GeoWideSouthRectangle extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoWideSouthRectangle extends GeoBBoxBase {
|
||||||
public final double topLat;
|
public final double topLat;
|
||||||
public final double leftLon;
|
public final double leftLon;
|
||||||
public final double rightLon;
|
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 double cosMiddleLat;
|
||||||
public final GeoPoint[] leftPlanePoints;
|
|
||||||
public final GeoPoint[] rightPlanePoints;
|
|
||||||
|
|
||||||
public final GeoPoint centerPoint;
|
public final GeoPoint ULHC;
|
||||||
|
public final GeoPoint URHC;
|
||||||
|
|
||||||
public final EitherBound eitherBound;
|
public final SidedPlane topPlane;
|
||||||
|
public final SidedPlane leftPlane;
|
||||||
public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE};
|
public final SidedPlane rightPlane;
|
||||||
|
|
||||||
/** 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;
|
public final GeoPoint[] topPlanePoints;
|
||||||
this.leftLon = leftLon;
|
public final GeoPoint[] leftPlanePoints;
|
||||||
this.rightLon = rightLon;
|
public final GeoPoint[] rightPlanePoints;
|
||||||
|
|
||||||
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);
|
public final GeoPoint centerPoint;
|
||||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
|
||||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
|
||||||
|
|
||||||
this.topPlanePoints = new GeoPoint[]{ULHC,URHC};
|
public final EitherBound eitherBound;
|
||||||
this.leftPlanePoints = new GeoPoint[]{ULHC,SOUTH_POLE};
|
|
||||||
this.rightPlanePoints = new GeoPoint[]{URHC,SOUTH_POLE};
|
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public GeoBBox expand(final double angle)
|
public boolean isWithin(final Vector v) {
|
||||||
{
|
return leftPlane.isWithin(v) || rightPlane.isWithin(v);
|
||||||
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
|
@Override
|
||||||
public boolean isWithin(final Vector point)
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
{
|
return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,102 +17,97 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Bounding box including the entire world.
|
/**
|
||||||
*/
|
* Bounding box including the entire world.
|
||||||
public class GeoWorld extends GeoBBoxBase
|
*/
|
||||||
{
|
public class GeoWorld extends GeoBBoxBase {
|
||||||
protected final static GeoPoint originPoint = new GeoPoint(1.0,0.0,0.0);
|
protected final static GeoPoint originPoint = new GeoPoint(1.0, 0.0, 0.0);
|
||||||
protected final static GeoPoint[] edgePoints = new GeoPoint[0];
|
protected final static GeoPoint[] edgePoints = new GeoPoint[0];
|
||||||
|
|
||||||
public GeoWorld()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GeoBBox expand(final double angle)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public GeoWorld() {
|
||||||
public double getRadius()
|
}
|
||||||
{
|
|
||||||
return Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the center of a circle into which the area will be inscribed.
|
@Override
|
||||||
*@return the center.
|
public GeoBBox expand(final double angle) {
|
||||||
*/
|
return this;
|
||||||
@Override
|
}
|
||||||
public GeoPoint getCenter() {
|
|
||||||
// Totally arbitrary
|
|
||||||
return originPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isWithin(final Vector point)
|
public double getRadius() {
|
||||||
{
|
return Math.PI;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public boolean isWithin(final double x, final double y, final double z)
|
* Returns the center of a circle into which the area will be inscribed.
|
||||||
{
|
*
|
||||||
return true;
|
* @return the center.
|
||||||
}
|
*/
|
||||||
|
@Override
|
||||||
@Override
|
public GeoPoint getCenter() {
|
||||||
public GeoPoint[] getEdgePoints()
|
// Totally arbitrary
|
||||||
{
|
return originPoint;
|
||||||
return edgePoints;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Compute longitude/latitude bounds for the shape.
|
@Override
|
||||||
*@param bounds is the optional input bounds object. If this is null,
|
public boolean isWithin(final Vector point) {
|
||||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
return true;
|
||||||
*@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
|
@Override
|
||||||
public int getRelationship(final GeoShape path) {
|
public boolean isWithin(final double x, final double y, final double z) {
|
||||||
if (path.getEdgePoints().length > 0)
|
return true;
|
||||||
// Path is always within the world
|
}
|
||||||
return WITHIN;
|
|
||||||
|
|
||||||
return OVERLAPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o)
|
public GeoPoint[] getEdgePoints() {
|
||||||
{
|
return edgePoints;
|
||||||
if (!(o instanceof GeoWorld))
|
}
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public String toString() {
|
* Compute longitude/latitude bounds for the shape.
|
||||||
return "GeoWorld";
|
*
|
||||||
}
|
* @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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,22 +17,27 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Interface describing 3d shape membership methods.
|
/**
|
||||||
*/
|
* Interface describing 3d shape membership methods.
|
||||||
|
*/
|
||||||
public interface Membership {
|
public interface Membership {
|
||||||
|
|
||||||
/** Check if a point is within this shape.
|
/**
|
||||||
*@param point is the point to check.
|
* Check if a point is within this shape.
|
||||||
*@return true if the point is within this shape
|
*
|
||||||
*/
|
* @param point is the point to check.
|
||||||
public boolean isWithin(final Vector point);
|
* @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.
|
* Check if a point is within this shape.
|
||||||
*@param y is y coordinate of point to check.
|
*
|
||||||
*@param z is z coordinate of point to check.
|
* @param x is x coordinate of point to check.
|
||||||
*@return true if the point is within this shape
|
* @param y is y coordinate of point to check.
|
||||||
*/
|
* @param z is z coordinate of point to check.
|
||||||
public boolean isWithin(final double x, final double y, final double z);
|
* @return true if the point is within this shape
|
||||||
|
*/
|
||||||
|
public boolean isWithin(final double x, final double y, final double z);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,95 +17,107 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Combination of a plane, and a sign value indicating what evaluation values are on the correct
|
/**
|
||||||
* side of the plane.
|
* 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 class SidedPlane extends Plane implements Membership {
|
||||||
public final double sigNum;
|
public final double sigNum;
|
||||||
|
|
||||||
/** Construct a SidedPlane identical to an existing one, but reversed.
|
/**
|
||||||
*@param sidedPlane is the existing plane.
|
* Construct a SidedPlane identical to an existing one, but reversed.
|
||||||
*/
|
*
|
||||||
public SidedPlane(SidedPlane sidedPlane) {
|
* @param sidedPlane is the existing plane.
|
||||||
super(sidedPlane,sidedPlane.D);
|
*/
|
||||||
this.sigNum = -sidedPlane.sigNum;
|
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.
|
* Construct a sided plane from a pair of vectors describing points, and including
|
||||||
*@param p point to evaluate
|
* origin, plus a point p which describes the side.
|
||||||
*@param A is the first in-plane point
|
*
|
||||||
*@param B is the second in-plane point
|
* @param p point to evaluate
|
||||||
*/
|
* @param A is the first in-plane point
|
||||||
public SidedPlane(Vector p, Vector A, Vector B) {
|
* @param B is the second in-plane point
|
||||||
super(A,B);
|
*/
|
||||||
sigNum = Math.signum(evaluate(p));
|
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.
|
* Construct a sided plane from a point and a Z coordinate.
|
||||||
*@param height is the Z coordinate of the plane.
|
*
|
||||||
*/
|
* @param p point to evaluate.
|
||||||
public SidedPlane(Vector p, double height) {
|
* @param height is the Z coordinate of the plane.
|
||||||
super(height);
|
*/
|
||||||
sigNum = Math.signum(evaluate(p));
|
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.
|
* Construct a sided vertical plane from a point and specified x and y coordinates.
|
||||||
*@param x is the specified x.
|
*
|
||||||
*@param y is the specified y.
|
* @param p point to evaluate.
|
||||||
*/
|
* @param x is the specified x.
|
||||||
public SidedPlane(Vector p, double x, double y) {
|
* @param y is the specified y.
|
||||||
super(x,y);
|
*/
|
||||||
sigNum = Math.signum(evaluate(p));
|
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.
|
* Construct a sided plane with a normal vector and offset.
|
||||||
*@param v is the normal vector.
|
*
|
||||||
*@param D is the origin offset for the plan.
|
* @param p point to evaluate.
|
||||||
*/
|
* @param v is the normal vector.
|
||||||
public SidedPlane(Vector p, Vector v, double D) {
|
* @param D is the origin offset for the plan.
|
||||||
super(v,D);
|
*/
|
||||||
sigNum = Math.signum(evaluate(p));
|
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.
|
* Check if a point is within this shape.
|
||||||
*@return true if the point is within this shape
|
*
|
||||||
*/
|
* @param point is the point to check.
|
||||||
@Override
|
* @return true if the point is within this shape
|
||||||
public boolean isWithin(Vector point)
|
*/
|
||||||
{
|
@Override
|
||||||
double evalResult = evaluate(point);
|
public boolean isWithin(Vector point) {
|
||||||
if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
|
double evalResult = evaluate(point);
|
||||||
return true;
|
if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
|
||||||
double sigNum = Math.signum(evalResult);
|
return true;
|
||||||
return sigNum == this.sigNum;
|
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.
|
* Check if a point is within this shape.
|
||||||
*@param y is y coordinate of point to check.
|
*
|
||||||
*@param z is z coordinate of point to check.
|
* @param x is x coordinate of point to check.
|
||||||
*@return true if the point is within this shape
|
* @param y is y coordinate of point to check.
|
||||||
*/
|
* @param z is z coordinate of point to check.
|
||||||
@Override
|
* @return true if the point is within this shape
|
||||||
public boolean isWithin(double x, double y, double z)
|
*/
|
||||||
{
|
@Override
|
||||||
double evalResult = evaluate(x,y,z);
|
public boolean isWithin(double x, double y, double z) {
|
||||||
if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
|
double evalResult = evaluate(x, y, z);
|
||||||
return true;
|
if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
|
||||||
double sigNum = Math.signum(evalResult);
|
return true;
|
||||||
return sigNum == this.sigNum;
|
double sigNum = Math.signum(evalResult);
|
||||||
}
|
return sigNum == this.sigNum;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
@Override
|
||||||
return "[A="+x+", B="+y+", C="+z+", D="+D+", side="+sigNum+"]";
|
public String toString() {
|
||||||
}
|
return "[A=" + x + ", B=" + y + ", C=" + z + ", D=" + D + ", side=" + sigNum + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,24 +17,24 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** Static methods globally useful for 3d geometric work.
|
/**
|
||||||
|
* Static methods globally useful for 3d geometric work.
|
||||||
*/
|
*/
|
||||||
public class Tools
|
public class Tools {
|
||||||
{
|
private Tools() {
|
||||||
private Tools()
|
}
|
||||||
{
|
|
||||||
}
|
/**
|
||||||
|
* Java acos yields a NAN if you take an arc-cos of an
|
||||||
/** 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
|
||||||
* angle that's just a tiny bit greater than 1.0, so
|
* here's a more resilient version.
|
||||||
* here's a more resilient version.
|
*/
|
||||||
*/
|
public static double safeAcos(double value) {
|
||||||
public static double safeAcos(double value) {
|
if (value > 1.0)
|
||||||
if (value > 1.0)
|
value = 1.0;
|
||||||
value = 1.0;
|
else if (value < -1.0)
|
||||||
else if (value < -1.0)
|
value = -1.0;
|
||||||
value = -1.0;
|
return Math.acos(value);
|
||||||
return Math.acos(value);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -17,270 +17,309 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** A 3d vector in space, not necessarily
|
/**
|
||||||
* going through the origin. */
|
* A 3d vector in space, not necessarily
|
||||||
public class Vector
|
* going through the origin.
|
||||||
{
|
*/
|
||||||
/** Values that are all considered to be essentially zero have a magnitude
|
public class Vector {
|
||||||
* less than this. */
|
/**
|
||||||
public static final double MINIMUM_RESOLUTION = 1e-12;
|
* Values that are all considered to be essentially zero have a magnitude
|
||||||
/** For squared quantities, the bound is squared too.
|
* less than this.
|
||||||
*/
|
*/
|
||||||
public static final double MINIMUM_RESOLUTION_SQUARED = MINIMUM_RESOLUTION * MINIMUM_RESOLUTION;
|
public static final double MINIMUM_RESOLUTION = 1e-12;
|
||||||
|
/**
|
||||||
public final double x;
|
* For squared quantities, the bound is squared too.
|
||||||
public final double y;
|
*/
|
||||||
public final double z;
|
public static final double MINIMUM_RESOLUTION_SQUARED = MINIMUM_RESOLUTION * MINIMUM_RESOLUTION;
|
||||||
|
|
||||||
/** Construct from (U.S.) x,y,z coordinates.
|
public final double x;
|
||||||
*/
|
public final double y;
|
||||||
public Vector(double x, double y, double z)
|
public final 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
|
|
||||||
|
|
||||||
this(A.y * B.z - A.z * B.y,
|
/**
|
||||||
A.z * B.x - A.x * B.z,
|
* Construct from (U.S.) x,y,z coordinates.
|
||||||
A.x * B.y - A.y * B.x);
|
*/
|
||||||
}
|
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
|
* Construct a vector that is perpendicular to
|
||||||
* a magnitude of zero.
|
* two other (non-zero) vectors. If the vectors are parallel,
|
||||||
*/
|
* the result vector will have magnitude 0.
|
||||||
public Vector normalize() {
|
*
|
||||||
double denom = magnitude();
|
* @param A is the first vector
|
||||||
if (denom < MINIMUM_RESOLUTION)
|
* @param B is the second
|
||||||
// Degenerate, can't normalize
|
*/
|
||||||
return null;
|
public Vector(final Vector A, final Vector B) {
|
||||||
double normFactor = 1.0/denom;
|
// x = u2v3 - u3v2
|
||||||
return new Vector(x*normFactor,y*normFactor,z*normFactor);
|
// y = u3v1 - u1v3
|
||||||
}
|
// z = u1v2 - u2v1
|
||||||
|
|
||||||
/** 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Do a dot product.
|
this(A.y * B.z - A.z * B.y,
|
||||||
*@param x is the x value of the vector to multiply.
|
A.z * B.x - A.x * B.z,
|
||||||
*@param y is the y value of the vector to multiply.
|
A.x * B.y - A.y * B.x);
|
||||||
*@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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Translate vector.
|
/**
|
||||||
*/
|
* Compute a normalized unit vector based on the current vector.
|
||||||
public Vector translate(final double xOffset, final double yOffset, final double zOffset) {
|
*
|
||||||
return new Vector(x - xOffset, y - yOffset, z - zOffset);
|
* @return the normalized vector, or null if the current vector has
|
||||||
}
|
* a magnitude of zero.
|
||||||
|
*/
|
||||||
/** Rotate vector counter-clockwise in x-y by an angle.
|
public Vector normalize() {
|
||||||
*/
|
double denom = magnitude();
|
||||||
public Vector rotateXY(final double angle) {
|
if (denom < MINIMUM_RESOLUTION)
|
||||||
return rotateXY(Math.sin(angle),Math.cos(angle));
|
// Degenerate, can't normalize
|
||||||
}
|
return null;
|
||||||
|
double normFactor = 1.0 / denom;
|
||||||
/** Rotate vector counter-clockwise in x-y by an angle, expressed as sin and cos.
|
return new Vector(x * normFactor, y * normFactor, z * normFactor);
|
||||||
*/
|
}
|
||||||
public Vector rotateXY(final double sinAngle, final double cosAngle) {
|
|
||||||
return new Vector(x * cosAngle - y * sinAngle, x * sinAngle + y * cosAngle, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Rotate vector counter-clockwise in x-z by an angle.
|
/**
|
||||||
*/
|
* Do a dot product.
|
||||||
public Vector rotateXZ(final double angle) {
|
*
|
||||||
return rotateXZ(Math.sin(angle),Math.cos(angle));
|
* @param v is the vector to multiply.
|
||||||
}
|
* @return the result.
|
||||||
|
*/
|
||||||
/** Rotate vector counter-clockwise in x-z by an angle, expressed as sin and cos.
|
public double dotProduct(final Vector v) {
|
||||||
*/
|
return this.x * v.x + this.y * v.y + this.z * v.z;
|
||||||
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.
|
/**
|
||||||
*/
|
* Do a dot product.
|
||||||
public Vector rotateZY(final double angle) {
|
*
|
||||||
return rotateZY(Math.sin(angle),Math.cos(angle));
|
* @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.
|
||||||
/** Rotate vector counter-clockwise in z-y by an angle, expressed as sin and cos.
|
* @return the result.
|
||||||
*/
|
*/
|
||||||
public Vector rotateZY(final double sinAngle, final double cosAngle) {
|
public double dotProduct(final double x, final double y, final double z) {
|
||||||
return new Vector(x, z * sinAngle + y * cosAngle, z * cosAngle - y * sinAngle);
|
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.
|
* Determine if this vector, taken from the origin,
|
||||||
* Monotonically increasing for arc distances up to PI.
|
* describes a point within a set of planes.
|
||||||
*@param v is the vector to compute a distance to.
|
*
|
||||||
*@return the square of the linear distance.
|
* @param bounds is the first part of the set of planes.
|
||||||
*/
|
* @param moreBounds is the second part of the set of planes.
|
||||||
public double linearDistanceSquared(final Vector v) {
|
* @return true if the point is within the bounds.
|
||||||
double deltaX = this.x - v.x;
|
*/
|
||||||
double deltaY = this.y - v.y;
|
public boolean isWithin(final Membership[] bounds, final Membership[] moreBounds) {
|
||||||
double deltaZ = this.z - v.z;
|
// Return true if the point described is within all provided bounds
|
||||||
return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
|
for (Membership bound : bounds) {
|
||||||
|
if (bound != null && !bound.isWithin(this))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
for (Membership bound : moreBounds) {
|
||||||
/** Compute the square of a straight-line distance to a point described by the
|
if (bound != null && !bound.isWithin(this))
|
||||||
* vector taken from the origin.
|
return false;
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Compute the straight-line distance to a point described by the
|
/**
|
||||||
* vector taken from the origin.
|
* Translate vector.
|
||||||
* Monotonically increasing for arc distances up to PI.
|
*/
|
||||||
*@param v is the vector to compute a distance to.
|
public Vector translate(final double xOffset, final double yOffset, final double zOffset) {
|
||||||
*@return the linear distance.
|
return new Vector(x - xOffset, y - yOffset, z - zOffset);
|
||||||
*/
|
}
|
||||||
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.
|
* Rotate vector counter-clockwise in x-y by an angle.
|
||||||
* Monotonically increasing for arc distances up to PI/2.
|
*/
|
||||||
*@param x is the x part of the vector to compute a distance to.
|
public Vector rotateXY(final double angle) {
|
||||||
*@param y is the y part of the vector to compute a distance to.
|
return rotateXY(Math.sin(angle), Math.cos(angle));
|
||||||
*@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.
|
* Rotate vector counter-clockwise in x-y by an angle, expressed as sin and cos.
|
||||||
* Monotonically increasing for arc distances up to PI/2.
|
*/
|
||||||
*@param x is the x part of the vector to compute a distance to.
|
public Vector rotateXY(final double sinAngle, final double cosAngle) {
|
||||||
*@param y is the y part of the vector to compute a distance to.
|
return new Vector(x * cosAngle - y * sinAngle, x * sinAngle + y * cosAngle, z);
|
||||||
*@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() {
|
* Rotate vector counter-clockwise in x-z by an angle.
|
||||||
int result;
|
*/
|
||||||
long temp;
|
public Vector rotateXZ(final double angle) {
|
||||||
temp = Double.doubleToLongBits(x);
|
return rotateXZ(Math.sin(angle), Math.cos(angle));
|
||||||
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() {
|
* Rotate vector counter-clockwise in x-z by an angle, expressed as sin and cos.
|
||||||
return "[X="+x+", Y="+y+", Z="+z+"]";
|
*/
|
||||||
}
|
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 + "]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
/** Shapes implemented using 3D planar geometry. */
|
/**
|
||||||
|
* Shapes implemented using 3D planar geometry.
|
||||||
|
*/
|
||||||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
package org.apache.lucene.spatial.spatial4j.geo3d;
|
@ -78,24 +78,8 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure() throws IOException {
|
public void testFailure1() throws IOException {
|
||||||
setupStrategy();
|
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<GeoPoint> points = new ArrayList<GeoPoint>();
|
final List<GeoPoint> points = new ArrayList<GeoPoint>();
|
||||||
points.add(new GeoPoint(18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS));
|
points.add(new GeoPoint(18 * DEGREES_TO_RADIANS, -27 * DEGREES_TO_RADIANS));
|
||||||
points.add(new GeoPoint(-57 * DEGREES_TO_RADIANS, 146 * 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
|
@Test
|
||||||
@Repeat(iterations = 2000)
|
@Repeat(iterations = 10)
|
||||||
//@Seed("B808B88D6F8E285C")
|
|
||||||
public void testOperations() throws IOException {
|
public void testOperations() throws IOException {
|
||||||
setupStrategy();
|
setupStrategy();
|
||||||
|
|
||||||
|
@ -54,26 +54,7 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTest {
|
|||||||
protected final static double RADIANS_PER_DEGREE = Math.PI/180.0;
|
protected final static double RADIANS_PER_DEGREE = Math.PI/180.0;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFailure() {
|
public void testFailure1() {
|
||||||
/*
|
|
||||||
[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)
|
|
||||||
*/
|
|
||||||
final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(88 * RADIANS_PER_DEGREE, 30 * RADIANS_PER_DEGREE, -30 * RADIANS_PER_DEGREE, 62 * RADIANS_PER_DEGREE);
|
final GeoBBox rect = GeoBBoxFactory.makeGeoBBox(88 * RADIANS_PER_DEGREE, 30 * RADIANS_PER_DEGREE, -30 * RADIANS_PER_DEGREE, 62 * RADIANS_PER_DEGREE);
|
||||||
final List<GeoPoint> points = new ArrayList<GeoPoint>();
|
final List<GeoPoint> points = new ArrayList<GeoPoint>();
|
||||||
points.add(new GeoPoint(66.2465299717 * RADIANS_PER_DEGREE, -29.1786158537 * RADIANS_PER_DEGREE));
|
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 GeoShape path = GeoPolygonFactory.makeGeoPolygon(points,0);
|
||||||
|
|
||||||
final GeoPoint point = new GeoPoint(34.2730264413182 * RADIANS_PER_DEGREE, 82.75500168892472 * RADIANS_PER_DEGREE);
|
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"
|
// Apparently the rectangle thinks the polygon is completely within it... "shape inside rectangle"
|
||||||
assertTrue(GeoArea.WITHIN == rect.getRelationship(path));
|
assertTrue(GeoArea.WITHIN == rect.getRelationship(path));
|
||||||
|
|
||||||
@ -135,7 +103,6 @@ public class Geo3dShapeRectRelationTest extends RandomizedShapeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
//@Seed("FAD1BAB12B6DCCFE")
|
|
||||||
public void testGeoCircleRect() {
|
public void testGeoCircleRect() {
|
||||||
new RectIntersectionTestHelper<Geo3dShape>(ctx) {
|
new RectIntersectionTestHelper<Geo3dShape>(ctx) {
|
||||||
|
|
||||||
|
@ -54,8 +54,9 @@ public abstract class RandomizedShapeTest extends RandomizedTest {
|
|||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void checkShapesImplementEquals( Class[] classes ) {
|
@SuppressWarnings("unchecked")
|
||||||
for( Class clazz : classes ) {
|
public static void checkShapesImplementEquals( Class<?>[] classes ) {
|
||||||
|
for( Class<?> clazz : classes ) {
|
||||||
try {
|
try {
|
||||||
clazz.getDeclaredMethod( "equals", Object.class );
|
clazz.getDeclaredMethod( "equals", Object.class );
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -28,241 +28,242 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
public class GeoBBoxTest {
|
public class GeoBBoxTest {
|
||||||
|
|
||||||
protected final double DEGREES_TO_RADIANS = Math.PI/180.0;
|
protected final double DEGREES_TO_RADIANS = Math.PI / 180.0;
|
||||||
@Test
|
|
||||||
public void testBBoxDegenerate() {
|
|
||||||
GeoBBox box;
|
|
||||||
GeoConvexPolygon cp;
|
|
||||||
int relationship;
|
|
||||||
List<GeoPoint> points = new ArrayList<GeoPoint>();
|
|
||||||
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));
|
|
||||||
|
|
||||||
}
|
@Test
|
||||||
|
public void testBBoxDegenerate() {
|
||||||
|
GeoBBox box;
|
||||||
|
GeoConvexPolygon cp;
|
||||||
|
int relationship;
|
||||||
|
List<GeoPoint> points = new ArrayList<GeoPoint>();
|
||||||
|
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
|
@Test
|
||||||
public void testBBoxExpand() {
|
public void testBBoxPointWithin() {
|
||||||
GeoBBox box;
|
GeoBBox box;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
// Standard normal Rect box, not crossing dateline
|
// Standard normal Rect box, not crossing dateline
|
||||||
box = GeoBBoxFactory.makeGeoBBox(0.0,-Math.PI * 0.25, -1.0, 1.0);
|
box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0);
|
||||||
box = box.expand(0.1);
|
gp = new GeoPoint(-0.1, 0.0);
|
||||||
gp = new GeoPoint(0.05,0.0);
|
assertTrue(box.isWithin(gp));
|
||||||
assertTrue(box.isWithin(gp));
|
gp = new GeoPoint(0.1, 0.0);
|
||||||
gp = new GeoPoint(0.15,0.0);
|
assertFalse(box.isWithin(gp));
|
||||||
assertFalse(box.isWithin(gp));
|
gp = new GeoPoint(-Math.PI * 0.5, 0.0);
|
||||||
gp = new GeoPoint(-Math.PI * 0.25 - 0.05,0.0);
|
assertFalse(box.isWithin(gp));
|
||||||
assertTrue(box.isWithin(gp));
|
gp = new GeoPoint(-0.1, 1.1);
|
||||||
gp = new GeoPoint(-Math.PI * 0.25 - 0.15,0.0);
|
assertFalse(box.isWithin(gp));
|
||||||
assertFalse(box.isWithin(gp));
|
gp = new GeoPoint(-0.1, -1.1);
|
||||||
gp = new GeoPoint(-0.1,-1.05);
|
assertFalse(box.isWithin(gp));
|
||||||
assertTrue(box.isWithin(gp));
|
// Standard normal Rect box, crossing dateline
|
||||||
gp = new GeoPoint(-0.1,-1.15);
|
box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0);
|
||||||
assertFalse(box.isWithin(gp));
|
gp = new GeoPoint(-0.1, -Math.PI);
|
||||||
gp = new GeoPoint(-0.1,1.05);
|
assertTrue(box.isWithin(gp));
|
||||||
assertTrue(box.isWithin(gp));
|
gp = new GeoPoint(0.1, -Math.PI);
|
||||||
gp = new GeoPoint(-0.1,1.15);
|
assertFalse(box.isWithin(gp));
|
||||||
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);
|
@Test
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
public void testBBoxExpand() {
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
GeoBBox box;
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
GeoPoint gp;
|
||||||
assertEquals(-1.0,b.getLeftLongitude(),0.000001);
|
// Standard normal Rect box, not crossing dateline
|
||||||
assertEquals(1.0,b.getRightLongitude(),0.000001);
|
box = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0);
|
||||||
assertEquals(-Math.PI * 0.25,b.getMinLatitude(),0.000001);
|
box = box.expand(0.1);
|
||||||
assertEquals(0.0,b.getMaxLatitude(),0.000001);
|
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);
|
c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -1.0, 1.0);
|
||||||
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(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);
|
c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, 1.0, -1.0);
|
||||||
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(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);
|
c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0);
|
||||||
assertTrue(b.checkNoLongitudeBound());
|
|
||||||
assertTrue(b.checkNoTopLatitudeBound());
|
|
||||||
assertTrue(b.checkNoBottomLatitudeBound());
|
|
||||||
//assertEquals(1.0,b.getLeftLongitude(),0.000001);
|
|
||||||
//assertEquals(-1.0,b.getRightLongitude(),0.000001);
|
|
||||||
|
|
||||||
// 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);
|
b = c.getBounds(null);
|
||||||
assertTrue(b.checkNoLongitudeBound());
|
assertTrue(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertTrue(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertTrue(b.checkNoBottomLatitudeBound());
|
||||||
//assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001);
|
//assertEquals(1.0,b.getLeftLongitude(),0.000001);
|
||||||
//assertEquals(Math.PI-0.1,b.getRightLongitude(),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, Math.PI-0.1, -Math.PI+0.1);
|
// Check wide variants of rectangle and longitude slice
|
||||||
|
|
||||||
b = c.getBounds(null);
|
c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, -Math.PI + 0.1, Math.PI - 0.1);
|
||||||
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(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);
|
c = GeoBBoxFactory.makeGeoBBox(0.0, -Math.PI * 0.25, Math.PI - 0.1, -Math.PI + 0.1);
|
||||||
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(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);
|
c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI + 0.1, Math.PI - 0.1);
|
||||||
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);
|
|
||||||
|
|
||||||
b = c.getBounds(null);
|
b = c.getBounds(null);
|
||||||
assertTrue(b.checkNoLongitudeBound());
|
assertTrue(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertTrue(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertTrue(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(-1.0,b.getMinLatitude(),0.000001);
|
//assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001);
|
||||||
assertEquals(1.0,b.getMaxLatitude(),0.000001);
|
//assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001);
|
||||||
|
|
||||||
// Now, combine a few things to test the bounds object
|
c = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, Math.PI - 0.1, -Math.PI + 0.1);
|
||||||
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);
|
|
||||||
|
|
||||||
b = new Bounds();
|
b = c.getBounds(null);
|
||||||
b = c1.getBounds(b);
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
b = c2.getBounds(b);
|
assertTrue(b.checkNoTopLatitudeBound());
|
||||||
assertTrue(b.checkNoLongitudeBound());
|
assertTrue(b.checkNoBottomLatitudeBound());
|
||||||
assertTrue(b.checkNoTopLatitudeBound());
|
assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001);
|
||||||
assertTrue(b.checkNoBottomLatitudeBound());
|
assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001);
|
||||||
|
|
||||||
c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0);
|
// Check latitude zone
|
||||||
c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI*0.5);
|
c = GeoBBoxFactory.makeGeoBBox(1.0, -1.0, -Math.PI, Math.PI);
|
||||||
|
|
||||||
b = new Bounds();
|
b = c.getBounds(null);
|
||||||
b = c1.getBounds(b);
|
assertTrue(b.checkNoLongitudeBound());
|
||||||
b = c2.getBounds(b);
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertTrue(b.checkNoLongitudeBound());
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertTrue(b.checkNoTopLatitudeBound());
|
assertEquals(-1.0, b.getMinLatitude(), 0.000001);
|
||||||
assertTrue(b.checkNoBottomLatitudeBound());
|
assertEquals(1.0, b.getMaxLatitude(), 0.000001);
|
||||||
//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);
|
// Now, combine a few things to test the bounds object
|
||||||
c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI);
|
GeoBBox c1;
|
||||||
|
GeoBBox c2;
|
||||||
|
|
||||||
b = new Bounds();
|
c1 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0);
|
||||||
b = c1.getBounds(b);
|
c2 = GeoBBoxFactory.makeGeoBBox(Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI);
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,201 +17,203 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
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 {
|
public class GeoCircleTest {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCircleDistance() {
|
public void testCircleDistance() {
|
||||||
GeoCircle c;
|
GeoCircle c;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
c = new GeoCircle(0.0,-0.5,0.1);
|
c = new GeoCircle(0.0, -0.5, 0.1);
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertEquals(Double.MAX_VALUE, c.computeArcDistance(gp), 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.computeLinearDistance(gp), 0.0);
|
||||||
assertEquals(Double.MAX_VALUE, c.computeNormalDistance(gp), 0.0);
|
assertEquals(Double.MAX_VALUE, c.computeNormalDistance(gp), 0.0);
|
||||||
gp = new GeoPoint(0.0,-0.5);
|
gp = new GeoPoint(0.0, -0.5);
|
||||||
assertEquals(0.0, c.computeArcDistance(gp), 0.000001);
|
assertEquals(0.0, c.computeArcDistance(gp), 0.000001);
|
||||||
assertEquals(0.0, c.computeLinearDistance(gp), 0.000001);
|
assertEquals(0.0, c.computeLinearDistance(gp), 0.000001);
|
||||||
assertEquals(0.0, c.computeNormalDistance(gp), 0.000001);
|
assertEquals(0.0, c.computeNormalDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(0.05,-0.5);
|
gp = new GeoPoint(0.05, -0.5);
|
||||||
assertEquals(0.05, c.computeArcDistance(gp), 0.000001);
|
assertEquals(0.05, c.computeArcDistance(gp), 0.000001);
|
||||||
assertEquals(0.049995, c.computeLinearDistance(gp), 0.000001);
|
assertEquals(0.049995, c.computeLinearDistance(gp), 0.000001);
|
||||||
assertEquals(0.049979, c.computeNormalDistance(gp), 0.000001);
|
assertEquals(0.049979, c.computeNormalDistance(gp), 0.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCirclePointWithin() {
|
public void testCirclePointWithin() {
|
||||||
GeoCircle c;
|
GeoCircle c;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
c = new GeoCircle(0.0,-0.5,0.1);
|
c = new GeoCircle(0.0, -0.5, 0.1);
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.5);
|
gp = new GeoPoint(0.0, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.55);
|
gp = new GeoPoint(0.0, -0.55);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.45);
|
gp = new GeoPoint(0.0, -0.45);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(Math.PI * 0.5,0.0);
|
gp = new GeoPoint(Math.PI * 0.5, 0.0);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,Math.PI);
|
gp = new GeoPoint(0.0, Math.PI);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCircleBounds() {
|
public void testCircleBounds() {
|
||||||
GeoCircle c;
|
GeoCircle c;
|
||||||
Bounds b;
|
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);
|
// Vertical circle cases
|
||||||
b = c.getBounds(null);
|
c = new GeoCircle(0.0, -0.5, 0.1);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
b = c.getBounds(null);
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertEquals(0.11,b.getMaxLatitude(),0.000001);
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(-0.09,b.getMinLatitude(),0.000001);
|
assertEquals(-0.6, b.getLeftLongitude(), 0.000001);
|
||||||
assertEquals(Math.PI-0.1,b.getLeftLongitude(),0.00001);
|
assertEquals(-0.4, b.getRightLongitude(), 0.000001);
|
||||||
assertEquals(-Math.PI+0.1,b.getRightLongitude(),0.00001);
|
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);
|
// Now do a somewhat tilted plane, facing different directions.
|
||||||
b = c.getBounds(null);
|
c = new GeoCircle(0.01, 0.0, 0.1);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
b = c.getBounds(null);
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertEquals(0.11,b.getMaxLatitude(),0.000001);
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(-0.09,b.getMinLatitude(),0.000001);
|
assertEquals(0.11, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(Math.PI * 0.5 - 0.1,b.getLeftLongitude(),0.00001);
|
assertEquals(-0.09, b.getMinLatitude(), 0.000001);
|
||||||
assertEquals(Math.PI * 0.5 + 0.1,b.getRightLongitude(),0.00001);
|
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);
|
c = new GeoCircle(0.01, Math.PI, 0.1);
|
||||||
b = c.getBounds(null);
|
b = c.getBounds(null);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(0.11,b.getMaxLatitude(),0.000001);
|
assertEquals(0.11, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(-0.09,b.getMinLatitude(),0.000001);
|
assertEquals(-0.09, b.getMinLatitude(), 0.000001);
|
||||||
assertEquals(-Math.PI * 0.5 - 0.1,b.getLeftLongitude(),0.00001);
|
assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.00001);
|
||||||
assertEquals(-Math.PI * 0.5 + 0.1,b.getRightLongitude(),0.00001);
|
assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.00001);
|
||||||
|
|
||||||
// Slightly tilted, PI/4 direction.
|
c = new GeoCircle(0.01, Math.PI * 0.5, 0.1);
|
||||||
c = new GeoCircle(0.01,Math.PI * 0.25,0.1);
|
b = c.getBounds(null);
|
||||||
b = c.getBounds(null);
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertEquals(0.11, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(0.11,b.getMaxLatitude(),0.000001);
|
assertEquals(-0.09, b.getMinLatitude(), 0.000001);
|
||||||
assertEquals(-0.09,b.getMinLatitude(),0.000001);
|
assertEquals(Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001);
|
||||||
assertEquals(Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001);
|
assertEquals(Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001);
|
||||||
assertEquals(Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001);
|
|
||||||
|
|
||||||
c = new GeoCircle(0.01,-Math.PI * 0.25,0.1);
|
c = new GeoCircle(0.01, -Math.PI * 0.5, 0.1);
|
||||||
b = c.getBounds(null);
|
b = c.getBounds(null);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(0.11,b.getMaxLatitude(),0.000001);
|
assertEquals(0.11, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(-0.09,b.getMinLatitude(),0.000001);
|
assertEquals(-0.09, b.getMinLatitude(), 0.000001);
|
||||||
assertEquals(-Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001);
|
assertEquals(-Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001);
|
||||||
assertEquals(-Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001);
|
assertEquals(-Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001);
|
||||||
|
|
||||||
c = new GeoCircle(-0.01,Math.PI * 0.25,0.1);
|
// Slightly tilted, PI/4 direction.
|
||||||
b = c.getBounds(null);
|
c = new GeoCircle(0.01, Math.PI * 0.25, 0.1);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
b = c.getBounds(null);
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertEquals(0.09,b.getMaxLatitude(),0.000001);
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(-0.11,b.getMinLatitude(),0.000001);
|
assertEquals(0.11, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(Math.PI * 0.25 - 0.1,b.getLeftLongitude(),0.00001);
|
assertEquals(-0.09, b.getMinLatitude(), 0.000001);
|
||||||
assertEquals(Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001);
|
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);
|
c = new GeoCircle(0.01, -Math.PI * 0.25, 0.1);
|
||||||
b = c.getBounds(null);
|
b = c.getBounds(null);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertEquals(0.09,b.getMaxLatitude(),0.000001);
|
assertEquals(0.11, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(-0.11,b.getMinLatitude(),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.getLeftLongitude(), 0.00001);
|
||||||
assertEquals(-Math.PI * 0.25 + 0.1,b.getRightLongitude(),0.00001);
|
assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001);
|
||||||
|
|
||||||
// Now do a somewhat tilted plane.
|
c = new GeoCircle(-0.01, Math.PI * 0.25, 0.1);
|
||||||
c = new GeoCircle(0.01,-0.5,0.1);
|
b = c.getBounds(null);
|
||||||
b = c.getBounds(null);
|
assertFalse(b.checkNoLongitudeBound());
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
assertFalse(b.checkNoTopLatitudeBound());
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
assertFalse(b.checkNoBottomLatitudeBound());
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
assertEquals(0.09, b.getMaxLatitude(), 0.000001);
|
||||||
assertEquals(0.11,b.getMaxLatitude(),0.000001);
|
assertEquals(-0.11, b.getMinLatitude(), 0.000001);
|
||||||
assertEquals(-0.09,b.getMinLatitude(),0.000001);
|
assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001);
|
||||||
assertEquals(-0.6,b.getLeftLongitude(),0.00001);
|
assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 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);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,63 +26,63 @@ import static org.junit.Assert.assertTrue;
|
|||||||
public class GeoConvexPolygonTest {
|
public class GeoConvexPolygonTest {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPolygonPointWithin() {
|
public void testPolygonPointWithin() {
|
||||||
GeoConvexPolygon c;
|
GeoConvexPolygon c;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
c = new GeoConvexPolygon(-0.1,-0.5);
|
c = new GeoConvexPolygon(-0.1, -0.5);
|
||||||
c.addPoint(0.0,-0.6,false);
|
c.addPoint(0.0, -0.6, false);
|
||||||
c.addPoint(0.1,-0.5,false);
|
c.addPoint(0.1, -0.5, false);
|
||||||
c.addPoint(0.0,-0.4,false);
|
c.addPoint(0.0, -0.4, false);
|
||||||
c.donePoints(false);
|
c.donePoints(false);
|
||||||
// Sample some points within
|
// Sample some points within
|
||||||
gp = new GeoPoint(0.0,-0.5);
|
gp = new GeoPoint(0.0, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.55);
|
gp = new GeoPoint(0.0, -0.55);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.45);
|
gp = new GeoPoint(0.0, -0.45);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(-0.05,-0.5);
|
gp = new GeoPoint(-0.05, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.05,-0.5);
|
gp = new GeoPoint(0.05, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
// Sample some nearby points outside
|
// Sample some nearby points outside
|
||||||
gp = new GeoPoint(0.0,-0.65);
|
gp = new GeoPoint(0.0, -0.65);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.35);
|
gp = new GeoPoint(0.0, -0.35);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(-0.15,-0.5);
|
gp = new GeoPoint(-0.15, -0.5);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.15,-0.5);
|
gp = new GeoPoint(0.15, -0.5);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
// Random points outside
|
// Random points outside
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(Math.PI * 0.5,0.0);
|
gp = new GeoPoint(Math.PI * 0.5, 0.0);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,Math.PI);
|
gp = new GeoPoint(0.0, Math.PI);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPolygonBounds() {
|
public void testPolygonBounds() {
|
||||||
GeoConvexPolygon c;
|
GeoConvexPolygon c;
|
||||||
Bounds b;
|
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);
|
|
||||||
|
|
||||||
b = c.getBounds(null);
|
c = new GeoConvexPolygon(-0.1, -0.5);
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
c.addPoint(0.0, -0.6, false);
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
c.addPoint(0.1, -0.5, false);
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
c.addPoint(0.0, -0.4, false);
|
||||||
assertEquals(-0.6,b.getLeftLongitude(),0.000001);
|
c.donePoints(false);
|
||||||
assertEquals(-0.4,b.getRightLongitude(),0.000001);
|
|
||||||
assertEquals(-0.1,b.getMinLatitude(),0.000001);
|
b = c.getBounds(null);
|
||||||
assertEquals(0.1,b.getMaxLatitude(),0.000001);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,160 +25,160 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
public class GeoPathTest {
|
public class GeoPathTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPathDistance() {
|
public void testPathDistance() {
|
||||||
// Start with a really simple case
|
// Start with a really simple case
|
||||||
GeoPath p;
|
GeoPath p;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
p = new GeoPath(0.1);
|
p = new GeoPath(0.1);
|
||||||
p.addPoint(0.0,0.0);
|
p.addPoint(0.0, 0.0);
|
||||||
p.addPoint(0.0,0.1);
|
p.addPoint(0.0, 0.1);
|
||||||
p.addPoint(0.0,0.2);
|
p.addPoint(0.0, 0.2);
|
||||||
p.done();
|
p.done();
|
||||||
gp = new GeoPoint(Math.PI * 0.5,0.15);
|
gp = new GeoPoint(Math.PI * 0.5, 0.15);
|
||||||
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
||||||
gp = new GeoPoint(0.05,0.15);
|
gp = new GeoPoint(0.05, 0.15);
|
||||||
assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(0.0,0.12);
|
gp = new GeoPoint(0.0, 0.12);
|
||||||
assertEquals(0.12 + 0.0, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.12 + 0.0, p.computeArcDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(-0.15,0.05);
|
gp = new GeoPoint(-0.15, 0.05);
|
||||||
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.000001);
|
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(0.0,0.25);
|
gp = new GeoPoint(0.0, 0.25);
|
||||||
assertEquals(0.20 + 0.05, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.20 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(0.0,-0.05);
|
gp = new GeoPoint(0.0, -0.05);
|
||||||
assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||||
|
|
||||||
// Compute path distances now
|
// Compute path distances now
|
||||||
p = new GeoPath(0.1);
|
p = new GeoPath(0.1);
|
||||||
p.addPoint(0.0,0.0);
|
p.addPoint(0.0, 0.0);
|
||||||
p.addPoint(0.0,0.1);
|
p.addPoint(0.0, 0.1);
|
||||||
p.addPoint(0.0,0.2);
|
p.addPoint(0.0, 0.2);
|
||||||
p.done();
|
p.done();
|
||||||
gp = new GeoPoint(0.05,0.15);
|
gp = new GeoPoint(0.05, 0.15);
|
||||||
assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(0.0,0.12);
|
gp = new GeoPoint(0.0, 0.12);
|
||||||
assertEquals(0.12, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.12, p.computeArcDistance(gp), 0.000001);
|
||||||
|
|
||||||
// Now try a vertical path, and make sure distances are as expected
|
// Now try a vertical path, and make sure distances are as expected
|
||||||
p = new GeoPath(0.1);
|
p = new GeoPath(0.1);
|
||||||
p.addPoint(-Math.PI * 0.25,-0.5);
|
p.addPoint(-Math.PI * 0.25, -0.5);
|
||||||
p.addPoint(Math.PI * 0.25,-0.5);
|
p.addPoint(Math.PI * 0.25, -0.5);
|
||||||
p.done();
|
p.done();
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
||||||
gp = new GeoPoint(-0.1,-1.0);
|
gp = new GeoPoint(-0.1, -1.0);
|
||||||
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
||||||
gp = new GeoPoint(Math.PI*0.25+0.05,-0.5);
|
gp = new GeoPoint(Math.PI * 0.25 + 0.05, -0.5);
|
||||||
assertEquals(Math.PI * 0.5 + 0.05, p.computeArcDistance(gp), 0.000001);
|
assertEquals(Math.PI * 0.5 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||||
gp = new GeoPoint(-Math.PI*0.25-0.05,-0.5);
|
gp = new GeoPoint(-Math.PI * 0.25 - 0.05, -0.5);
|
||||||
assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001);
|
assertEquals(0.0 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPathPointWithin() {
|
public void testPathPointWithin() {
|
||||||
// Tests whether we can properly detect whether a point is within a path or not
|
// Tests whether we can properly detect whether a point is within a path or not
|
||||||
GeoPath p;
|
GeoPath p;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
p = new GeoPath(0.1);
|
p = new GeoPath(0.1);
|
||||||
// Build a diagonal path crossing the equator
|
// Build a diagonal path crossing the equator
|
||||||
p.addPoint(-0.2,-0.2);
|
p.addPoint(-0.2, -0.2);
|
||||||
p.addPoint(0.2,0.2);
|
p.addPoint(0.2, 0.2);
|
||||||
p.done();
|
p.done();
|
||||||
// Test points on the path
|
// Test points on the path
|
||||||
gp = new GeoPoint(-0.2,-0.2);
|
gp = new GeoPoint(-0.2, -0.2);
|
||||||
assertTrue(p.isWithin(gp));
|
assertTrue(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertTrue(p.isWithin(gp));
|
assertTrue(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.1,0.1);
|
gp = new GeoPoint(0.1, 0.1);
|
||||||
assertTrue(p.isWithin(gp));
|
assertTrue(p.isWithin(gp));
|
||||||
// Test points off the path
|
// Test points off the path
|
||||||
gp = new GeoPoint(-0.2,0.2);
|
gp = new GeoPoint(-0.2, 0.2);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
gp = new GeoPoint(-Math.PI*0.5,0.0);
|
gp = new GeoPoint(-Math.PI * 0.5, 0.0);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.2,-0.2);
|
gp = new GeoPoint(0.2, -0.2);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,Math.PI);
|
gp = new GeoPoint(0.0, Math.PI);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
// Repeat the test, but across the terminator
|
// Repeat the test, but across the terminator
|
||||||
p = new GeoPath(0.1);
|
p = new GeoPath(0.1);
|
||||||
// Build a diagonal path crossing the equator
|
// Build a diagonal path crossing the equator
|
||||||
p.addPoint(-0.2,Math.PI-0.2);
|
p.addPoint(-0.2, Math.PI - 0.2);
|
||||||
p.addPoint(0.2,-Math.PI+0.2);
|
p.addPoint(0.2, -Math.PI + 0.2);
|
||||||
// Test points on the path
|
// Test points on the path
|
||||||
gp = new GeoPoint(-0.2,Math.PI-0.2);
|
gp = new GeoPoint(-0.2, Math.PI - 0.2);
|
||||||
assertTrue(p.isWithin(gp));
|
assertTrue(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,Math.PI);
|
gp = new GeoPoint(0.0, Math.PI);
|
||||||
assertTrue(p.isWithin(gp));
|
assertTrue(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.1,-Math.PI+0.1);
|
gp = new GeoPoint(0.1, -Math.PI + 0.1);
|
||||||
assertTrue(p.isWithin(gp));
|
assertTrue(p.isWithin(gp));
|
||||||
// Test points off the path
|
// Test points off the path
|
||||||
gp = new GeoPoint(-0.2,-Math.PI+0.2);
|
gp = new GeoPoint(-0.2, -Math.PI + 0.2);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
gp = new GeoPoint(-Math.PI*0.5,0.0);
|
gp = new GeoPoint(-Math.PI * 0.5, 0.0);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.2,Math.PI-0.2);
|
gp = new GeoPoint(0.2, Math.PI - 0.2);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertFalse(p.isWithin(gp));
|
assertFalse(p.isWithin(gp));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetRelationship() {
|
public void testGetRelationship() {
|
||||||
GeoArea rect;
|
GeoArea rect;
|
||||||
GeoPath p;
|
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 = new GeoPath(0.1);
|
||||||
p.addPoint(-0.3,-0.3);
|
p.addPoint(-0.3, -0.3);
|
||||||
p.addPoint(0.3,0.3);
|
p.addPoint(0.3, 0.3);
|
||||||
p.done();
|
p.done();
|
||||||
// Easiest: The path is wholly contains the georect
|
// Easiest: The path is wholly contains the georect
|
||||||
rect = new GeoRectangle(0.05,-0.05,-0.05,0.05);
|
rect = new GeoRectangle(0.05, -0.05, -0.05, 0.05);
|
||||||
assertEquals(GeoArea.CONTAINS, rect.getRelationship(p));
|
assertEquals(GeoArea.CONTAINS, rect.getRelationship(p));
|
||||||
// Next easiest: Some endpoints of the rectangle are inside, and some are outside.
|
// Next easiest: Some endpoints of the rectangle are inside, and some are outside.
|
||||||
rect = new GeoRectangle(0.05,-0.05,-0.05,0.5);
|
rect = new GeoRectangle(0.05, -0.05, -0.05, 0.5);
|
||||||
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
||||||
// Now, all points are outside, but the figures intersect
|
// Now, all points are outside, but the figures intersect
|
||||||
rect = new GeoRectangle(0.05,-0.05,-0.5,0.5);
|
rect = new GeoRectangle(0.05, -0.05, -0.5, 0.5);
|
||||||
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
||||||
// Finally, all points are outside, and the figures *do not* intersect
|
// Finally, all points are outside, and the figures *do not* intersect
|
||||||
rect = new GeoRectangle(0.5,-0.5,-0.5,0.5);
|
rect = new GeoRectangle(0.5, -0.5, -0.5, 0.5);
|
||||||
assertEquals(GeoArea.WITHIN, rect.getRelationship(p));
|
assertEquals(GeoArea.WITHIN, rect.getRelationship(p));
|
||||||
// Check that segment edge overlap detection works
|
// Check that segment edge overlap detection works
|
||||||
rect = new GeoRectangle(0.1,0.0,-0.1,0.0);
|
rect = new GeoRectangle(0.1, 0.0, -0.1, 0.0);
|
||||||
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
||||||
rect = new GeoRectangle(0.2,0.1,-0.2,-0.1);
|
rect = new GeoRectangle(0.2, 0.1, -0.2, -0.1);
|
||||||
assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
|
assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
|
||||||
// Check if overlap at endpoints behaves as expected next
|
// Check if overlap at endpoints behaves as expected next
|
||||||
rect = new GeoRectangle(0.5,-0.5,-0.5,-0.35);
|
rect = new GeoRectangle(0.5, -0.5, -0.5, -0.35);
|
||||||
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
|
||||||
rect = new GeoRectangle(0.5,-0.5,-0.5,-0.45);
|
rect = new GeoRectangle(0.5, -0.5, -0.5, -0.45);
|
||||||
assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,65 +17,68 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
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 {
|
public class GeoPolygonTest {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPolygonPointWithin() {
|
public void testPolygonPointWithin() {
|
||||||
GeoMembershipShape c;
|
GeoMembershipShape c;
|
||||||
GeoPoint gp;
|
GeoPoint gp;
|
||||||
List<GeoPoint> points;
|
List<GeoPoint> points;
|
||||||
|
|
||||||
points = new ArrayList<GeoPoint>();
|
points = new ArrayList<GeoPoint>();
|
||||||
points.add(new GeoPoint(-0.1,-0.5));
|
points.add(new GeoPoint(-0.1, -0.5));
|
||||||
points.add(new GeoPoint(0.0,-0.6));
|
points.add(new GeoPoint(0.0, -0.6));
|
||||||
points.add(new GeoPoint(0.1,-0.5));
|
points.add(new GeoPoint(0.1, -0.5));
|
||||||
points.add(new GeoPoint(0.0,-0.4));
|
points.add(new GeoPoint(0.0, -0.4));
|
||||||
|
|
||||||
c = GeoPolygonFactory.makeGeoPolygon(points,0);
|
c = GeoPolygonFactory.makeGeoPolygon(points, 0);
|
||||||
// Sample some points within
|
// Sample some points within
|
||||||
gp = new GeoPoint(0.0,-0.5);
|
gp = new GeoPoint(0.0, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.55);
|
gp = new GeoPoint(0.0, -0.55);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.45);
|
gp = new GeoPoint(0.0, -0.45);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(-0.05,-0.5);
|
gp = new GeoPoint(-0.05, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.05,-0.5);
|
gp = new GeoPoint(0.05, -0.5);
|
||||||
assertTrue(c.isWithin(gp));
|
assertTrue(c.isWithin(gp));
|
||||||
// Sample some nearby points outside
|
// Sample some nearby points outside
|
||||||
gp = new GeoPoint(0.0,-0.65);
|
gp = new GeoPoint(0.0, -0.65);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,-0.35);
|
gp = new GeoPoint(0.0, -0.35);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(-0.15,-0.5);
|
gp = new GeoPoint(-0.15, -0.5);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.15,-0.5);
|
gp = new GeoPoint(0.15, -0.5);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
// Random points outside
|
// Random points outside
|
||||||
gp = new GeoPoint(0.0,0.0);
|
gp = new GeoPoint(0.0, 0.0);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(Math.PI * 0.5,0.0);
|
gp = new GeoPoint(Math.PI * 0.5, 0.0);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
gp = new GeoPoint(0.0,Math.PI);
|
gp = new GeoPoint(0.0, Math.PI);
|
||||||
assertFalse(c.isWithin(gp));
|
assertFalse(c.isWithin(gp));
|
||||||
|
|
||||||
points = new ArrayList<GeoPoint>();
|
points = new ArrayList<GeoPoint>();
|
||||||
points.add(new GeoPoint(-0.1,-0.5));
|
points.add(new GeoPoint(-0.1, -0.5));
|
||||||
points.add(new GeoPoint(-0.01,-0.6));
|
points.add(new GeoPoint(-0.01, -0.6));
|
||||||
points.add(new GeoPoint(-0.1,-0.7));
|
points.add(new GeoPoint(-0.1, -0.7));
|
||||||
points.add(new GeoPoint(0.0,-0.8));
|
points.add(new GeoPoint(0.0, -0.8));
|
||||||
points.add(new GeoPoint(0.1,-0.7));
|
points.add(new GeoPoint(0.1, -0.7));
|
||||||
points.add(new GeoPoint(0.01,-0.6));
|
points.add(new GeoPoint(0.01, -0.6));
|
||||||
points.add(new GeoPoint(0.1,-0.5));
|
points.add(new GeoPoint(0.1, -0.5));
|
||||||
points.add(new GeoPoint(0.0,-0.4));
|
points.add(new GeoPoint(0.0, -0.4));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
System.out.println("Points: ");
|
System.out.println("Points: ");
|
||||||
@ -83,60 +86,60 @@ public class GeoPolygonTest {
|
|||||||
System.out.println(" "+p);
|
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<GeoPoint> points;
|
|
||||||
|
|
||||||
points = new ArrayList<GeoPoint>();
|
|
||||||
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);
|
@Test
|
||||||
assertFalse(b.checkNoLongitudeBound());
|
public void testPolygonBounds() {
|
||||||
assertFalse(b.checkNoTopLatitudeBound());
|
GeoMembershipShape c;
|
||||||
assertFalse(b.checkNoBottomLatitudeBound());
|
Bounds b;
|
||||||
assertEquals(-0.6,b.getLeftLongitude(),0.000001);
|
List<GeoPoint> points;
|
||||||
assertEquals(-0.4,b.getRightLongitude(),0.000001);
|
|
||||||
assertEquals(-0.1,b.getMinLatitude(),0.000001);
|
points = new ArrayList<GeoPoint>();
|
||||||
assertEquals(0.1,b.getMaxLatitude(),0.000001);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,43 +22,44 @@ import org.junit.Test;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/** Test basic plane functionality.
|
/**
|
||||||
*/
|
* Test basic plane functionality.
|
||||||
|
*/
|
||||||
public class PlaneTest {
|
public class PlaneTest {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIdenticalPlanes() {
|
public void testIdenticalPlanes() {
|
||||||
final GeoPoint p = new GeoPoint(0.123,-0.456);
|
final GeoPoint p = new GeoPoint(0.123, -0.456);
|
||||||
final Plane plane1 = new Plane(p,0.0);
|
final Plane plane1 = new Plane(p, 0.0);
|
||||||
final Plane plane2 = new Plane(p,0.0);
|
final Plane plane2 = new Plane(p, 0.0);
|
||||||
assertTrue(plane1.isNumericallyIdentical(plane2));
|
assertTrue(plane1.isNumericallyIdentical(plane2));
|
||||||
final Plane plane3 = new Plane(p,0.1);
|
final Plane plane3 = new Plane(p, 0.1);
|
||||||
assertFalse(plane1.isNumericallyIdentical(plane3));
|
assertFalse(plane1.isNumericallyIdentical(plane3));
|
||||||
final Vector v1 = new Vector(0.1,-0.732,0.9);
|
final Vector v1 = new Vector(0.1, -0.732, 0.9);
|
||||||
final double constant = 0.432;
|
final double constant = 0.432;
|
||||||
final Vector v2 = new Vector(v1.x*constant,v1.y*constant,v1.z*constant);
|
final Vector v2 = new Vector(v1.x * constant, v1.y * constant, v1.z * constant);
|
||||||
final Plane p1 = new Plane(v1,0.2);
|
final Plane p1 = new Plane(v1, 0.2);
|
||||||
final Plane p2 = new Plane(v2,0.2*constant);
|
final Plane p2 = new Plane(v2, 0.2 * constant);
|
||||||
assertTrue(p1.isNumericallyIdentical(p2));
|
assertTrue(p1.isNumericallyIdentical(p2));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testInterpolation() {
|
public void testInterpolation() {
|
||||||
// [X=0.35168818443386646, Y=-0.19637966197066342, Z=0.9152870857244183],
|
// [X=0.35168818443386646, Y=-0.19637966197066342, Z=0.9152870857244183],
|
||||||
// [X=0.5003343189532654, Y=0.522128543226148, Z=0.6906861469771293],
|
// [X=0.5003343189532654, Y=0.522128543226148, Z=0.6906861469771293],
|
||||||
|
|
||||||
final GeoPoint start = new GeoPoint(0.35168818443386646,-0.19637966197066342,0.9152870857244183);
|
final GeoPoint start = new GeoPoint(0.35168818443386646, -0.19637966197066342, 0.9152870857244183);
|
||||||
final GeoPoint end = new GeoPoint(0.5003343189532654,0.522128543226148,0.6906861469771293);
|
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;
|
// [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 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});
|
final GeoPoint[] points = p.interpolate(start, end, new double[]{0.25, 0.50, 0.75});
|
||||||
|
|
||||||
for (GeoPoint point : points) {
|
for (GeoPoint point : points) {
|
||||||
assertTrue(p.evaluateIsZero(point));
|
assertTrue(p.evaluateIsZero(point));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user