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:
David Wayne Smiley 2015-04-22 14:47:00 +00:00
parent bf74c72eb2
commit 1242bf1519
31 changed files with 1726 additions and 203 deletions

View File

@ -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);

View File

@ -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);
}

View File

@ -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,

View File

@ -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.

View File

@ -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;

View File

@ -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;
}

View File

@ -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))

View File

@ -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;
}

View File

@ -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))

View File

@ -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;

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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+")}";
}
}

View File

@ -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+")}";
}
}

View File

@ -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)

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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+")}";
}
}

View File

@ -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+")}";
}
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -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;
}

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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.

View File

@ -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