mirror of https://github.com/apache/lucene.git
LUCENE-6196: committing Karl's latest patch
https://reviews.apache.org/r/33353/ (diff #9) https://reviews.apache.org/r/33353/diff/raw/ git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6196@1675374 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
bf74c72eb2
commit
1242bf1519
|
@ -22,6 +22,9 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
*/
|
||||
public abstract class GeoBBoxBase implements GeoBBox {
|
||||
|
||||
protected final static GeoPoint NORTH_POLE = new GeoPoint(0.0,0.0,1.0);
|
||||
protected final static GeoPoint SOUTH_POLE = new GeoPoint(0.0,0.0,-1.0);
|
||||
|
||||
@Override
|
||||
public abstract boolean isWithin(final Vector point);
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ public class GeoBBoxFactory
|
|||
*@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)
|
||||
|
@ -41,10 +42,18 @@ public class GeoBBoxFactory
|
|||
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 == 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;
|
||||
|
@ -57,19 +66,36 @@ public class GeoBBoxFactory
|
|||
|
||||
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)
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ public abstract class GeoBaseExtendedShape implements GeoShape
|
|||
*@return true if there's such an intersection, false if not.
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean intersects(final Plane plane, final Membership... bounds);
|
||||
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,
|
||||
|
|
|
@ -27,6 +27,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
public final double cutoffLinearDistance;
|
||||
public final SidedPlane circlePlane;
|
||||
public final GeoPoint[] edgePoints;
|
||||
public static final GeoPoint[] circlePoints = new GeoPoint[0];
|
||||
|
||||
public GeoCircle(final double lat, final double lon, final double cutoffAngle)
|
||||
{
|
||||
|
@ -35,7 +36,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
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)
|
||||
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);
|
||||
|
@ -47,9 +48,25 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
this.cutoffAngle = cutoffAngle;
|
||||
this.circlePlane = new SidedPlane(center, center, -cosAngle);
|
||||
|
||||
// Compute a point on the circle boundary. This can be any point that is easy to compute.
|
||||
// This requires some math, so I've implemented it in Plane.
|
||||
this.edgePoints = new GeoPoint[]{center.getSamplePoint(sinAngle,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
|
||||
|
@ -170,8 +187,6 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
if (point == null)
|
||||
return false;
|
||||
// Fastest way of determining membership
|
||||
return circlePlane.isWithin(point);
|
||||
}
|
||||
|
@ -190,9 +205,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return circlePlane.intersects(p, bounds);
|
||||
return circlePlane.intersects(p, notablePoints, circlePoints, bounds);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
|
|
@ -63,10 +63,10 @@ public class GeoCompositeMembershipShape implements GeoMembershipShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
for (GeoMembershipShape shape : shapes) {
|
||||
if (shape.intersects(p,bounds))
|
||||
if (shape.intersects(p,notablePoints,bounds))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -33,6 +33,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
|
||||
protected SidedPlane[] edges = null;
|
||||
protected boolean[] internalEdges = null;
|
||||
protected GeoPoint[][] notableEdgePoints = null;
|
||||
|
||||
protected GeoPoint[] edgePoints = null;
|
||||
|
||||
|
@ -98,6 +99,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
throw new IllegalArgumentException("Polygon needs at least three points.");
|
||||
// Time to construct the planes. If the polygon is truly convex, then any adjacent point
|
||||
edges = new SidedPlane[points.size()];
|
||||
notableEdgePoints = new GeoPoint[points.size()][];
|
||||
internalEdges = new boolean[points.size()];
|
||||
// to a segment can provide an interior measurement.
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
|
@ -108,6 +110,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
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();
|
||||
|
@ -163,11 +166,14 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
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];
|
||||
|
@ -177,10 +183,13 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
membershipBounds[count++] = edges[otherIndex];
|
||||
}
|
||||
}
|
||||
if (edge.intersects(p,bounds,membershipBounds))
|
||||
if (edge.intersects(p,notablePoints, points, bounds,membershipBounds)) {
|
||||
//System.err.println(" intersects!");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
//System.err.println(" no intersection");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,9 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
|||
public final Plane plane;
|
||||
public final SidedPlane leftPlane;
|
||||
public final SidedPlane rightPlane;
|
||||
|
||||
|
||||
public final GeoPoint[] planePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
|
@ -83,6 +85,8 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
|||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
|
||||
this.planePoints = new GeoPoint[]{LHC,RHC};
|
||||
|
||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
||||
}
|
||||
|
||||
|
@ -107,7 +111,7 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
return plane.evaluateIsZero(point) &&
|
||||
leftPlane.isWithin(point) &&
|
||||
rightPlane.isWithin(point);
|
||||
}
|
||||
|
@ -115,7 +119,7 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
return plane.evaluateIsZero(x,y,z) &&
|
||||
leftPlane.isWithin(x,y,z) &&
|
||||
rightPlane.isWithin(x,y,z);
|
||||
}
|
||||
|
@ -135,9 +139,9 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds,leftPlane,rightPlane);
|
||||
return p.intersects(plane,notablePoints,planePoints,bounds,leftPlane,rightPlane);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -158,7 +162,7 @@ public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
|||
|
||||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.intersects(plane,leftPlane,rightPlane))
|
||||
if (path.intersects(plane,planePoints,leftPlane,rightPlane))
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.isWithin(centerPoint))
|
||||
|
|
|
@ -28,6 +28,7 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
|||
public final Plane plane;
|
||||
public final GeoPoint interiorPoint;
|
||||
public final GeoPoint[] edgePoints;
|
||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||
|
||||
public GeoDegenerateLatitudeZone(final double latitude)
|
||||
{
|
||||
|
@ -36,7 +37,6 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
|||
this.sinLatitude = Math.sin(latitude);
|
||||
double cosLatitude = Math.cos(latitude);
|
||||
this.plane = new Plane(sinLatitude);
|
||||
|
||||
// Compute an interior point.
|
||||
interiorPoint = new GeoPoint(cosLatitude,0.0,sinLatitude);
|
||||
edgePoints = new GeoPoint[]{interiorPoint};
|
||||
|
@ -53,13 +53,13 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return point.z == this.sinLatitude;
|
||||
return Math.abs(point.z - this.sinLatitude) < 1e-10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return z == this.sinLatitude;
|
||||
return Math.abs(z - this.sinLatitude) < 1e-10;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -75,9 +75,9 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds);
|
||||
return p.intersects(plane,notablePoints,planePoints,bounds);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -102,7 +102,7 @@ public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
|||
// work with no area endpoints. So we rely entirely on intersections.
|
||||
//System.out.println("Got here! latitude="+latitude+" path="+path);
|
||||
|
||||
if (path.intersects(plane)) {
|
||||
if (path.intersects(plane,planePoints)) {
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,8 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase
|
|||
public final GeoPoint interiorPoint;
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public final static GeoPoint[] planePoints = new GeoPoint[]{NORTH_POLE,SOUTH_POLE};
|
||||
|
||||
/** Accepts only values in the following ranges: lon: {@code -PI -> PI} */
|
||||
public GeoDegenerateLongitudeSlice(final double longitude)
|
||||
{
|
||||
|
@ -65,14 +67,14 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
return plane.evaluateIsZero(point) &&
|
||||
boundingPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
return plane.evaluateIsZero(x,y,z) &&
|
||||
boundingPlane.isWithin(x,y,z);
|
||||
}
|
||||
|
||||
|
@ -89,9 +91,9 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds,boundingPlane);
|
||||
return p.intersects(plane,notablePoints,planePoints,bounds,boundingPlane);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -114,7 +116,7 @@ public class GeoDegenerateLongitudeSlice extends GeoBBoxBase
|
|||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
// Look for intersections.
|
||||
if (path.intersects(plane,boundingPlane))
|
||||
if (path.intersects(plane,planePoints,boundingPlane))
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.isWithin(interiorPoint))
|
||||
|
|
|
@ -64,7 +64,7 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox
|
|||
*@return true if there's such an intersection, false if not.
|
||||
*/
|
||||
@Override
|
||||
public boolean intersects(final Plane plane, final Membership... bounds) {
|
||||
public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds) {
|
||||
if (plane.evaluate(this) == 0.0)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -32,7 +32,9 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
|||
public final SidedPlane bottomPlane;
|
||||
public final SidedPlane boundingPlane;
|
||||
public final Plane plane;
|
||||
|
||||
|
||||
public final GeoPoint[] planePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
|
@ -77,6 +79,8 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
|||
|
||||
this.boundingPlane = new SidedPlane(centerPoint,-sinLongitude,cosLongitude);
|
||||
|
||||
this.planePoints = new GeoPoint[]{UHC,LHC};
|
||||
|
||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
||||
}
|
||||
|
||||
|
@ -98,7 +102,7 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
return plane.evaluateIsZero(point) &&
|
||||
boundingPlane.isWithin(point) &&
|
||||
topPlane.isWithin(point) &&
|
||||
bottomPlane.isWithin(point);
|
||||
|
@ -107,7 +111,7 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
return plane.evaluateIsZero(x,y,z) &&
|
||||
boundingPlane.isWithin(x,y,z) &&
|
||||
topPlane.isWithin(x,y,z) &&
|
||||
bottomPlane.isWithin(x,y,z);
|
||||
|
@ -131,9 +135,9 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds,boundingPlane,topPlane,bottomPlane);
|
||||
return p.intersects(plane,notablePoints,planePoints,bounds,boundingPlane,topPlane,bottomPlane);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -155,12 +159,18 @@ public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
|||
|
||||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.intersects(plane,boundingPlane,topPlane,bottomPlane))
|
||||
//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))
|
||||
if (path.isWithin(centerPoint)) {
|
||||
//System.err.println(" contains");
|
||||
return CONTAINS;
|
||||
}
|
||||
|
||||
//System.err.println(" disjoint");
|
||||
return DISJOINT;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ public class GeoLatitudeZone extends GeoBBoxBase
|
|||
public final SidedPlane topPlane;
|
||||
public final SidedPlane bottomPlane;
|
||||
public final GeoPoint interiorPoint;
|
||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||
|
||||
// We need two additional points because a latitude zone's boundaries don't intersect. This is a very
|
||||
// special case that most GeoBBox's do not have.
|
||||
|
@ -106,10 +107,10 @@ public class GeoLatitudeZone extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(topPlane,bounds,bottomPlane) ||
|
||||
p.intersects(bottomPlane,bounds,topPlane);
|
||||
return p.intersects(topPlane,notablePoints,planePoints,bounds,bottomPlane) ||
|
||||
p.intersects(bottomPlane,notablePoints,planePoints,bounds,topPlane);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -149,8 +150,8 @@ public class GeoLatitudeZone extends GeoBBoxBase
|
|||
// 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,bottomPlane) ||
|
||||
path.intersects(bottomPlane,topPlane))
|
||||
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
|
||||
|
|
|
@ -29,9 +29,11 @@ public class GeoLongitudeSlice extends GeoBBoxBase
|
|||
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 GeoPoint northPole = new GeoPoint(0.0,0.0,1.0);
|
||||
public final GeoPoint[] edgePoints = new GeoPoint[]{northPole};
|
||||
|
||||
public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
||||
|
||||
/** Accepts only values in the following ranges: lon: {@code -PI -> PI} */
|
||||
public GeoLongitudeSlice(final double leftLon, double rightLon)
|
||||
|
@ -115,10 +117,10 @@ public class GeoLongitudeSlice extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(leftPlane,bounds,rightPlane) ||
|
||||
p.intersects(rightPlane,bounds,leftPlane);
|
||||
return p.intersects(leftPlane,notablePoints,planePoints,bounds,rightPlane) ||
|
||||
p.intersects(rightPlane,notablePoints,planePoints,bounds,leftPlane);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -144,13 +146,13 @@ public class GeoLongitudeSlice extends GeoBBoxBase
|
|||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(northPole);
|
||||
final boolean insideShape = path.isWithin(NORTH_POLE);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.intersects(leftPlane,rightPlane) ||
|
||||
path.intersects(rightPlane,leftPlane)) {
|
||||
if (path.intersects(leftPlane,planePoints,rightPlane) ||
|
||||
path.intersects(rightPlane,planePoints,leftPlane)) {
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** This GeoBBox represents an area rectangle limited only in south latitude.
|
||||
*/
|
||||
public class GeoNorthLatitudeZone extends GeoBBoxBase
|
||||
{
|
||||
public final double bottomLat;
|
||||
public final double cosBottomLat;
|
||||
public final SidedPlane bottomPlane;
|
||||
public final GeoPoint interiorPoint;
|
||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||
|
||||
public final GeoPoint bottomBoundaryPoint;
|
||||
|
||||
// Edge points
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public GeoNorthLatitudeZone(final double bottomLat)
|
||||
{
|
||||
this.bottomLat = bottomLat;
|
||||
|
||||
final double sinBottomLat = Math.sin(bottomLat);
|
||||
this.cosBottomLat = Math.cos(bottomLat);
|
||||
|
||||
// Construct sample points, so we get our sidedness right
|
||||
final Vector bottomPoint = new Vector(0.0,0.0,sinBottomLat);
|
||||
|
||||
// Compute an interior point. Pick one whose lat is between top and bottom.
|
||||
final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5;
|
||||
final double sinMiddleLat = Math.sin(middleLat);
|
||||
this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat);
|
||||
this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat),0.0,sinBottomLat);
|
||||
|
||||
this.bottomPlane = new SidedPlane(interiorPoint,sinBottomLat);
|
||||
|
||||
this.edgePoints = new GeoPoint[]{bottomBoundaryPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
final double newTopLat = Math.PI * 0.5;
|
||||
final double newBottomLat = bottomLat - angle;
|
||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return
|
||||
bottomPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return
|
||||
bottomPlane.isWithin(x,y,z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadius()
|
||||
{
|
||||
// This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that
|
||||
// would contain all the bounding box points, when starting in the "center".
|
||||
if (bottomLat < 0.0)
|
||||
return Math.PI;
|
||||
double maxCosLat = cosBottomLat;
|
||||
return maxCosLat * Math.PI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return
|
||||
p.intersects(bottomPlane,notablePoints,planePoints,bounds);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
*@param bounds is the optional input bounds object. If this is null,
|
||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
||||
* be computed, then return a Bounds object with noLongitudeBound,
|
||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
||||
*/
|
||||
@Override
|
||||
public Bounds getBounds(Bounds bounds)
|
||||
{
|
||||
if (bounds == null)
|
||||
bounds = new Bounds();
|
||||
bounds.noLongitudeBound().noTopLatitudeBound().addLatitudeZone(bottomLat);
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(bottomBoundaryPoint);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
// Second, the shortcut of seeing whether endpoints are in/out is not going to
|
||||
// work with no area endpoints. So we rely entirely on intersections.
|
||||
|
||||
if (
|
||||
path.intersects(bottomPlane,planePoints))
|
||||
return OVERLAPS;
|
||||
|
||||
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
|
||||
// within the zone, but the shape includes areas outside the zone crossing a pole.
|
||||
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
|
||||
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
|
||||
// one such point is within, then OVERLAPS is the right answer.
|
||||
|
||||
if (insideShape)
|
||||
return CONTAINS;
|
||||
|
||||
if (insideRectangle == ALL_INSIDE)
|
||||
return WITHIN;
|
||||
|
||||
return DISJOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (!(o instanceof GeoNorthLatitudeZone))
|
||||
return false;
|
||||
GeoNorthLatitudeZone other = (GeoNorthLatitudeZone)o;
|
||||
return other.bottomPlane.equals(bottomPlane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = bottomPlane.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoNorthLatitudeZone: {bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,243 @@
|
|||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** Bounding box limited on three sides (bottom lat, left lon, right lon), including
|
||||
* the north pole.
|
||||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||
* GeoWideNorthRectangle.
|
||||
*/
|
||||
public class GeoNorthRectangle extends GeoBBoxBase
|
||||
{
|
||||
public final double bottomLat;
|
||||
public final double leftLon;
|
||||
public final double rightLon;
|
||||
|
||||
public final double cosMiddleLat;
|
||||
|
||||
public final GeoPoint LRHC;
|
||||
public final GeoPoint LLHC;
|
||||
|
||||
public final SidedPlane bottomPlane;
|
||||
public final SidedPlane leftPlane;
|
||||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint[] bottomPlanePoints;
|
||||
public final GeoPoint[] leftPlanePoints;
|
||||
public final GeoPoint[] rightPlanePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
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+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +67,20 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
if (ps.isDegenerate())
|
||||
return;
|
||||
segments.add(ps);
|
||||
} else {
|
||||
// First point. We compute the basic set of edgepoints here because we've got the lat and lon available.
|
||||
// Move from center only in latitude. Then, if we go past the north pole, adjust the longitude also.
|
||||
double newLat = lat + cutoffAngle;
|
||||
double newLon = lon;
|
||||
if (newLat > Math.PI * 0.5) {
|
||||
newLat = Math.PI - newLat;
|
||||
newLon += Math.PI;
|
||||
}
|
||||
while (newLon > Math.PI) {
|
||||
newLon -= Math.PI * 2.0;
|
||||
}
|
||||
final GeoPoint edgePoint = new GeoPoint(newLat,newLon);
|
||||
this.edgePoints = new GeoPoint[]{edgePoint};
|
||||
}
|
||||
final SegmentEndpoint se = new SegmentEndpoint(end, originDistance, cutoffOffset, cutoffAngle, chordDistance);
|
||||
points.add(se);
|
||||
|
@ -77,8 +91,24 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
throw new IllegalArgumentException("Path must have at least one point");
|
||||
if (segments.size() > 0) {
|
||||
edgePoints = new GeoPoint[]{points.get(0).circlePlane.getSampleIntersectionPoint(segments.get(0).invertedStartCutoffPlane)};
|
||||
} else {
|
||||
edgePoints = new GeoPoint[]{points.get(0).point.getSamplePoint(cutoffOffset,originDistance)};
|
||||
}
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
final SegmentEndpoint pathPoint = points.get(i);
|
||||
Membership previousEndBound = null;
|
||||
GeoPoint[] previousEndNotablePoints = null;
|
||||
Membership nextStartBound = null;
|
||||
GeoPoint[] nextStartNotablePoints = null;
|
||||
if (i > 0) {
|
||||
final PathSegment previousSegment = segments.get(i-1);
|
||||
previousEndBound = previousSegment.invertedEndCutoffPlane;
|
||||
previousEndNotablePoints = previousSegment.endCutoffPlanePoints;
|
||||
}
|
||||
if (i < segments.size()) {
|
||||
final PathSegment nextSegment = segments.get(i);
|
||||
nextStartBound = nextSegment.invertedStartCutoffPlane;
|
||||
nextStartNotablePoints = nextSegment.startCutoffPlanePoints;
|
||||
}
|
||||
pathPoint.setCutoffPlanes(previousEndNotablePoints,previousEndBound,nextStartNotablePoints,nextStartBound);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,7 +297,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane plane, final Membership... bounds)
|
||||
public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
// We look for an intersection with any of the exterior edges of the path.
|
||||
// We also have to look for intersections with the cones described by the endpoints.
|
||||
|
@ -279,21 +309,14 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
// Well, sort of. We can detect intersections also due to overlap of segments with each other.
|
||||
// But that's an edge case and we won't be optimizing for it.
|
||||
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
final SegmentEndpoint pathPoint = points.get(i);
|
||||
Membership previousEndBound = null;
|
||||
Membership nextStartBound = null;
|
||||
if (i > 0)
|
||||
previousEndBound = segments.get(i-1).invertedEndCutoffPlane;
|
||||
if (i < segments.size())
|
||||
nextStartBound = segments.get(i).invertedStartCutoffPlane;
|
||||
if (pathPoint.intersects(plane, bounds, previousEndBound, nextStartBound)) {
|
||||
for (final SegmentEndpoint pathPoint : points) {
|
||||
if (pathPoint.intersects(plane, notablePoints, bounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (PathSegment pathSegment : segments) {
|
||||
if (pathSegment.intersects(plane, bounds)) {
|
||||
for (final PathSegment pathSegment : segments) {
|
||||
if (pathSegment.intersects(plane, notablePoints, bounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +386,11 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
public final double cutoffNormalDistance;
|
||||
public final double cutoffAngle;
|
||||
public final double chordDistance;
|
||||
|
||||
public Membership[] cutoffPlanes = null;
|
||||
public GeoPoint[] notablePoints = null;
|
||||
|
||||
public final static GeoPoint[] circlePoints = new GeoPoint[0];
|
||||
|
||||
public SegmentEndpoint(final GeoPoint point, final double originDistance, final double cutoffOffset, final double cutoffAngle, final double chordDistance)
|
||||
{
|
||||
this.point = point;
|
||||
|
@ -373,6 +400,30 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
this.circlePlane = new SidedPlane(point, point, -originDistance);
|
||||
}
|
||||
|
||||
public void setCutoffPlanes(final GeoPoint[] previousEndNotablePoints, final Membership previousEndPlane,
|
||||
final GeoPoint[] nextStartNotablePoints, final Membership nextStartPlane) {
|
||||
if (previousEndNotablePoints == null && nextStartNotablePoints == null) {
|
||||
cutoffPlanes = new Membership[0];
|
||||
notablePoints = new GeoPoint[0];
|
||||
} else if (previousEndNotablePoints != null && nextStartNotablePoints == null) {
|
||||
cutoffPlanes = new Membership[]{previousEndPlane};
|
||||
notablePoints = previousEndNotablePoints;
|
||||
} else if (previousEndNotablePoints == null && nextStartNotablePoints != null) {
|
||||
cutoffPlanes = new Membership[]{nextStartPlane};
|
||||
notablePoints = nextStartNotablePoints;
|
||||
} else {
|
||||
cutoffPlanes = new Membership[]{previousEndPlane,nextStartPlane};
|
||||
notablePoints = new GeoPoint[previousEndNotablePoints.length + nextStartNotablePoints.length];
|
||||
int i = 0;
|
||||
for (GeoPoint p : previousEndNotablePoints) {
|
||||
notablePoints[i++] = p;
|
||||
}
|
||||
for (GeoPoint p : nextStartNotablePoints) {
|
||||
notablePoints[i++] = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return circlePlane.isWithin(point);
|
||||
|
@ -407,9 +458,9 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return dist;
|
||||
}
|
||||
|
||||
public boolean intersects(final Plane p, final Membership[] bounds, final Membership previousEndCutoff, final Membership nextStartCutoff)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds)
|
||||
{
|
||||
return circlePlane.intersects(p, bounds, previousEndCutoff, nextStartCutoff);
|
||||
return circlePlane.intersects(p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes);
|
||||
}
|
||||
|
||||
public void getBounds(Bounds bounds)
|
||||
|
@ -450,6 +501,10 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
public final SidedPlane lowerConnectingPlane;
|
||||
public final SidedPlane startCutoffPlane;
|
||||
public final SidedPlane endCutoffPlane;
|
||||
public final GeoPoint[] upperConnectingPlanePoints;
|
||||
public final GeoPoint[] lowerConnectingPlanePoints;
|
||||
public final GeoPoint[] startCutoffPlanePoints;
|
||||
public final GeoPoint[] endCutoffPlanePoints;
|
||||
public final double planeBoundingOffset;
|
||||
public final double arcWidth;
|
||||
public final double chordDistance;
|
||||
|
@ -475,6 +530,10 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
lowerConnectingPlane = null;
|
||||
startCutoffPlane = null;
|
||||
endCutoffPlane = null;
|
||||
upperConnectingPlanePoints = null;
|
||||
lowerConnectingPlanePoints = null;
|
||||
startCutoffPlanePoints = null;
|
||||
endCutoffPlanePoints = null;
|
||||
invertedStartCutoffPlane = null;
|
||||
invertedEndCutoffPlane = null;
|
||||
} else {
|
||||
|
@ -484,6 +543,18 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
// Cutoff planes use opposite endpoints as correct side examples
|
||||
startCutoffPlane = new SidedPlane(end,normalizedConnectingPlane,start);
|
||||
endCutoffPlane = new SidedPlane(start,normalizedConnectingPlane,end);
|
||||
final Membership[] upperSide = new Membership[]{upperConnectingPlane};
|
||||
final Membership[] lowerSide = new Membership[]{lowerConnectingPlane};
|
||||
final Membership[] startSide = new Membership[]{startCutoffPlane};
|
||||
final Membership[] endSide = new Membership[]{endCutoffPlane};
|
||||
final GeoPoint ULHC = upperConnectingPlane.findIntersections(startCutoffPlane,lowerSide,endSide)[0];
|
||||
final GeoPoint URHC = upperConnectingPlane.findIntersections(endCutoffPlane,lowerSide,startSide)[0];
|
||||
final GeoPoint LLHC = lowerConnectingPlane.findIntersections(startCutoffPlane,upperSide,endSide)[0];
|
||||
final GeoPoint LRHC = lowerConnectingPlane.findIntersections(endCutoffPlane,upperSide,startSide)[0];
|
||||
upperConnectingPlanePoints = new GeoPoint[]{ULHC,URHC};
|
||||
lowerConnectingPlanePoints = new GeoPoint[]{LLHC,LRHC};
|
||||
startCutoffPlanePoints = new GeoPoint[]{ULHC,LLHC};
|
||||
endCutoffPlanePoints = new GeoPoint[]{URHC,LRHC};
|
||||
invertedStartCutoffPlane = new SidedPlane(startCutoffPlane);
|
||||
invertedEndCutoffPlane = new SidedPlane(endCutoffPlane);
|
||||
}
|
||||
|
@ -535,7 +606,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
|
||||
|
||||
// If we have a degenerate line, then just compute the normal distance from point x to the start
|
||||
if (perpX < 1e-10 && perpY < 1e-10 && perpZ < 1e-10)
|
||||
if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION)
|
||||
return point.normalDistance(start);
|
||||
|
||||
final double normFactor = 1.0 / Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ);
|
||||
|
@ -556,7 +627,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
|
||||
|
||||
// If we have a degenerate line, then just compute the normal distance from point x to the start
|
||||
if (Math.abs(perpX) < 1e-10 && Math.abs(perpY) < 1e-10 && Math.abs(perpZ) < 1e-10)
|
||||
if (Math.abs(perpX) < Vector.MINIMUM_RESOLUTION && Math.abs(perpY) < Vector.MINIMUM_RESOLUTION && Math.abs(perpZ) < Vector.MINIMUM_RESOLUTION)
|
||||
return point.linearDistance(start);
|
||||
|
||||
// Next, we need the vector of the line, which is the cross product of the normalized connecting plane
|
||||
|
@ -584,10 +655,10 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return point.linearDistance(normLineX,normLineY,normLineZ) + start.linearDistance(normLineX,normLineY,normLineZ);
|
||||
}
|
||||
|
||||
public boolean intersects(final Plane p, final Membership[] bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership[] bounds)
|
||||
{
|
||||
return upperConnectingPlane.intersects(p, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
|
||||
lowerConnectingPlane.intersects(p, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
|
||||
return upperConnectingPlane.intersects(p, notablePoints, upperConnectingPlanePoints, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
|
||||
lowerConnectingPlane.intersects(p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
|
||||
}
|
||||
|
||||
public void getBounds(Bounds bounds)
|
||||
|
|
|
@ -41,42 +41,4 @@ public class GeoPoint extends Vector
|
|||
return Tools.safeAcos(evaluate(v));
|
||||
}
|
||||
|
||||
/** Find a single point that is a specified arc distance away from this point.
|
||||
*/
|
||||
public GeoPoint getSamplePoint(final double sinRotationAngle, final double cosRotationAngle) {
|
||||
// Rotate in the best of three possible directions: x-y, x-z, y-z.
|
||||
final double absX = Math.abs(x);
|
||||
final double absY = Math.abs(y);
|
||||
final double absZ = Math.abs(z);
|
||||
if (absX > absY) {
|
||||
// x > y
|
||||
if (absY > absZ) {
|
||||
// x > y > z
|
||||
// rotate in x-y
|
||||
return new GeoPoint(x*cosRotationAngle-y*sinRotationAngle,x*sinRotationAngle+y*cosRotationAngle,z);
|
||||
} else {
|
||||
// x > z > y OR z > x > y
|
||||
// rotate in x-z
|
||||
return new GeoPoint(x*cosRotationAngle-z*sinRotationAngle,y,x*sinRotationAngle+z*cosRotationAngle);
|
||||
}
|
||||
} else {
|
||||
// y > x
|
||||
if (absX > absZ) {
|
||||
// y > x > z
|
||||
// rotate in x-y
|
||||
return new GeoPoint(x*cosRotationAngle-y*sinRotationAngle,x*sinRotationAngle+y*cosRotationAngle,z);
|
||||
} else {
|
||||
// y > z > x OR z > y > x
|
||||
// rotate in y-z
|
||||
return new GeoPoint(x,y*cosRotationAngle-z*sinRotationAngle,y*sinRotationAngle+z*cosRotationAngle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Find a single point that is a specified arc distance away from this point.
|
||||
*/
|
||||
public GeoPoint getSamplePoint(final double rotationAngle) {
|
||||
return getSamplePoint(Math.sin(rotationAngle), Math.cos(rotationAngle));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -39,7 +39,12 @@ public class GeoRectangle extends GeoBBoxBase
|
|||
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;
|
||||
|
@ -103,6 +108,11 @@ public class GeoRectangle extends GeoBBoxBase
|
|||
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};
|
||||
}
|
||||
|
||||
|
@ -161,12 +171,12 @@ public class GeoRectangle extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(topPlane,bounds,bottomPlane,leftPlane,rightPlane) ||
|
||||
p.intersects(bottomPlane,bounds,topPlane,leftPlane,rightPlane) ||
|
||||
p.intersects(leftPlane,bounds,rightPlane,topPlane,bottomPlane) ||
|
||||
p.intersects(rightPlane,bounds,leftPlane,topPlane,bottomPlane);
|
||||
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.
|
||||
|
@ -188,27 +198,40 @@ public class GeoRectangle extends GeoBBoxBase
|
|||
|
||||
@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)
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.intersects(topPlane,bottomPlane,leftPlane,rightPlane) ||
|
||||
path.intersects(bottomPlane,topPlane,leftPlane,rightPlane) ||
|
||||
path.intersects(leftPlane,topPlane,bottomPlane,rightPlane) ||
|
||||
path.intersects(rightPlane,leftPlane,topPlane,bottomPlane))
|
||||
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)
|
||||
if (insideShape) {
|
||||
//System.err.println(" shape contains rectangle");
|
||||
return CONTAINS;
|
||||
|
||||
}
|
||||
//System.err.println(" disjoint");
|
||||
return DISJOINT;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,11 +37,14 @@ public interface GeoShape extends Membership {
|
|||
* 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 Membership... bounds);
|
||||
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,
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** This GeoBBox represents an area rectangle limited only in north latitude.
|
||||
*/
|
||||
public class GeoSouthLatitudeZone extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double cosTopLat;
|
||||
public final SidedPlane topPlane;
|
||||
public final GeoPoint interiorPoint;
|
||||
public final static GeoPoint[] planePoints = new GeoPoint[0];
|
||||
|
||||
public final GeoPoint topBoundaryPoint;
|
||||
|
||||
// Edge points
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public GeoSouthLatitudeZone(final double topLat)
|
||||
{
|
||||
this.topLat = topLat;
|
||||
|
||||
final double sinTopLat = Math.sin(topLat);
|
||||
this.cosTopLat = Math.cos(topLat);
|
||||
|
||||
// Construct sample points, so we get our sidedness right
|
||||
final Vector topPoint = new Vector(0.0,0.0,sinTopLat);
|
||||
|
||||
// Compute an interior point. Pick one whose lat is between top and bottom.
|
||||
final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
|
||||
final double sinMiddleLat = Math.sin(middleLat);
|
||||
this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat);
|
||||
this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat),0.0,sinTopLat);
|
||||
|
||||
this.topPlane = new SidedPlane(interiorPoint,sinTopLat);
|
||||
|
||||
this.edgePoints = new GeoPoint[]{topBoundaryPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
final double newTopLat = topLat + angle;
|
||||
final double newBottomLat = -Math.PI * 0.5;
|
||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return topPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return topPlane.isWithin(x,y,z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRadius()
|
||||
{
|
||||
// This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that
|
||||
// would contain all the bounding box points, when starting in the "center".
|
||||
if (topLat > 0.0)
|
||||
return Math.PI;
|
||||
double maxCosLat = cosTopLat;
|
||||
return maxCosLat * Math.PI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(topPlane,notablePoints,planePoints,bounds);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
*@param bounds is the optional input bounds object. If this is null,
|
||||
* a bounds object will be created. Otherwise, the input object will be modified.
|
||||
*@return a Bounds object describing the shape's bounds. If the bounds cannot
|
||||
* be computed, then return a Bounds object with noLongitudeBound,
|
||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
||||
*/
|
||||
@Override
|
||||
public Bounds getBounds(Bounds bounds)
|
||||
{
|
||||
if (bounds == null)
|
||||
bounds = new Bounds();
|
||||
bounds.noLongitudeBound().addLatitudeZone(topLat).noBottomLatitudeBound();
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(topBoundaryPoint);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
// Second, the shortcut of seeing whether endpoints are in/out is not going to
|
||||
// work with no area endpoints. So we rely entirely on intersections.
|
||||
|
||||
if (path.intersects(topPlane,planePoints))
|
||||
return OVERLAPS;
|
||||
|
||||
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
|
||||
// within the zone, but the shape includes areas outside the zone crossing a pole.
|
||||
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
|
||||
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
|
||||
// one such point is within, then OVERLAPS is the right answer.
|
||||
|
||||
if (insideShape)
|
||||
return CONTAINS;
|
||||
|
||||
if (insideRectangle == ALL_INSIDE)
|
||||
return WITHIN;
|
||||
|
||||
return DISJOINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (!(o instanceof GeoSouthLatitudeZone))
|
||||
return false;
|
||||
GeoSouthLatitudeZone other = (GeoSouthLatitudeZone)o;
|
||||
return other.topPlane.equals(topPlane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = topPlane.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoSouthLatitudeZone: {toplat="+topLat+"("+topLat*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** Bounding box limited on three sides (top lat, left lon, right lon). The
|
||||
* other corner is the south pole.
|
||||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||
* GeoWideSouthRectangle.
|
||||
*/
|
||||
public class GeoSouthRectangle extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double leftLon;
|
||||
public final double rightLon;
|
||||
|
||||
public final double cosMiddleLat;
|
||||
|
||||
public final GeoPoint ULHC;
|
||||
public final GeoPoint URHC;
|
||||
|
||||
public final SidedPlane topPlane;
|
||||
public final SidedPlane leftPlane;
|
||||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint[] topPlanePoints;
|
||||
public final GeoPoint[] leftPlanePoints;
|
||||
public final GeoPoint[] rightPlanePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
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+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +31,9 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
|||
public final Plane plane;
|
||||
public final SidedPlane leftPlane;
|
||||
public final SidedPlane rightPlane;
|
||||
|
||||
|
||||
public final GeoPoint[] planePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final EitherBound eitherBound;
|
||||
|
@ -87,6 +89,8 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
|||
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};
|
||||
|
@ -115,7 +119,7 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
|||
{
|
||||
if (point == null)
|
||||
return false;
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
return plane.evaluateIsZero(point) &&
|
||||
(leftPlane.isWithin(point) ||
|
||||
rightPlane.isWithin(point));
|
||||
}
|
||||
|
@ -123,7 +127,7 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
|||
@Override
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
return plane.evaluateIsZero(x,y,z) &&
|
||||
(leftPlane.isWithin(x,y,z) ||
|
||||
rightPlane.isWithin(x,y,z));
|
||||
}
|
||||
|
@ -146,11 +150,11 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
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,bounds,eitherBound);
|
||||
return p.intersects(plane,notablePoints,planePoints,bounds,eitherBound);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -172,7 +176,7 @@ public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
|||
|
||||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.intersects(plane,eitherBound)) {
|
||||
if (path.intersects(plane,planePoints,eitherBound)) {
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,10 +27,12 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase
|
|||
|
||||
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 GeoPoint northPole = new GeoPoint(0.0,0.0,1.0);
|
||||
public final GeoPoint[] edgePoints = new GeoPoint[]{northPole};
|
||||
|
||||
public final static GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
||||
|
||||
/** Accepts only values in the following ranges: lon: {@code -PI -> PI}.
|
||||
* Horizantal angle must be greater than or equal to PI.
|
||||
|
@ -115,12 +117,12 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
// Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
|
||||
// requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
|
||||
return p.intersects(leftPlane,bounds) ||
|
||||
p.intersects(rightPlane,bounds);
|
||||
return p.intersects(leftPlane,notablePoints,planePoints,bounds) ||
|
||||
p.intersects(rightPlane,notablePoints,planePoints,bounds);
|
||||
}
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
|
@ -146,13 +148,13 @@ public class GeoWideLongitudeSlice extends GeoBBoxBase
|
|||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(northPole);
|
||||
final boolean insideShape = path.isWithin(NORTH_POLE);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.intersects(leftPlane) ||
|
||||
path.intersects(rightPlane))
|
||||
if (path.intersects(leftPlane,planePoints) ||
|
||||
path.intersects(rightPlane,planePoints))
|
||||
return OVERLAPS;
|
||||
|
||||
if (insideRectangle == ALL_INSIDE)
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** Bounding box wider than PI but limited on three sides (
|
||||
* bottom lat, left lon, right lon).
|
||||
*/
|
||||
public class GeoWideNorthRectangle extends GeoBBoxBase
|
||||
{
|
||||
public final double bottomLat;
|
||||
public final double leftLon;
|
||||
public final double rightLon;
|
||||
|
||||
public final double cosMiddleLat;
|
||||
|
||||
public final GeoPoint LRHC;
|
||||
public final GeoPoint LLHC;
|
||||
|
||||
public final SidedPlane bottomPlane;
|
||||
public final SidedPlane leftPlane;
|
||||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint[] bottomPlanePoints;
|
||||
public final GeoPoint[] leftPlanePoints;
|
||||
public final GeoPoint[] rightPlanePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final EitherBound eitherBound;
|
||||
|
||||
public final GeoPoint[] edgePoints = new GeoPoint[]{NORTH_POLE};
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}.
|
||||
* Horizontal angle must be greater than or equal to PI.
|
||||
*/
|
||||
public GeoWideNorthRectangle(final double bottomLat, final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5)
|
||||
throw new IllegalArgumentException("Bottom latitude out of range");
|
||||
if (leftLon < -Math.PI || leftLon > Math.PI)
|
||||
throw new IllegalArgumentException("Left longitude out of range");
|
||||
if (rightLon < -Math.PI || rightLon > Math.PI)
|
||||
throw new IllegalArgumentException("Right longitude out of range");
|
||||
double extent = rightLon - leftLon;
|
||||
if (extent < 0.0) {
|
||||
extent += 2.0 * Math.PI;
|
||||
}
|
||||
if (extent < Math.PI)
|
||||
throw new IllegalArgumentException("Width of rectangle too small");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -38,7 +38,12 @@ public class GeoWideRectangle extends GeoBBoxBase
|
|||
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 EitherBound eitherBound;
|
||||
|
@ -105,7 +110,12 @@ public class GeoWideRectangle extends GeoBBoxBase
|
|||
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};
|
||||
|
@ -166,14 +176,14 @@ public class GeoWideRectangle extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
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,bounds,bottomPlane,eitherBound) ||
|
||||
p.intersects(bottomPlane,bounds,topPlane,eitherBound) ||
|
||||
p.intersects(leftPlane,bounds,topPlane,bottomPlane) ||
|
||||
p.intersects(rightPlane,bounds,topPlane,bottomPlane);
|
||||
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.
|
||||
|
@ -195,30 +205,40 @@ public class GeoWideRectangle extends GeoBBoxBase
|
|||
|
||||
@Override
|
||||
public int getRelationship(final GeoShape path) {
|
||||
//System.err.println(this+" comparing to "+path);
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
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,bottomPlane,eitherBound) ||
|
||||
path.intersects(bottomPlane,topPlane,eitherBound) ||
|
||||
path.intersects(leftPlane,topPlane,bottomPlane) ||
|
||||
path.intersects(rightPlane,topPlane,bottomPlane)) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
package org.apache.lucene.spatial.spatial4j.geo3d;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/** Bounding box wider than PI but limited on three sides (top lat,
|
||||
* left lon, right lon).
|
||||
*/
|
||||
public class GeoWideSouthRectangle extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double leftLon;
|
||||
public final double rightLon;
|
||||
|
||||
public final double cosMiddleLat;
|
||||
|
||||
public final GeoPoint ULHC;
|
||||
public final GeoPoint URHC;
|
||||
|
||||
public final SidedPlane topPlane;
|
||||
public final SidedPlane leftPlane;
|
||||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint[] topPlanePoints;
|
||||
public final GeoPoint[] leftPlanePoints;
|
||||
public final GeoPoint[] rightPlanePoints;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final EitherBound eitherBound;
|
||||
|
||||
public final GeoPoint[] edgePoints = new GeoPoint[]{SOUTH_POLE};
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}.
|
||||
* Horizontal angle must be greater than or equal to PI.
|
||||
*/
|
||||
public GeoWideSouthRectangle(final double topLat, final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
|
||||
throw new IllegalArgumentException("Top latitude out of range");
|
||||
if (leftLon < -Math.PI || leftLon > Math.PI)
|
||||
throw new IllegalArgumentException("Left longitude out of range");
|
||||
if (rightLon < -Math.PI || rightLon > Math.PI)
|
||||
throw new IllegalArgumentException("Right longitude out of range");
|
||||
double extent = rightLon - leftLon;
|
||||
if (extent < 0.0) {
|
||||
extent += 2.0 * Math.PI;
|
||||
}
|
||||
if (extent < Math.PI)
|
||||
throw new IllegalArgumentException("Width of rectangle too small");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,7 @@ public class GeoWorld extends GeoBBoxBase
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ public class Plane extends Vector
|
|||
|
||||
// Now compute latitude min/max points
|
||||
if (!boundsInfo.checkNoTopLatitudeBound() || !boundsInfo.checkNoBottomLatitudeBound()) {
|
||||
if ((Math.abs(A) >= 1e-10 || Math.abs(B) >= 1e-10)) {
|
||||
if ((Math.abs(A) >= MINIMUM_RESOLUTION || Math.abs(B) >= MINIMUM_RESOLUTION)) {
|
||||
//System.out.println("A = "+A+" B = "+B+" C = "+C+" D = "+D);
|
||||
// sin (phi) = z
|
||||
// cos (theta - phi) = D
|
||||
|
@ -359,11 +359,11 @@ public class Plane extends Vector
|
|||
double b;
|
||||
double c;
|
||||
|
||||
if (Math.abs(C) < 1e-10) {
|
||||
if (Math.abs(C) < MINIMUM_RESOLUTION) {
|
||||
// Degenerate; the equation describes a line
|
||||
//System.out.println("It's a zero-width ellipse");
|
||||
// Ax + By + D = 0
|
||||
if (Math.abs(D) >= 1e-10) {
|
||||
if (Math.abs(D) >= MINIMUM_RESOLUTION) {
|
||||
if (Math.abs(A) > Math.abs(B)) {
|
||||
// Use equation suitable for A != 0
|
||||
// We need to find the endpoints of the zero-width ellipse.
|
||||
|
@ -432,7 +432,6 @@ public class Plane extends Vector
|
|||
double sqrtClause = b * b - 4.0 * a * c;
|
||||
|
||||
if (sqrtClause >= 0.0) {
|
||||
|
||||
if (sqrtClause == 0.0) {
|
||||
double x0 = -b / (2.0 * a);
|
||||
double y0 = (- D - A * x0) / B;
|
||||
|
@ -560,7 +559,7 @@ public class Plane extends Vector
|
|||
addPoint(boundsInfo, bounds, x0a, y0a, z0a);
|
||||
addPoint(boundsInfo, bounds, x0b, y0b, z0b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
//System.out.println("Using the x quadratic");
|
||||
|
@ -588,7 +587,6 @@ public class Plane extends Vector
|
|||
//System.out.println("sqrtClause="+sqrtClause);
|
||||
|
||||
if (sqrtClause >= 0.0) {
|
||||
|
||||
if (sqrtClause == 0.0) {
|
||||
//System.out.println("One solution");
|
||||
double x0 = -b / (2.0 * a);
|
||||
|
@ -634,13 +632,44 @@ public class Plane extends Vector
|
|||
/** Determine whether the plane intersects another plane within the
|
||||
* bounds provided.
|
||||
*@param q is the other plane.
|
||||
*@param notablePoints are points to look at to disambiguate cases when the two planes are identical.
|
||||
*@param moreNotablePoints are additional points to look at to disambiguate cases when the two planes are identical.
|
||||
*@param bounds is one part of the bounds.
|
||||
*@param moreBounds are more bounds.
|
||||
*@return true if there's an intersection.
|
||||
*/
|
||||
public boolean intersects(final Plane q, final Membership[] bounds, final Membership... moreBounds) {
|
||||
public boolean intersects(final Plane q, final GeoPoint[] notablePoints, final GeoPoint[] moreNotablePoints, final Membership[] bounds, final Membership... moreBounds) {
|
||||
// If the two planes are identical, then the math will find no points of intersection.
|
||||
// So a special case of this is to check for plane equality. But that is not enough, because
|
||||
// what we really need at that point is to determine whether overlap occurs between the two parts of the intersection
|
||||
// of plane and circle. That is, are there *any* points on the plane that are within the bounds described?
|
||||
if (equals(q)) {
|
||||
// The only way to efficiently figure this out will be to have a list of trial points available to evaluate.
|
||||
// We look for any point that fulfills all the bounds.
|
||||
for (GeoPoint p : notablePoints) {
|
||||
if (meetsAllBounds(p,bounds,moreBounds))
|
||||
return true;
|
||||
}
|
||||
for (GeoPoint p : moreNotablePoints) {
|
||||
if (meetsAllBounds(p,bounds,moreBounds))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return findIntersections(q,bounds,moreBounds).length > 0;
|
||||
}
|
||||
|
||||
protected static boolean meetsAllBounds(final GeoPoint p, final Membership[] bounds, final Membership[] moreBounds) {
|
||||
for (final Membership bound : bounds) {
|
||||
if (!bound.isWithin(p))
|
||||
return false;
|
||||
}
|
||||
for (final Membership bound : moreBounds) {
|
||||
if (!bound.isWithin(p))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Find a sample point on the intersection between two planes and the unit sphere.
|
||||
*/
|
||||
|
|
|
@ -79,9 +79,10 @@ public class SidedPlane extends Plane implements Membership
|
|||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
{
|
||||
double sigNum = Math.signum(evaluate(point));
|
||||
if (sigNum == 0.0)
|
||||
double evalResult = evaluate(point);
|
||||
if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
|
||||
return true;
|
||||
double sigNum = Math.signum(evalResult);
|
||||
return sigNum == this.sigNum;
|
||||
}
|
||||
|
||||
|
@ -94,7 +95,10 @@ public class SidedPlane extends Plane implements Membership
|
|||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
{
|
||||
double sigNum = Math.signum(this.x * x + this.y * y + this.z * z);
|
||||
double evalResult = this.x * x + this.y * y + this.z * z;
|
||||
if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
|
||||
return true;
|
||||
double sigNum = Math.signum(evalResult);
|
||||
return sigNum == this.sigNum;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* going through the origin. */
|
||||
public class Vector
|
||||
{
|
||||
/** Values that are all considered to be essentially zero have a magnitude
|
||||
* less than this. */
|
||||
public static final double MINIMUM_RESOLUTION = 1e-15;
|
||||
|
||||
public final double x;
|
||||
public final double y;
|
||||
public final double z;
|
||||
|
@ -56,13 +60,31 @@ public class Vector
|
|||
*/
|
||||
public Vector normalize() {
|
||||
double denom = magnitude();
|
||||
if (denom < 1e-10)
|
||||
if (denom < MINIMUM_RESOLUTION)
|
||||
// Degenerate, can't normalize
|
||||
return null;
|
||||
double normFactor = 1.0/denom;
|
||||
return new Vector(x*normFactor,y*normFactor,z*normFactor);
|
||||
}
|
||||
|
||||
/** Evaluate a vector (dot product) and check for "zero".
|
||||
*@param v is the vector to evaluate.
|
||||
*@return true if the evaluation yielded zero.
|
||||
*/
|
||||
public boolean evaluateIsZero(final Vector v) {
|
||||
return Math.abs(evaluate(v)) < MINIMUM_RESOLUTION;
|
||||
}
|
||||
|
||||
/** Evaluate a vector (do a dot product) snd check for "zero".
|
||||
*@param x is the x value of the vector to evaluate.
|
||||
*@param y is the x value of the vector to evaluate.
|
||||
*@param z is the x value of the vector to evaluate.
|
||||
*@return true if the evaluation yielded zero.
|
||||
*/
|
||||
public boolean evaluateIsZero(final double x, final double y, final double z) {
|
||||
return Math.abs(evaluate(x,y,z)) < MINIMUM_RESOLUTION;
|
||||
}
|
||||
|
||||
/** Evaluate a vector (do a dot product).
|
||||
*@param v is the vector to evaluate.
|
||||
*@return the result.
|
||||
|
|
|
@ -21,12 +21,10 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Seed;
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
import com.spatial4j.core.shape.SpatialRelation;
|
||||
import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
|
||||
import org.apache.lucene.spatial.prefix.RandomSpatialOpStrategyTestCase;
|
||||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
|
@ -34,7 +32,6 @@ import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
|||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBox;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBoxFactory;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPath;
|
||||
|
@ -80,42 +77,14 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
//@Repeat(iterations = 2000)
|
||||
@Seed("B808B88D6F8E285C")
|
||||
@Repeat(iterations = 2000)
|
||||
//@Seed("B808B88D6F8E285C")
|
||||
public void testOperations() throws IOException {
|
||||
setupStrategy();
|
||||
|
||||
testOperationRandomShapes(SpatialOperation.Intersects);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigCircleFailure() throws IOException {
|
||||
Rectangle rect = ctx.makeRectangle(-162, 89, -46, 38);
|
||||
GeoCircle rawShape = new GeoCircle(-9 * DEGREES_TO_RADIANS, 134 * DEGREES_TO_RADIANS, 159 * DEGREES_TO_RADIANS);
|
||||
Shape shape = new Geo3dShape(rawShape, ctx);
|
||||
assertTrue(rect.relate(shape).intersects() == false); //DWS: unsure if this is correct or not but passes
|
||||
//since they don't intersect, then the following cell rect can't be WITHIN the circle
|
||||
final Rectangle cellRect = ctx.makeRectangle(-11.25, 0, 0, 5.625);
|
||||
assert cellRect.relate(rect).intersects();
|
||||
assertTrue(cellRect.relate(shape) != SpatialRelation.WITHIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWideRectFailure() throws IOException {
|
||||
Rectangle rect = ctx.makeRectangle(-29, 9, 16, 25);
|
||||
final GeoBBox geoBBox = GeoBBoxFactory.makeGeoBBox(
|
||||
74 * DEGREES_TO_RADIANS, -31 * DEGREES_TO_RADIANS, -29 * DEGREES_TO_RADIANS, -45 * DEGREES_TO_RADIANS);
|
||||
Shape shape = new Geo3dShape(geoBBox, ctx);
|
||||
//Rect(minX=-22.5,maxX=-11.25,minY=11.25,maxY=16.875)
|
||||
//since they don't intersect, then the following cell rect can't be WITHIN the geo3d shape
|
||||
final Rectangle cellRect = ctx.makeRectangle(-22.5, -11.25, 11.25, 16.875);
|
||||
assert cellRect.relate(rect).intersects();
|
||||
assertTrue(rect.relate(shape).intersects() == false);
|
||||
assertTrue(cellRect.relate(shape) != SpatialRelation.WITHIN);
|
||||
// setupStrategy();
|
||||
// testOperation(rect, SpatialOperation.Intersects, shape, false);
|
||||
}
|
||||
|
||||
private Shape makeTriangle(double x1, double y1, double x2, double y2, double x3, double y3) {
|
||||
final List<GeoPoint> geoPoints = new ArrayList<>();
|
||||
geoPoints.add(new GeoPoint(y1 * DEGREES_TO_RADIANS, x1 * DEGREES_TO_RADIANS));
|
||||
|
@ -159,7 +128,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
case 1: {
|
||||
// Circles
|
||||
while (true) {
|
||||
final int circleRadius = random().nextInt(180);
|
||||
final int circleRadius = random().nextInt(179) + 1;
|
||||
final Point point = randomPoint();
|
||||
try {
|
||||
final GeoShape shape = new GeoCircle(point.getY() * DEGREES_TO_RADIANS, point.getX() * DEGREES_TO_RADIANS,
|
||||
|
@ -188,6 +157,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
lrhcPoint.getY() * DEGREES_TO_RADIANS,
|
||||
ulhcPoint.getX() * DEGREES_TO_RADIANS,
|
||||
lrhcPoint.getX() * DEGREES_TO_RADIANS);
|
||||
//System.err.println("Trial rectangle shape: "+shape);
|
||||
return new Geo3dShape(shape, ctx);
|
||||
} catch (IllegalArgumentException e) {
|
||||
// This is what happens when we create a shape that is invalid. Although it is conceivable that there are cases where
|
||||
|
|
Loading…
Reference in New Issue