mirror of https://github.com/apache/lucene.git
committing Karl's latest LUCENE-6196-fixes.patch (plus my test failures)
https://reviews.apache.org/r/33268/diff/raw/ WIP; tests fail git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/branches/lucene6196@1674732 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b2c273fa7a
commit
c3a4ee1c3b
|
@ -37,6 +37,10 @@ public interface GeoArea extends Membership {
|
|||
* other way around. For example, if this GeoArea is entirely within the
|
||||
* shape, then CONTAINS should be returned. If the shape is entirely enclosed
|
||||
* by this GeoArea, then WITHIN should be returned.
|
||||
* Note well: When a shape consists of multiple independent overlapping subshapes,
|
||||
* it is sometimes impossible to determine the distinction between
|
||||
* OVERLAPS and CONTAINS. In that case, OVERLAPS may be returned even
|
||||
* though the proper result would in fact be CONTAINS. Code accordingly.
|
||||
*@param shape is the shape to consider.
|
||||
*@return the relationship, from the perspective of the shape.
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
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.
|
||||
*/
|
||||
|
||||
/** All bounding box shapes can derive from this base class, which furnishes
|
||||
* some common code
|
||||
*/
|
||||
public abstract class GeoBBoxBase implements GeoBBox {
|
||||
|
||||
@Override
|
||||
public abstract boolean isWithin(final Vector point);
|
||||
|
||||
protected final static int ALL_INSIDE = 0;
|
||||
protected final static int SOME_INSIDE = 1;
|
||||
protected final static int NONE_INSIDE = 2;
|
||||
|
||||
protected int isShapeInsideBBox(final GeoShape path) {
|
||||
final GeoPoint[] pathPoints = path.getEdgePoints();
|
||||
boolean foundOutside = false;
|
||||
boolean foundInside = false;
|
||||
for (GeoPoint p : pathPoints) {
|
||||
if (isWithin(p)) {
|
||||
foundInside = true;
|
||||
} else {
|
||||
foundOutside = true;
|
||||
}
|
||||
}
|
||||
if (!foundInside && !foundOutside)
|
||||
return NONE_INSIDE;
|
||||
if (foundInside && !foundOutside)
|
||||
return ALL_INSIDE;
|
||||
if (foundOutside && !foundInside)
|
||||
return NONE_INSIDE;
|
||||
return SOME_INSIDE;
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ public abstract class GeoBaseExtendedShape implements GeoShape
|
|||
*@return true if the point is within this shape
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean isWithin(Vector point);
|
||||
public abstract boolean isWithin(final Vector point);
|
||||
|
||||
/** Check if a point is within this shape.
|
||||
*@param x is x coordinate of point to check.
|
||||
|
@ -42,13 +42,13 @@ public abstract class GeoBaseExtendedShape implements GeoShape
|
|||
*@return true if the point is within this shape
|
||||
*/
|
||||
@Override
|
||||
public abstract boolean isWithin(double x, double y, double z);
|
||||
public abstract boolean isWithin(final double x, final double y, final double z);
|
||||
|
||||
/** Return a sample point that is inside the shape.
|
||||
*@return an interior point.
|
||||
/** Return a sample point that is on the edge of the shape.
|
||||
*@return a number of edge points, one for each disconnected edge.
|
||||
*/
|
||||
@Override
|
||||
public abstract GeoPoint getInteriorPoint();
|
||||
public abstract GeoPoint[] getEdgePoints();
|
||||
|
||||
/** Assess whether a plane, within the provided bounds, intersects
|
||||
* with the shape.
|
||||
|
@ -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(Plane plane, Membership... bounds);
|
||||
public abstract boolean intersects(final Plane plane, final Membership... bounds);
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
*@param bounds is the optional input bounds object. If this is null,
|
||||
|
|
|
@ -26,8 +26,9 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
public final double cutoffNormalDistance;
|
||||
public final double cutoffLinearDistance;
|
||||
public final SidedPlane circlePlane;
|
||||
|
||||
public GeoCircle(double lat, double lon, double cutoffAngle)
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public GeoCircle(final double lat, final double lon, final double cutoffAngle)
|
||||
{
|
||||
super();
|
||||
if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5)
|
||||
|
@ -36,15 +37,19 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
throw new IllegalArgumentException("Longitude out of bounds");
|
||||
if (cutoffAngle < 0.0 || cutoffAngle > Math.PI)
|
||||
throw new IllegalArgumentException("Cutoff angle out of bounds");
|
||||
double sinAngle = Math.sin(cutoffAngle);
|
||||
double cosAngle = Math.cos(cutoffAngle);
|
||||
final double sinAngle = Math.sin(cutoffAngle);
|
||||
final double cosAngle = Math.cos(cutoffAngle);
|
||||
this.center = new GeoPoint(lat,lon);
|
||||
this.cutoffNormalDistance = sinAngle;
|
||||
// Need the chord distance. This is just the chord distance: sqrt((1 - cos(angle))^2 + (sin(angle))^2).
|
||||
double xDiff = 1.0 - cosAngle;
|
||||
final double xDiff = 1.0 - cosAngle;
|
||||
this.cutoffLinearDistance = Math.sqrt(xDiff * xDiff + sinAngle * sinAngle);
|
||||
this.cutoffAngle = cutoffAngle;
|
||||
this.circlePlane = new SidedPlane(center, center, -cosAngle);
|
||||
|
||||
// Compute a point on the circle boundary. 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)};
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,7 +62,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* points outside of the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeNormalDistance(GeoPoint point)
|
||||
public double computeNormalDistance(final GeoPoint point)
|
||||
{
|
||||
double normalDistance = this.center.normalDistance(point);
|
||||
if (normalDistance > cutoffNormalDistance)
|
||||
|
@ -70,7 +75,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* points outside of the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeNormalDistance(double x, double y, double z)
|
||||
public double computeNormalDistance(final double x, final double y, final double z)
|
||||
{
|
||||
double normalDistance = this.center.normalDistance(x,y,z);
|
||||
if (normalDistance > cutoffNormalDistance)
|
||||
|
@ -83,7 +88,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredNormalDistance(GeoPoint point)
|
||||
public double computeSquaredNormalDistance(final GeoPoint point)
|
||||
{
|
||||
double normalDistanceSquared = this.center.normalDistanceSquared(point);
|
||||
if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance)
|
||||
|
@ -96,7 +101,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredNormalDistance(double x, double y, double z)
|
||||
public double computeSquaredNormalDistance(final double x, final double y, final double z)
|
||||
{
|
||||
double normalDistanceSquared = this.center.normalDistanceSquared(x,y,z);
|
||||
if (normalDistanceSquared > cutoffNormalDistance * cutoffNormalDistance)
|
||||
|
@ -108,7 +113,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* return Double.MAX_VALUE for points outside the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeLinearDistance(GeoPoint point)
|
||||
public double computeLinearDistance(final GeoPoint point)
|
||||
{
|
||||
double linearDistance = this.center.linearDistance(point);
|
||||
if (linearDistance > cutoffLinearDistance)
|
||||
|
@ -120,7 +125,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* return Double.MAX_VALUE for points outside the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeLinearDistance(double x, double y, double z)
|
||||
public double computeLinearDistance(final double x, final double y, final double z)
|
||||
{
|
||||
double linearDistance = this.center.linearDistance(x,y,z);
|
||||
if (linearDistance > cutoffLinearDistance)
|
||||
|
@ -131,7 +136,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
/** Compute a squared linear distance to the vector.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredLinearDistance(GeoPoint point)
|
||||
public double computeSquaredLinearDistance(final GeoPoint point)
|
||||
{
|
||||
double linearDistanceSquared = this.center.linearDistanceSquared(point);
|
||||
if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance)
|
||||
|
@ -142,7 +147,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
/** Compute a squared linear distance to the vector.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredLinearDistance(double x, double y, double z)
|
||||
public double computeSquaredLinearDistance(final double x, final double y, final double z)
|
||||
{
|
||||
double linearDistanceSquared = this.center.linearDistanceSquared(x,y,z);
|
||||
if (linearDistanceSquared > cutoffLinearDistance * cutoffLinearDistance)
|
||||
|
@ -154,7 +159,7 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
* Double.MAX_VALUE indicates a point is outside of the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeArcDistance(GeoPoint point)
|
||||
public double computeArcDistance(final GeoPoint point)
|
||||
{
|
||||
double dist = this.center.arcDistance(point);
|
||||
if (dist > cutoffAngle)
|
||||
|
@ -163,27 +168,29 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
if (point == null)
|
||||
return false;
|
||||
// Fastest way of determining membership
|
||||
return circlePlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
// Fastest way of determining membership
|
||||
return circlePlane.isWithin(x,y,z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return center;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return circlePlane.intersects(p, bounds);
|
||||
}
|
||||
|
@ -224,8 +231,6 @@ public class GeoCircle extends GeoBaseExtendedShape implements GeoDistanceShape,
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("Circle: center = ");
|
||||
sb.append(center).append(" radius = ").append(cutoffAngle);
|
||||
return sb.toString();
|
||||
return "GeoCircle: {center="+center+", radius="+cutoffAngle+"("+cutoffAngle*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** GeoComposite is a set of GeoMembershipShape's, treated as a unit.
|
||||
*/
|
||||
|
@ -31,12 +32,12 @@ public class GeoCompositeMembershipShape implements GeoMembershipShape
|
|||
|
||||
/** Add a shape to the composite.
|
||||
*/
|
||||
public void addShape(GeoMembershipShape shape) {
|
||||
public void addShape(final GeoMembershipShape shape) {
|
||||
shapes.add(shape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
for (GeoMembershipShape shape : shapes) {
|
||||
if (shape.isWithin(point))
|
||||
|
@ -46,7 +47,7 @@ public class GeoCompositeMembershipShape implements GeoMembershipShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
for (GeoMembershipShape shape : shapes) {
|
||||
if (shape.isWithin(x,y,z))
|
||||
|
@ -56,13 +57,13 @@ public class GeoCompositeMembershipShape implements GeoMembershipShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return shapes.get(0).getInteriorPoint();
|
||||
return shapes.get(0).getEdgePoints();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
for (GeoMembershipShape shape : shapes) {
|
||||
if (shape.intersects(p,bounds))
|
||||
|
@ -112,7 +113,7 @@ public class GeoCompositeMembershipShape implements GeoMembershipShape
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoCompositeMembershipShape{" + shapes + '}';
|
||||
return "GeoCompositeMembershipShape: {" + shapes + '}';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,9 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
/** GeoConvexPolygon objects are generic building blocks of more complex structures.
|
||||
* The only restrictions on these objects are: (1) they must be convex; (2) they must have
|
||||
|
@ -27,70 +29,97 @@ import java.util.*;
|
|||
public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembershipShape
|
||||
{
|
||||
protected final List<GeoPoint> points;
|
||||
protected final BitSet isInternalEdges;
|
||||
|
||||
protected SidedPlane[] edges = null;
|
||||
protected GeoPoint interiorPoint = null;
|
||||
protected boolean[] internalEdges = null;
|
||||
|
||||
/** Create a convex polygon from a list of points.
|
||||
protected GeoPoint[] edgePoints = null;
|
||||
|
||||
/** Create a convex polygon from a list of points. The first point must be on the
|
||||
* external edge.
|
||||
*/
|
||||
public GeoConvexPolygon(List<GeoPoint> pointList) {
|
||||
public GeoConvexPolygon(final List<GeoPoint> pointList) {
|
||||
this.points = pointList;
|
||||
donePoints();
|
||||
this.isInternalEdges = null;
|
||||
donePoints(false);
|
||||
}
|
||||
|
||||
/** Create a convex polygon from a list of points, keeping track of which boundaries
|
||||
* are internal. This is used when creating a polygon as a building block for another shape.
|
||||
*/
|
||||
public GeoConvexPolygon(final List<GeoPoint> pointList, final BitSet internalEdgeFlags, final boolean returnEdgeInternal) {
|
||||
this.points = pointList;
|
||||
this.isInternalEdges = internalEdgeFlags;
|
||||
donePoints(returnEdgeInternal);
|
||||
}
|
||||
|
||||
/** Create a convex polygon, with a starting latitude and longitude.
|
||||
* Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}
|
||||
*/
|
||||
public GeoConvexPolygon(double startLatitude, double startLongitude)
|
||||
public GeoConvexPolygon(final double startLatitude, final double startLongitude)
|
||||
{
|
||||
points = new ArrayList<GeoPoint>();
|
||||
isInternalEdges = new BitSet();
|
||||
// Argument checking
|
||||
if (startLatitude > Math.PI * 0.5 || startLatitude < -Math.PI * 0.5)
|
||||
throw new IllegalArgumentException("Latitude out of range");
|
||||
if (startLongitude < -Math.PI || startLongitude > Math.PI)
|
||||
throw new IllegalArgumentException("Longitude out of range");
|
||||
|
||||
GeoPoint p = new GeoPoint(startLatitude, startLongitude);
|
||||
final GeoPoint p = new GeoPoint(startLatitude, startLongitude);
|
||||
points.add(p);
|
||||
}
|
||||
|
||||
/** Add a point to the polygon.
|
||||
* Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}
|
||||
*@param latitude is the latitude of the next point.
|
||||
*@param longitude is the longitude of the next point.
|
||||
*@param isInternalEdge is true if the edge just added should be considered "internal", and not
|
||||
* intersected as part of the intersects() operation.
|
||||
*/
|
||||
public void addPoint(double latitude, double longitude) {
|
||||
public void addPoint(final double latitude, final double longitude, final boolean isInternalEdge) {
|
||||
// Argument checking
|
||||
if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5)
|
||||
throw new IllegalArgumentException("Latitude out of range");
|
||||
if (longitude < -Math.PI || longitude > Math.PI)
|
||||
throw new IllegalArgumentException("Longitude out of range");
|
||||
|
||||
GeoPoint p = new GeoPoint(latitude, longitude);
|
||||
final GeoPoint p = new GeoPoint(latitude, longitude);
|
||||
isInternalEdges.set(points.size(),isInternalEdge);
|
||||
points.add(p);
|
||||
}
|
||||
|
||||
/** Finish the polygon, by connecting the last added point with the starting point.
|
||||
*/
|
||||
public void donePoints() {
|
||||
public void donePoints(final boolean isInternalReturnEdge) {
|
||||
// If fewer than 3 points, can't do it.
|
||||
if (points.size() < 3)
|
||||
throw new IllegalArgumentException("Polygon needs at least three points.");
|
||||
// Time to construct the planes. If the polygon is truly convex, then any adjacent point
|
||||
edges = new SidedPlane[points.size()];
|
||||
internalEdges = new boolean[points.size()];
|
||||
// to a segment can provide an interior measurement.
|
||||
for (int i = 0; i < points.size(); i++) {
|
||||
GeoPoint start = points.get(i);
|
||||
GeoPoint end = points.get(legalIndex(i+1));
|
||||
GeoPoint check = points.get(legalIndex(i+2));
|
||||
SidedPlane sp = new SidedPlane(check,start,end);
|
||||
final GeoPoint start = points.get(i);
|
||||
final boolean isInternalEdge = (isInternalEdges!=null?(i == isInternalEdges.size()?isInternalReturnEdge:isInternalEdges.get(i)):false);
|
||||
final GeoPoint end = points.get(legalIndex(i+1));
|
||||
final GeoPoint check = points.get(legalIndex(i+2));
|
||||
final SidedPlane sp = new SidedPlane(check,start,end);
|
||||
//System.out.println("Created edge "+sp+" using start="+start+" end="+end+" check="+check);
|
||||
edges[i] = sp;
|
||||
internalEdges[i] = isInternalEdge;
|
||||
}
|
||||
|
||||
createCenterPoint();
|
||||
}
|
||||
|
||||
protected void createCenterPoint() {
|
||||
// In order to naively confirm that the polygon is convex, I would need to
|
||||
// check every edge, and verify that every point (other than the edge endpoints)
|
||||
// is within the edge's sided plane. This is an order n^2 operation. That's still
|
||||
// not wrong, though, because everything else about polygons has a similar cost.
|
||||
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
||||
SidedPlane edge = edges[edgeIndex];
|
||||
final SidedPlane edge = edges[edgeIndex];
|
||||
for (int pointIndex =0; pointIndex < points.size(); pointIndex++) {
|
||||
if (pointIndex != edgeIndex && pointIndex != legalIndex(edgeIndex+1)) {
|
||||
if (!edge.isWithin(points.get(pointIndex)))
|
||||
|
@ -98,46 +127,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we need to compute a single interior point that will satisfy
|
||||
// all edges. If the polygon is convex, we know that such a point exists.
|
||||
|
||||
// This is actually surprisingly hard. I believe merely averaging the x, y, and z
|
||||
// values of the points will produce a point inside the shape, but it won't be
|
||||
// on the unit sphere, and it may be in fact degenerate and have a zero magnitude.
|
||||
// In that case, an alternate algorithm would be required. But since such cases
|
||||
// are very special (or very contrived), I'm just going to not worry about that
|
||||
// for the moment.
|
||||
double sumX = 0.0;
|
||||
double sumY = 0.0;
|
||||
double sumZ = 0.0;
|
||||
for (GeoPoint p : points) {
|
||||
sumX += p.x;
|
||||
sumY += p.y;
|
||||
sumZ += p.z;
|
||||
}
|
||||
double denom = 1.0 / (double)points.size();
|
||||
sumX *= denom;
|
||||
sumY *= denom;
|
||||
sumZ *= denom;
|
||||
double magnitude = Math.sqrt(sumX * sumX + sumY * sumY + sumZ * sumZ);
|
||||
if (magnitude < 1.0e-10)
|
||||
throw new IllegalArgumentException("Polygon interior point cannot be determined");
|
||||
denom = 1.0/magnitude;
|
||||
|
||||
interiorPoint = new GeoPoint(sumX*denom,sumY*denom,sumZ*denom);
|
||||
|
||||
// Let's be sure that our interior point is really inside
|
||||
for (SidedPlane sp : edges) {
|
||||
if (!sp.isWithin(interiorPoint)) {
|
||||
StringBuilder sb = new StringBuilder("Interior point logic failed to produce an interior point. Vertices: ");
|
||||
for (GeoPoint p : points) {
|
||||
sb.append(p).append(" ");
|
||||
}
|
||||
sb.append(". Interior point: ").append(interiorPoint);
|
||||
throw new IllegalArgumentException(sb.toString());
|
||||
}
|
||||
}
|
||||
edgePoints = new GeoPoint[]{points.get(0)};
|
||||
}
|
||||
|
||||
protected int legalIndex(int index) {
|
||||
|
@ -147,9 +137,9 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
for (SidedPlane edge : edges) {
|
||||
for (final SidedPlane edge : edges) {
|
||||
if (!edge.isWithin(point))
|
||||
return false;
|
||||
}
|
||||
|
@ -157,9 +147,9 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
for (SidedPlane edge : edges) {
|
||||
for (final SidedPlane edge : edges) {
|
||||
if (!edge.isWithin(x,y,z))
|
||||
return false;
|
||||
}
|
||||
|
@ -167,26 +157,29 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return interiorPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
||||
SidedPlane edge = edges[edgeIndex];
|
||||
// Construct boundaries
|
||||
Membership[] membershipBounds = new Membership[edges.length-1];
|
||||
int count = 0;
|
||||
for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) {
|
||||
if (otherIndex != edgeIndex) {
|
||||
membershipBounds[count++] = edges[otherIndex];
|
||||
final SidedPlane edge = edges[edgeIndex];
|
||||
if (!internalEdges[edgeIndex]) {
|
||||
// Edges flagged as 'internal only' are excluded from the matching
|
||||
// Construct boundaries
|
||||
final Membership[] membershipBounds = new Membership[edges.length-1];
|
||||
int count = 0;
|
||||
for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) {
|
||||
if (otherIndex != edgeIndex) {
|
||||
membershipBounds[count++] = edges[otherIndex];
|
||||
}
|
||||
}
|
||||
if (edge.intersects(p,bounds,membershipBounds))
|
||||
return true;
|
||||
}
|
||||
if (edge.intersects(p,bounds,membershipBounds))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -204,15 +197,15 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
bounds = super.getBounds(bounds);
|
||||
|
||||
// Add all the points
|
||||
for (GeoPoint point : points) {
|
||||
for (final GeoPoint point : points) {
|
||||
bounds.addPoint(point);
|
||||
}
|
||||
|
||||
// Add planes with membership.
|
||||
for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
|
||||
SidedPlane edge = edges[edgeIndex];
|
||||
final SidedPlane edge = edges[edgeIndex];
|
||||
// Construct boundaries
|
||||
Membership[] membershipBounds = new Membership[edges.length-1];
|
||||
final Membership[] membershipBounds = new Membership[edges.length-1];
|
||||
int count = 0;
|
||||
for (int otherIndex = 0; otherIndex < edges.length; otherIndex++) {
|
||||
if (otherIndex != edgeIndex) {
|
||||
|
@ -248,7 +241,7 @@ public class GeoConvexPolygon extends GeoBaseExtendedShape implements GeoMembers
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoConvexPolygon{" + points + "}";
|
||||
return "GeoConvexPolygon: {" + points + "}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||
* GeoWideDegenerateHorizontalLine.
|
||||
*/
|
||||
public class GeoDegenerateHorizontalLine implements GeoBBox
|
||||
public class GeoDegenerateHorizontalLine extends GeoBBoxBase
|
||||
{
|
||||
public final double latitude;
|
||||
public final double leftLon;
|
||||
|
@ -35,9 +35,10 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */
|
||||
public GeoDegenerateHorizontalLine(double latitude, double leftLon, double rightLon)
|
||||
public GeoDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5)
|
||||
|
@ -57,12 +58,12 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
this.leftLon = leftLon;
|
||||
this.rightLon = rightLon;
|
||||
|
||||
double sinLatitude = Math.sin(latitude);
|
||||
double cosLatitude = Math.cos(latitude);
|
||||
double sinLeftLon = Math.sin(leftLon);
|
||||
double cosLeftLon = Math.cos(leftLon);
|
||||
double sinRightLon = Math.sin(rightLon);
|
||||
double cosRightLon = Math.cos(rightLon);
|
||||
final double sinLatitude = Math.sin(latitude);
|
||||
final double cosLatitude = Math.cos(latitude);
|
||||
final double sinLeftLon = Math.sin(leftLon);
|
||||
final double cosLeftLon = Math.cos(leftLon);
|
||||
final double sinRightLon = Math.sin(rightLon);
|
||||
final double cosRightLon = Math.cos(rightLon);
|
||||
|
||||
// Now build the two points
|
||||
this.LHC = new GeoPoint(sinLatitude,sinLeftLon,cosLatitude,cosLeftLon);
|
||||
|
@ -74,18 +75,19 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
while (leftLon > rightLon) {
|
||||
rightLon += Math.PI * 2.0;
|
||||
}
|
||||
double middleLon = (leftLon + rightLon) * 0.5;
|
||||
double sinMiddleLon = Math.sin(middleLon);
|
||||
double cosMiddleLon = Math.cos(middleLon);
|
||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||
final double sinMiddleLon = Math.sin(middleLon);
|
||||
final double cosMiddleLon = Math.cos(middleLon);
|
||||
|
||||
centerPoint = new GeoPoint(sinLatitude,sinMiddleLon,cosLatitude,cosMiddleLon);
|
||||
this.centerPoint = new GeoPoint(sinLatitude,sinMiddleLon,cosLatitude,cosMiddleLon);
|
||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
|
||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = latitude + angle;
|
||||
double newBottomLat = latitude - angle;
|
||||
|
@ -103,7 +105,7 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
leftPlane.isWithin(point) &&
|
||||
|
@ -111,7 +113,7 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
leftPlane.isWithin(x,y,z) &&
|
||||
|
@ -127,13 +129,13 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds,leftPlane,rightPlane);
|
||||
}
|
||||
|
@ -155,7 +157,7 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.intersects(plane,leftPlane,rightPlane))
|
||||
return OVERLAPS;
|
||||
|
||||
|
@ -180,6 +182,11 @@ public class GeoDegenerateHorizontalLine implements GeoBBox
|
|||
result = 31 * result + RHC.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoDegenerateHorizontalLine: {latitude="+latitude+"("+latitude*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightLon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -20,15 +20,16 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
/** This GeoBBox represents an area rectangle of one specific latitude with
|
||||
* no longitude bounds.
|
||||
*/
|
||||
public class GeoDegenerateLatitudeZone implements GeoBBox
|
||||
public class GeoDegenerateLatitudeZone extends GeoBBoxBase
|
||||
{
|
||||
public final double latitude;
|
||||
|
||||
public final double sinLatitude;
|
||||
public final Plane plane;
|
||||
public final GeoPoint interiorPoint;
|
||||
|
||||
public GeoDegenerateLatitudeZone(double latitude)
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public GeoDegenerateLatitudeZone(final double latitude)
|
||||
{
|
||||
this.latitude = latitude;
|
||||
|
||||
|
@ -38,10 +39,11 @@ public class GeoDegenerateLatitudeZone implements GeoBBox
|
|||
|
||||
// Compute an interior point.
|
||||
interiorPoint = new GeoPoint(cosLatitude,0.0,sinLatitude);
|
||||
edgePoints = new GeoPoint[]{interiorPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = latitude + angle;
|
||||
double newBottomLat = latitude - angle;
|
||||
|
@ -49,13 +51,13 @@ public class GeoDegenerateLatitudeZone implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return point.z == this.sinLatitude;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return z == this.sinLatitude;
|
||||
}
|
||||
|
@ -67,13 +69,13 @@ public class GeoDegenerateLatitudeZone implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return interiorPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds);
|
||||
}
|
||||
|
@ -95,7 +97,7 @@ public class GeoDegenerateLatitudeZone implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
// Second, the shortcut of seeing whether endpoints are in/out is not going to
|
||||
// work with no area endpoints. So we rely entirely on intersections.
|
||||
//System.out.println("Got here! latitude="+latitude+" path="+path);
|
||||
|
@ -126,5 +128,10 @@ public class GeoDegenerateLatitudeZone implements GeoBBox
|
|||
int result = (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoDegenerateLatitudeZone: {lat="+latitude+"("+latitude*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
|
||||
/** Degenerate longitude slice.
|
||||
*/
|
||||
public class GeoDegenerateLongitudeSlice implements GeoBBox
|
||||
public class GeoDegenerateLongitudeSlice extends GeoBBoxBase
|
||||
{
|
||||
public final double longitude;
|
||||
|
||||
|
@ -28,9 +28,10 @@ public class GeoDegenerateLongitudeSlice implements GeoBBox
|
|||
public final SidedPlane boundingPlane;
|
||||
public final Plane plane;
|
||||
public final GeoPoint interiorPoint;
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
/** Accepts only values in the following ranges: lon: {@code -PI -> PI} */
|
||||
public GeoDegenerateLongitudeSlice(double longitude)
|
||||
public GeoDegenerateLongitudeSlice(final double longitude)
|
||||
{
|
||||
// Argument checking
|
||||
if (longitude < -Math.PI || longitude > Math.PI)
|
||||
|
@ -44,10 +45,11 @@ public class GeoDegenerateLongitudeSlice implements GeoBBox
|
|||
// We need a bounding plane too, which is perpendicular to the longitude plane and sided so that the point (0.0, longitude) is inside.
|
||||
this.interiorPoint = new GeoPoint(cosLongitude, sinLongitude, 0.0);
|
||||
this.boundingPlane = new SidedPlane(interiorPoint, -sinLongitude, cosLongitude);
|
||||
this.edgePoints = new GeoPoint[]{interiorPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
// Figuring out when we escalate to a special case requires some prefiguring
|
||||
double newLeftLon = longitude - angle;
|
||||
|
@ -61,14 +63,14 @@ public class GeoDegenerateLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
boundingPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
boundingPlane.isWithin(x,y,z);
|
||||
|
@ -81,13 +83,13 @@ public class GeoDegenerateLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return interiorPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds,boundingPlane);
|
||||
}
|
||||
|
@ -110,7 +112,7 @@ public class GeoDegenerateLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
// Look for intersections.
|
||||
if (path.intersects(plane,boundingPlane))
|
||||
return OVERLAPS;
|
||||
|
@ -138,6 +140,11 @@ public class GeoDegenerateLongitudeSlice implements GeoBBox
|
|||
result = (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoDegenerateLongitudeSlice: {longitude="+longitude+"("+longitude*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -24,12 +24,14 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox
|
|||
{
|
||||
public final double latitude;
|
||||
public final double longitude;
|
||||
|
||||
public GeoDegeneratePoint(double lat, double lon)
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public GeoDegeneratePoint(final double lat, final double lon)
|
||||
{
|
||||
super(lat,lon);
|
||||
this.latitude = lat;
|
||||
this.longitude = lon;
|
||||
this.edgePoints = new GeoPoint[]{this};
|
||||
}
|
||||
|
||||
/** Expand box by specified angle.
|
||||
|
@ -37,20 +39,20 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox
|
|||
*@return a new GeoBBox.
|
||||
*/
|
||||
@Override
|
||||
public GeoBBox expand(double angle) {
|
||||
double newTopLat = latitude + angle;
|
||||
double newBottomLat = latitude - angle;
|
||||
double newLeftLon = longitude - angle;
|
||||
double newRightLon = longitude + angle;
|
||||
public GeoBBox expand(final double angle) {
|
||||
final double newTopLat = latitude + angle;
|
||||
final double newBottomLat = latitude - angle;
|
||||
final double newLeftLon = longitude - angle;
|
||||
final double newRightLon = longitude + angle;
|
||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, newLeftLon, newRightLon);
|
||||
}
|
||||
|
||||
/** Return a sample point that is inside the shape.
|
||||
/** Return a sample point that is on the edge of the shape.
|
||||
*@return an interior point.
|
||||
*/
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint() {
|
||||
return this;
|
||||
public GeoPoint[] getEdgePoints() {
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
/** Assess whether a plane, within the provided bounds, intersects
|
||||
|
@ -62,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(Plane plane, Membership... bounds) {
|
||||
public boolean intersects(final Plane plane, final Membership... bounds) {
|
||||
if (plane.evaluate(this) == 0.0)
|
||||
return false;
|
||||
|
||||
|
@ -108,12 +110,17 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoDegeneratePoint: {lat="+latitude+"("+latitude*180.0/Math.PI+"), lon="+longitude+"("+longitude*180.0/Math.PI+")}";
|
||||
}
|
||||
|
||||
/** Check if a point is within this shape.
|
||||
*@param point is the point to check.
|
||||
*@return true if the point is within this shape
|
||||
*/
|
||||
@Override
|
||||
public boolean isWithin(Vector point) {
|
||||
public boolean isWithin(final Vector point) {
|
||||
return isWithin(point.x,point.y,point.z);
|
||||
}
|
||||
|
||||
|
@ -124,7 +131,7 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox
|
|||
*@return true if the point is within this shape
|
||||
*/
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z) {
|
||||
public boolean isWithin(final double x, final double y, final double z) {
|
||||
return x == this.x && y == this.y && z == this.z;
|
||||
}
|
||||
|
||||
|
@ -146,7 +153,7 @@ public class GeoDegeneratePoint extends GeoPoint implements GeoBBox
|
|||
*@return the relationship, from the perspective of the shape.
|
||||
*/
|
||||
@Override
|
||||
public int getRelationship(GeoShape shape) {
|
||||
public int getRelationship(final GeoShape shape) {
|
||||
if (shape.isWithin(this))
|
||||
return CONTAINS;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
|
||||
/** Degenerate bounding box limited on two sides (top lat, bottom lat).
|
||||
*/
|
||||
public class GeoDegenerateVerticalLine implements GeoBBox
|
||||
public class GeoDegenerateVerticalLine extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double bottomLat;
|
||||
|
@ -34,9 +34,10 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
public final Plane plane;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, longitude: {@code -PI -> PI} */
|
||||
public GeoDegenerateVerticalLine(double topLat, double bottomLat, double longitude)
|
||||
public GeoDegenerateVerticalLine(final double topLat, final double bottomLat, final double longitude)
|
||||
{
|
||||
// Argument checking
|
||||
if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
|
||||
|
@ -52,12 +53,12 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
this.bottomLat = bottomLat;
|
||||
this.longitude = longitude;
|
||||
|
||||
double sinTopLat = Math.sin(topLat);
|
||||
double cosTopLat = Math.cos(topLat);
|
||||
double sinBottomLat = Math.sin(bottomLat);
|
||||
double cosBottomLat = Math.cos(bottomLat);
|
||||
double sinLongitude = Math.sin(longitude);
|
||||
double cosLongitude = Math.cos(longitude);
|
||||
final double sinTopLat = Math.sin(topLat);
|
||||
final double cosTopLat = Math.cos(topLat);
|
||||
final double sinBottomLat = Math.sin(bottomLat);
|
||||
final double cosBottomLat = Math.cos(bottomLat);
|
||||
final double sinLongitude = Math.sin(longitude);
|
||||
final double cosLongitude = Math.cos(longitude);
|
||||
|
||||
// Now build the two points
|
||||
this.UHC = new GeoPoint(sinTopLat,sinLongitude,cosTopLat,cosLongitude);
|
||||
|
@ -65,24 +66,25 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
|
||||
this.plane = new Plane(cosLongitude,sinLongitude);
|
||||
|
||||
double middleLat = (topLat + bottomLat) * 0.5;
|
||||
double sinMiddleLat = Math.sin(middleLat);
|
||||
double cosMiddleLat = Math.cos(middleLat);
|
||||
final double middleLat = (topLat + bottomLat) * 0.5;
|
||||
final double sinMiddleLat = Math.sin(middleLat);
|
||||
final double cosMiddleLat = Math.cos(middleLat);
|
||||
|
||||
centerPoint = new GeoPoint(sinMiddleLat,sinLongitude,cosMiddleLat,cosLongitude);
|
||||
this.centerPoint = new GeoPoint(sinMiddleLat,sinLongitude,cosMiddleLat,cosLongitude);
|
||||
|
||||
this.topPlane = new SidedPlane(centerPoint,sinTopLat);
|
||||
this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat);
|
||||
|
||||
this.boundingPlane = new SidedPlane(centerPoint,-sinLongitude,cosLongitude);
|
||||
|
||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = topLat + angle;
|
||||
double newBottomLat = bottomLat - angle;
|
||||
final double newTopLat = topLat + angle;
|
||||
final double newBottomLat = bottomLat - angle;
|
||||
double newLeftLon = longitude - angle;
|
||||
double newRightLon = longitude + angle;
|
||||
double currentLonSpan = 2.0 * angle;
|
||||
|
@ -94,7 +96,7 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
boundingPlane.isWithin(point) &&
|
||||
|
@ -103,7 +105,7 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
boundingPlane.isWithin(x,y,z) &&
|
||||
|
@ -117,19 +119,19 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
// 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.
|
||||
double topAngle = centerPoint.arcDistance(UHC);
|
||||
double bottomAngle = centerPoint.arcDistance(LHC);
|
||||
final double topAngle = centerPoint.arcDistance(UHC);
|
||||
final double bottomAngle = centerPoint.arcDistance(LHC);
|
||||
return Math.max(topAngle,bottomAngle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(plane,bounds,boundingPlane,topPlane,bottomPlane);
|
||||
}
|
||||
|
@ -152,7 +154,7 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.intersects(plane,boundingPlane,topPlane,bottomPlane))
|
||||
return OVERLAPS;
|
||||
|
||||
|
@ -177,6 +179,11 @@ public class GeoDegenerateVerticalLine implements GeoBBox
|
|||
result = 31 * result + LHC.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoDegenerateVerticalLine: {longitude="+longitude+"("+longitude*180.0/Math.PI+"), toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
|
||||
/** This GeoBBox represents an area rectangle limited only in latitude.
|
||||
*/
|
||||
public class GeoLatitudeZone implements GeoBBox
|
||||
public class GeoLatitudeZone extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double bottomLat;
|
||||
|
@ -29,46 +29,58 @@ public class GeoLatitudeZone implements GeoBBox
|
|||
public final SidedPlane bottomPlane;
|
||||
public final GeoPoint interiorPoint;
|
||||
|
||||
public GeoLatitudeZone(double topLat, double bottomLat)
|
||||
// We need two additional points because a latitude zone's boundaries don't intersect. This is a very
|
||||
// special case that most GeoBBox's do not have.
|
||||
public final GeoPoint topBoundaryPoint;
|
||||
public final GeoPoint bottomBoundaryPoint;
|
||||
|
||||
// Edge points
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
public GeoLatitudeZone(final double topLat, final double bottomLat)
|
||||
{
|
||||
this.topLat = topLat;
|
||||
this.bottomLat = bottomLat;
|
||||
|
||||
double sinTopLat = Math.sin(topLat);
|
||||
double sinBottomLat = Math.sin(bottomLat);
|
||||
cosTopLat = Math.cos(topLat);
|
||||
cosBottomLat = Math.cos(bottomLat);
|
||||
final double sinTopLat = Math.sin(topLat);
|
||||
final double sinBottomLat = Math.sin(bottomLat);
|
||||
this.cosTopLat = Math.cos(topLat);
|
||||
this.cosBottomLat = Math.cos(bottomLat);
|
||||
|
||||
// Construct sample points, so we get our sidedness right
|
||||
Vector topPoint = new Vector(0.0,0.0,sinTopLat);
|
||||
Vector bottomPoint = new Vector(0.0,0.0,sinBottomLat);
|
||||
final Vector topPoint = new Vector(0.0,0.0,sinTopLat);
|
||||
final Vector bottomPoint = new Vector(0.0,0.0,sinBottomLat);
|
||||
|
||||
// Compute an interior point. Pick one whose lat is between top and bottom.
|
||||
double middleLat = (topLat + bottomLat) * 0.5;
|
||||
double sinMiddleLat = Math.sin(middleLat);
|
||||
interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat);
|
||||
final double middleLat = (topLat + bottomLat) * 0.5;
|
||||
final double sinMiddleLat = Math.sin(middleLat);
|
||||
this.interiorPoint = new GeoPoint(Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat),0.0,sinMiddleLat);
|
||||
this.topBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinTopLat * sinTopLat),0.0,sinTopLat);
|
||||
this.bottomBoundaryPoint = new GeoPoint(Math.sqrt(1.0 - sinBottomLat * sinBottomLat),0.0,sinBottomLat);
|
||||
|
||||
this.topPlane = new SidedPlane(interiorPoint,sinTopLat);
|
||||
this.bottomPlane = new SidedPlane(interiorPoint,sinBottomLat);
|
||||
|
||||
this.edgePoints = new GeoPoint[]{topBoundaryPoint,bottomBoundaryPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = topLat + angle;
|
||||
double newBottomLat = bottomLat - angle;
|
||||
final double newTopLat = topLat + angle;
|
||||
final double newBottomLat = bottomLat - angle;
|
||||
return GeoBBoxFactory.makeGeoBBox(newTopLat, newBottomLat, -Math.PI, Math.PI);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return topPlane.isWithin(point) &&
|
||||
bottomPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return topPlane.isWithin(x,y,z) &&
|
||||
bottomPlane.isWithin(x,y,z);
|
||||
|
@ -88,13 +100,13 @@ public class GeoLatitudeZone implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return interiorPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(topPlane,bounds,bottomPlane) ||
|
||||
p.intersects(bottomPlane,bounds,topPlane);
|
||||
|
@ -117,7 +129,23 @@ public class GeoLatitudeZone implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint);
|
||||
final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint);
|
||||
|
||||
if (topBoundaryInsideShape && !bottomBoundaryInsideShape ||
|
||||
!topBoundaryInsideShape && bottomBoundaryInsideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape;
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
// Second, the shortcut of seeing whether endpoints are in/out is not going to
|
||||
// work with no area endpoints. So we rely entirely on intersections.
|
||||
|
||||
|
@ -125,10 +153,16 @@ public class GeoLatitudeZone implements GeoBBox
|
|||
path.intersects(bottomPlane,topPlane))
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.isWithin(interiorPoint))
|
||||
// There is another case for latitude zones only. This is when the boundaries of the shape all fit
|
||||
// within the zone, but the shape includes areas outside the zone crossing a pole.
|
||||
// In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
|
||||
// whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
|
||||
// one such point is within, then OVERLAPS is the right answer.
|
||||
|
||||
if (insideShape)
|
||||
return CONTAINS;
|
||||
|
||||
if (isWithin(path.getInteriorPoint()))
|
||||
if (insideRectangle == ALL_INSIDE)
|
||||
return WITHIN;
|
||||
|
||||
return DISJOINT;
|
||||
|
@ -149,4 +183,9 @@ public class GeoLatitudeZone implements GeoBBox
|
|||
result = 31 * result + bottomPlane.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoLatitudeZone: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||
* GeoWideLongitudeSlice.
|
||||
*/
|
||||
public class GeoLongitudeSlice implements GeoBBox
|
||||
public class GeoLongitudeSlice extends GeoBBoxBase
|
||||
{
|
||||
public final double leftLon;
|
||||
public final double rightLon;
|
||||
|
@ -30,9 +30,11 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final GeoPoint northPole = new GeoPoint(0.0,0.0,1.0);
|
||||
public final GeoPoint[] edgePoints = new GeoPoint[]{northPole};
|
||||
|
||||
/** Accepts only values in the following ranges: lon: {@code -PI -> PI} */
|
||||
public GeoLongitudeSlice(double leftLon, double rightLon)
|
||||
public GeoLongitudeSlice(final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (leftLon < -Math.PI || leftLon > Math.PI)
|
||||
|
@ -49,17 +51,17 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
this.leftLon = leftLon;
|
||||
this.rightLon = rightLon;
|
||||
|
||||
double sinLeftLon = Math.sin(leftLon);
|
||||
double cosLeftLon = Math.cos(leftLon);
|
||||
double sinRightLon = Math.sin(rightLon);
|
||||
double cosRightLon = Math.cos(rightLon);
|
||||
final double sinLeftLon = Math.sin(leftLon);
|
||||
final double cosLeftLon = Math.cos(leftLon);
|
||||
final double sinRightLon = Math.sin(rightLon);
|
||||
final double cosRightLon = Math.cos(rightLon);
|
||||
|
||||
// Normalize
|
||||
while (leftLon > rightLon) {
|
||||
rightLon += Math.PI * 2.0;
|
||||
}
|
||||
double middleLon = (leftLon + rightLon) * 0.5;
|
||||
centerPoint = new GeoPoint(0.0,middleLon);
|
||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||
this.centerPoint = new GeoPoint(0.0,middleLon);
|
||||
|
||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
|
@ -67,7 +69,7 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
// Figuring out when we escalate to a special case requires some prefiguring
|
||||
double currentLonSpan = rightLon - leftLon;
|
||||
|
@ -83,14 +85,14 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return leftPlane.isWithin(point) &&
|
||||
rightPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return leftPlane.isWithin(x,y,z) &&
|
||||
rightPlane.isWithin(x,y,z);
|
||||
|
@ -107,13 +109,13 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(leftPlane,bounds,rightPlane) ||
|
||||
p.intersects(rightPlane,bounds,leftPlane);
|
||||
|
@ -137,17 +139,26 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(northPole);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.intersects(leftPlane,rightPlane) ||
|
||||
path.intersects(rightPlane,leftPlane)) {
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
||||
if (isWithin(path.getInteriorPoint())) {
|
||||
if (insideRectangle == ALL_INSIDE) {
|
||||
return WITHIN;
|
||||
}
|
||||
|
||||
if (path.isWithin(centerPoint)) {
|
||||
if (insideShape) {
|
||||
return CONTAINS;
|
||||
}
|
||||
|
||||
|
@ -173,5 +184,10 @@ public class GeoLongitudeSlice implements GeoBBox
|
|||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoLongitudeSlice: {leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,8 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** GeoSearchableShape representing a path across the surface of the globe,
|
||||
* with a specified half-width. Path is described by a series of points.
|
||||
|
@ -34,14 +35,16 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
public final List<SegmentEndpoint> points = new ArrayList<SegmentEndpoint>();
|
||||
public final List<PathSegment> segments = new ArrayList<PathSegment>();
|
||||
|
||||
public GeoPath(double cutoffAngle)
|
||||
public GeoPoint[] edgePoints = null;
|
||||
|
||||
public GeoPath(final double cutoffAngle)
|
||||
{
|
||||
super();
|
||||
if (cutoffAngle < 0.0 || cutoffAngle > Math.PI * 0.5)
|
||||
if (cutoffAngle <= 0.0 || cutoffAngle > Math.PI * 0.5)
|
||||
throw new IllegalArgumentException("Cutoff angle out of bounds");
|
||||
this.cutoffAngle = cutoffAngle;
|
||||
double cosAngle = Math.cos(cutoffAngle);
|
||||
double sinAngle = Math.sin(cutoffAngle);
|
||||
final double cosAngle = Math.cos(cutoffAngle);
|
||||
final double sinAngle = Math.sin(cutoffAngle);
|
||||
// Cutoff offset is the linear distance given the angle
|
||||
this.cutoffOffset = sinAngle;
|
||||
this.originDistance = cosAngle;
|
||||
|
@ -56,22 +59,27 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
throw new IllegalArgumentException("Latitude out of range");
|
||||
if (lon < -Math.PI || lon > Math.PI)
|
||||
throw new IllegalArgumentException("Longitude out of range");
|
||||
GeoPoint end = new GeoPoint(lat,lon);
|
||||
final GeoPoint end = new GeoPoint(lat,lon);
|
||||
if (points.size() > 0) {
|
||||
GeoPoint start = points.get(points.size()-1).point;
|
||||
PathSegment ps = new PathSegment(start,end,cutoffOffset,cutoffAngle,chordDistance);
|
||||
final GeoPoint start = points.get(points.size()-1).point;
|
||||
final PathSegment ps = new PathSegment(start,end,cutoffOffset,cutoffAngle,chordDistance);
|
||||
// Check for degeneracy; if the segment is degenerate, don't include the point
|
||||
if (ps.isDegenerate())
|
||||
return;
|
||||
segments.add(ps);
|
||||
}
|
||||
SegmentEndpoint se = new SegmentEndpoint(end, originDistance, cutoffOffset, cutoffAngle, chordDistance);
|
||||
final SegmentEndpoint se = new SegmentEndpoint(end, originDistance, cutoffOffset, cutoffAngle, chordDistance);
|
||||
points.add(se);
|
||||
}
|
||||
|
||||
public void done() {
|
||||
if (points.size() == 0)
|
||||
throw new IllegalArgumentException("Path must have at least one point");
|
||||
if (segments.size() > 0) {
|
||||
edgePoints = new GeoPoint[]{points.get(0).circlePlane.getSampleIntersectionPoint(segments.get(0).invertedStartCutoffPlane)};
|
||||
} else {
|
||||
edgePoints = new GeoPoint[]{points.get(0).point.getSamplePoint(cutoffOffset,originDistance)};
|
||||
}
|
||||
}
|
||||
|
||||
/** Compute an estimate of "distance" to the GeoPoint.
|
||||
|
@ -79,7 +87,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
* points outside of the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeNormalDistance(GeoPoint point)
|
||||
public double computeNormalDistance(final GeoPoint point)
|
||||
{
|
||||
// Algorithm:
|
||||
// (1) If the point is within any of the segments along the path, return that value.
|
||||
|
@ -110,7 +118,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
* points outside of the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeNormalDistance(double x, double y, double z)
|
||||
public double computeNormalDistance(final double x, final double y, final double z)
|
||||
{
|
||||
return computeNormalDistance(new GeoPoint(x,y,z));
|
||||
}
|
||||
|
@ -120,7 +128,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
* shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredNormalDistance(GeoPoint point)
|
||||
public double computeSquaredNormalDistance(final GeoPoint point)
|
||||
{
|
||||
double pd = computeNormalDistance(point);
|
||||
if (pd == Double.MAX_VALUE)
|
||||
|
@ -133,7 +141,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
* shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredNormalDistance(double x, double y, double z)
|
||||
public double computeSquaredNormalDistance(final double x, final double y, final double z)
|
||||
{
|
||||
return computeSquaredNormalDistance(new GeoPoint(x,y,z));
|
||||
}
|
||||
|
@ -141,7 +149,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
/** Compute a linear distance to the point.
|
||||
*/
|
||||
@Override
|
||||
public double computeLinearDistance(GeoPoint point)
|
||||
public double computeLinearDistance(final GeoPoint point)
|
||||
{
|
||||
// Algorithm:
|
||||
// (1) If the point is within any of the segments along the path, return that value.
|
||||
|
@ -170,7 +178,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
/** Compute a linear distance to the point.
|
||||
*/
|
||||
@Override
|
||||
public double computeLinearDistance(double x, double y, double z)
|
||||
public double computeLinearDistance(final double x, final double y, final double z)
|
||||
{
|
||||
return computeLinearDistance(new GeoPoint(x,y,z));
|
||||
}
|
||||
|
@ -178,7 +186,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
/** Compute a squared linear distance to the vector.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredLinearDistance(GeoPoint point)
|
||||
public double computeSquaredLinearDistance(final GeoPoint point)
|
||||
{
|
||||
double pd = computeLinearDistance(point);
|
||||
if (pd == Double.MAX_VALUE)
|
||||
|
@ -189,7 +197,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
/** Compute a squared linear distance to the vector.
|
||||
*/
|
||||
@Override
|
||||
public double computeSquaredLinearDistance(double x, double y, double z)
|
||||
public double computeSquaredLinearDistance(final double x, final double y, final double z)
|
||||
{
|
||||
return computeSquaredLinearDistance(new GeoPoint(x,y,z));
|
||||
}
|
||||
|
@ -198,7 +206,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
* Double.MAX_VALUE indicates a point is outside of the shape.
|
||||
*/
|
||||
@Override
|
||||
public double computeArcDistance(GeoPoint point)
|
||||
public double computeArcDistance(final GeoPoint point)
|
||||
{
|
||||
// Algorithm:
|
||||
// (1) If the point is within any of the segments along the path, return that value.
|
||||
|
@ -225,7 +233,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
for (SegmentEndpoint pathPoint : points) {
|
||||
if (pathPoint.isWithin(point))
|
||||
|
@ -239,7 +247,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
for (SegmentEndpoint pathPoint : points) {
|
||||
if (pathPoint.isWithin(x,y,z))
|
||||
|
@ -253,13 +261,13 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return points.get(0).point;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane plane, Membership... bounds)
|
||||
public boolean intersects(final Plane plane, 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.
|
||||
|
@ -268,14 +276,26 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
// For plane intersections, the basic idea is to come up with an equation of the line that is
|
||||
// the intersection (if any). Then, find the intersections with the unit sphere (if any). If
|
||||
// any of the intersection points are within the bounds, then we've detected an intersection.
|
||||
// Well, sort of. We can detect intersections also due to overlap of segments with each other.
|
||||
// But that's an edge case and we won't be optimizing for it.
|
||||
|
||||
for (SegmentEndpoint pathPoint : points) {
|
||||
if (pathPoint.intersects(plane, bounds))
|
||||
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)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (PathSegment pathSegment : segments) {
|
||||
if (pathSegment.intersects(plane, bounds))
|
||||
if (pathSegment.intersects(plane, bounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -329,6 +349,11 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoPath: {width="+cutoffAngle+"("+cutoffAngle*180.0/Math.PI+"), points={"+points+"}}";
|
||||
}
|
||||
|
||||
/** This is precalculated data for segment endpoint.
|
||||
*/
|
||||
public static class SegmentEndpoint
|
||||
|
@ -339,7 +364,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
public final double cutoffAngle;
|
||||
public final double chordDistance;
|
||||
|
||||
public SegmentEndpoint(GeoPoint point, double originDistance, double cutoffOffset, double cutoffAngle, double chordDistance)
|
||||
public SegmentEndpoint(final GeoPoint point, final double originDistance, final double cutoffOffset, final double cutoffAngle, final double chordDistance)
|
||||
{
|
||||
this.point = point;
|
||||
this.cutoffNormalDistance = cutoffOffset;
|
||||
|
@ -348,17 +373,17 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
this.circlePlane = new SidedPlane(point, point, -originDistance);
|
||||
}
|
||||
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return circlePlane.isWithin(point);
|
||||
}
|
||||
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return circlePlane.isWithin(x,y,z);
|
||||
}
|
||||
|
||||
public double pathDistance(GeoPoint point)
|
||||
public double pathDistance(final GeoPoint point)
|
||||
{
|
||||
double dist = this.point.arcDistance(point);
|
||||
if (dist > cutoffAngle)
|
||||
|
@ -366,7 +391,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return dist;
|
||||
}
|
||||
|
||||
public double pathNormalDistance(GeoPoint point)
|
||||
public double pathNormalDistance(final GeoPoint point)
|
||||
{
|
||||
double dist = this.point.normalDistance(point);
|
||||
if (dist > cutoffNormalDistance)
|
||||
|
@ -374,7 +399,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return dist;
|
||||
}
|
||||
|
||||
public double pathLinearDistance(GeoPoint point)
|
||||
public double pathLinearDistance(final GeoPoint point)
|
||||
{
|
||||
double dist = this.point.linearDistance(point);
|
||||
if (dist > chordDistance)
|
||||
|
@ -382,9 +407,9 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return dist;
|
||||
}
|
||||
|
||||
public boolean intersects(Plane p, Membership[] bounds)
|
||||
public boolean intersects(final Plane p, final Membership[] bounds, final Membership previousEndCutoff, final Membership nextStartCutoff)
|
||||
{
|
||||
return circlePlane.intersects(p, bounds);
|
||||
return circlePlane.intersects(p, bounds, previousEndCutoff, nextStartCutoff);
|
||||
}
|
||||
|
||||
public void getBounds(Bounds bounds)
|
||||
|
@ -404,6 +429,11 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
public int hashCode() {
|
||||
return point.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return point.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/** This is the precalculated data for a path segment.
|
||||
|
@ -424,7 +454,11 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
public final double arcWidth;
|
||||
public final double chordDistance;
|
||||
|
||||
public PathSegment(GeoPoint start, GeoPoint end, double planeBoundingOffset, double arcWidth, double chordDistance)
|
||||
// For the adjoining SegmentEndpoint...
|
||||
public final SidedPlane invertedStartCutoffPlane;
|
||||
public final SidedPlane invertedEndCutoffPlane;
|
||||
|
||||
public PathSegment(final GeoPoint start, final GeoPoint end, final double planeBoundingOffset, final double arcWidth, final double chordDistance)
|
||||
{
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
@ -441,6 +475,8 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
lowerConnectingPlane = null;
|
||||
startCutoffPlane = null;
|
||||
endCutoffPlane = null;
|
||||
invertedStartCutoffPlane = null;
|
||||
invertedEndCutoffPlane = null;
|
||||
} else {
|
||||
// Either start or end should be on the correct side
|
||||
upperConnectingPlane = new SidedPlane(start,normalizedConnectingPlane,-planeBoundingOffset);
|
||||
|
@ -448,6 +484,8 @@ 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);
|
||||
invertedStartCutoffPlane = new SidedPlane(startCutoffPlane);
|
||||
invertedEndCutoffPlane = new SidedPlane(endCutoffPlane);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -456,7 +494,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return normalizedConnectingPlane == null;
|
||||
}
|
||||
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return startCutoffPlane.isWithin(point) &&
|
||||
endCutoffPlane.isWithin(point) &&
|
||||
|
@ -464,7 +502,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
lowerConnectingPlane.isWithin(point);
|
||||
}
|
||||
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return startCutoffPlane.isWithin(x,y,z) &&
|
||||
endCutoffPlane.isWithin(x,y,z) &&
|
||||
|
@ -472,40 +510,40 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
lowerConnectingPlane.isWithin(x,y,z);
|
||||
}
|
||||
|
||||
public double pathDistance(GeoPoint point)
|
||||
public double pathDistance(final GeoPoint point)
|
||||
{
|
||||
if (!isWithin(point))
|
||||
return Double.MAX_VALUE;
|
||||
|
||||
// Compute the distance, filling in both components.
|
||||
double perpDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedConnectingPlane.evaluate(point)));
|
||||
Plane normalizedPerpPlane = new Plane(normalizedConnectingPlane,point).normalize();
|
||||
double pathDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedPerpPlane.evaluate(start)));
|
||||
final double perpDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedConnectingPlane.evaluate(point)));
|
||||
final Plane normalizedPerpPlane = new Plane(normalizedConnectingPlane,point).normalize();
|
||||
final double pathDistance = Math.PI * 0.5 - Tools.safeAcos(Math.abs(normalizedPerpPlane.evaluate(start)));
|
||||
return perpDistance + pathDistance;
|
||||
}
|
||||
|
||||
public double pathNormalDistance(GeoPoint point)
|
||||
public double pathNormalDistance(final GeoPoint point)
|
||||
{
|
||||
if (!isWithin(point))
|
||||
return Double.MAX_VALUE;
|
||||
|
||||
double pointEval = Math.abs(normalizedConnectingPlane.evaluate(point));
|
||||
final double pointEval = Math.abs(normalizedConnectingPlane.evaluate(point));
|
||||
|
||||
// Want no allocations or expensive operations! so we do this the hard way
|
||||
double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y;
|
||||
double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z;
|
||||
double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
|
||||
final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y;
|
||||
final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z;
|
||||
final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
|
||||
|
||||
// If we have a degenerate line, then just compute the normal distance from point x to the start
|
||||
if (perpX < 1e-10 && perpY < 1e-10 && perpZ < 1e-10)
|
||||
return point.normalDistance(start);
|
||||
|
||||
double normFactor = 1.0 / Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ);
|
||||
double perpEval = Math.abs(perpX * start.x + perpY * start.y + perpZ * start.z);
|
||||
final double normFactor = 1.0 / Math.sqrt(perpX * perpX + perpY * perpY + perpZ * perpZ);
|
||||
final double perpEval = Math.abs(perpX * start.x + perpY * start.y + perpZ * start.z);
|
||||
return perpEval * normFactor + pointEval;
|
||||
}
|
||||
|
||||
public double pathLinearDistance(GeoPoint point)
|
||||
public double pathLinearDistance(final GeoPoint point)
|
||||
{
|
||||
if (!isWithin(point))
|
||||
return Double.MAX_VALUE;
|
||||
|
@ -513,9 +551,9 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
// We have a normalized connecting plane.
|
||||
// First, compute the perpendicular plane.
|
||||
// Want no allocations or expensive operations! so we do this the hard way
|
||||
double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y;
|
||||
double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z;
|
||||
double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
|
||||
final double perpX = normalizedConnectingPlane.y * point.z - normalizedConnectingPlane.z * point.y;
|
||||
final double perpY = normalizedConnectingPlane.z * point.x - normalizedConnectingPlane.x * point.z;
|
||||
final double perpZ = normalizedConnectingPlane.x * point.y - normalizedConnectingPlane.y * point.x;
|
||||
|
||||
// If we have a degenerate line, then just compute the normal distance from point x to the start
|
||||
if (Math.abs(perpX) < 1e-10 && Math.abs(perpY) < 1e-10 && Math.abs(perpZ) < 1e-10)
|
||||
|
@ -523,12 +561,12 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
|
||||
// Next, we need the vector of the line, which is the cross product of the normalized connecting plane
|
||||
// and the perpendicular plane that we just calculated.
|
||||
double lineX = normalizedConnectingPlane.y * perpZ - normalizedConnectingPlane.z * perpY;
|
||||
double lineY = normalizedConnectingPlane.z * perpX - normalizedConnectingPlane.x * perpZ;
|
||||
double lineZ = normalizedConnectingPlane.x * perpY - normalizedConnectingPlane.y * perpX;
|
||||
final double lineX = normalizedConnectingPlane.y * perpZ - normalizedConnectingPlane.z * perpY;
|
||||
final double lineY = normalizedConnectingPlane.z * perpX - normalizedConnectingPlane.x * perpZ;
|
||||
final double lineZ = normalizedConnectingPlane.x * perpY - normalizedConnectingPlane.y * perpX;
|
||||
|
||||
// Now, compute a normalization factor
|
||||
double normalizer = 1.0/Math.sqrt(lineX * lineX + lineY * lineY + lineZ * lineZ);
|
||||
final double normalizer = 1.0/Math.sqrt(lineX * lineX + lineY * lineY + lineZ * lineZ);
|
||||
|
||||
// Pick which point by using bounding planes
|
||||
double normLineX = lineX * normalizer;
|
||||
|
@ -546,7 +584,7 @@ public class GeoPath extends GeoBaseExtendedShape implements GeoDistanceShape
|
|||
return point.linearDistance(normLineX,normLineY,normLineZ) + start.linearDistance(normLineX,normLineY,normLineZ);
|
||||
}
|
||||
|
||||
public boolean intersects(Plane p, Membership[] bounds)
|
||||
public boolean intersects(final Plane p, final Membership[] bounds)
|
||||
{
|
||||
return upperConnectingPlane.intersects(p, bounds, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
|
||||
lowerConnectingPlane.intersects(p, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
|
||||
|
|
|
@ -21,24 +21,62 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
*/
|
||||
public class GeoPoint extends Vector
|
||||
{
|
||||
public GeoPoint(double sinLat, double sinLon, double cosLat, double cosLon)
|
||||
public GeoPoint(final double sinLat, final double sinLon, final double cosLat, final double cosLon)
|
||||
{
|
||||
super(cosLat*cosLon,cosLat*sinLon,sinLat);
|
||||
}
|
||||
|
||||
public GeoPoint(double lat, double lon)
|
||||
public GeoPoint(final double lat, final double lon)
|
||||
{
|
||||
this(Math.sin(lat),Math.sin(lon),Math.cos(lat),Math.cos(lon));
|
||||
}
|
||||
|
||||
public GeoPoint(double x, double y, double z)
|
||||
public GeoPoint(final double x, final double y, final double z)
|
||||
{
|
||||
super(x,y,z);
|
||||
}
|
||||
|
||||
public double arcDistance(GeoPoint v)
|
||||
public double arcDistance(final GeoPoint v)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.BitSet;
|
||||
import java.util.List;
|
||||
|
||||
/** Class which constructs a GeoMembershipShape representing an arbitrary polygon.
|
||||
*/
|
||||
|
@ -38,25 +39,28 @@ public class GeoPolygonFactory
|
|||
// describing membership.
|
||||
return buildPolygonShape(pointList, convexPointIndex, getLegalIndex(convexPointIndex+1,pointList.size()),
|
||||
new SidedPlane(pointList.get(getLegalIndex(convexPointIndex-1,pointList.size())),
|
||||
pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex+1,pointList.size()))));
|
||||
pointList.get(convexPointIndex), pointList.get(getLegalIndex(convexPointIndex+1,pointList.size()))),
|
||||
false);
|
||||
}
|
||||
|
||||
public static GeoMembershipShape buildPolygonShape(List<GeoPoint> pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge) {
|
||||
public static GeoMembershipShape buildPolygonShape(List<GeoPoint> pointsList, int startPointIndex, int endPointIndex, SidedPlane startingEdge, boolean isInternalEdge) {
|
||||
// Algorithm as follows:
|
||||
// Start with sided edge. Go through all points in some order. For each new point, determine if the point is within all edges considered so far.
|
||||
// If not, put it into a list of points for recursion. If it is within, add new edge and keep going.
|
||||
// Once we detect a point that is within, if there are points put aside for recursion, then call recursively.
|
||||
|
||||
// Current composite. This is what we'll actually be returning.
|
||||
GeoCompositeMembershipShape rval = new GeoCompositeMembershipShape();
|
||||
final GeoCompositeMembershipShape rval = new GeoCompositeMembershipShape();
|
||||
|
||||
List<GeoPoint> recursionList = new ArrayList<GeoPoint>();
|
||||
List<GeoPoint> currentList = new ArrayList<GeoPoint>();
|
||||
List<SidedPlane> currentPlanes = new ArrayList<SidedPlane>();
|
||||
final List<GeoPoint> recursionList = new ArrayList<GeoPoint>();
|
||||
final List<GeoPoint> currentList = new ArrayList<GeoPoint>();
|
||||
final BitSet internalEdgeList = new BitSet();
|
||||
final List<SidedPlane> currentPlanes = new ArrayList<SidedPlane>();
|
||||
|
||||
// Initialize the current list and current planes
|
||||
currentList.add(pointsList.get(startPointIndex));
|
||||
currentList.add(pointsList.get(endPointIndex));
|
||||
internalEdgeList.set(currentPlanes.size(),isInternalEdge);
|
||||
currentPlanes.add(startingEdge);
|
||||
|
||||
// Now, scan all remaining points, in order. We'll use an index and just add to it.
|
||||
|
@ -92,7 +96,8 @@ public class GeoPolygonFactory
|
|||
}
|
||||
if (!pointInside) {
|
||||
// Any excluded points?
|
||||
if (recursionList.size() > 0) {
|
||||
boolean isInternalBoundary = recursionList.size() > 0;
|
||||
if (isInternalBoundary) {
|
||||
// Handle exclusion
|
||||
recursionList.add(newPoint);
|
||||
recursionList.add(currentList.get(currentList.size()-1));
|
||||
|
@ -103,10 +108,11 @@ public class GeoPolygonFactory
|
|||
}
|
||||
// We want the other side for the recursion
|
||||
SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary);
|
||||
rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary));
|
||||
rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary,true));
|
||||
recursionList.clear();
|
||||
}
|
||||
currentList.add(newPoint);
|
||||
internalEdgeList.set(currentPlanes.size(),isInternalBoundary);
|
||||
currentPlanes.add(newBoundary);
|
||||
} else {
|
||||
recursionList.add(newPoint);
|
||||
|
@ -116,7 +122,8 @@ public class GeoPolygonFactory
|
|||
}
|
||||
}
|
||||
|
||||
if (recursionList.size() > 0) {
|
||||
boolean returnEdgeInternalBoundary = recursionList.size() > 0;
|
||||
if (returnEdgeInternalBoundary) {
|
||||
// The last step back to the start point had a recursion, so take care of that before we complete our work
|
||||
recursionList.add(currentList.get(0));
|
||||
recursionList.add(currentList.get(currentList.size()-1));
|
||||
|
@ -129,18 +136,11 @@ public class GeoPolygonFactory
|
|||
SidedPlane newBoundary = new SidedPlane(currentList.get(currentList.size()-2),currentList.get(0),currentList.get(currentList.size()-1));
|
||||
// We want the other side for the recursion
|
||||
SidedPlane otherSideNewBoundary = new SidedPlane(newBoundary);
|
||||
rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary));
|
||||
rval.addShape(buildPolygonShape(recursionList,recursionList.size()-2,recursionList.size()-1,otherSideNewBoundary,true));
|
||||
recursionList.clear();
|
||||
}
|
||||
|
||||
// Now, add in the current shape.
|
||||
/*
|
||||
System.out.println("Creating polygon:");
|
||||
for (GeoPoint p : currentList) {
|
||||
System.out.println(" "+p);
|
||||
}
|
||||
*/
|
||||
rval.addShape(new GeoConvexPolygon(currentList));
|
||||
rval.addShape(new GeoConvexPolygon(currentList,internalEdgeList,returnEdgeInternalBoundary));
|
||||
//System.out.println("Done creating polygon");
|
||||
return rval;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* The left-right maximum extent for this shape is PI; for anything larger, use
|
||||
* GeoWideRectangle.
|
||||
*/
|
||||
public class GeoRectangle implements GeoBBox
|
||||
public class GeoRectangle extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double bottomLat;
|
||||
|
@ -42,8 +42,10 @@ public class GeoRectangle implements GeoBBox
|
|||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI} */
|
||||
public GeoRectangle(double topLat, double bottomLat, double leftLon, double rightLon)
|
||||
public GeoRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
|
||||
|
@ -68,14 +70,14 @@ public class GeoRectangle implements GeoBBox
|
|||
this.leftLon = leftLon;
|
||||
this.rightLon = rightLon;
|
||||
|
||||
double sinTopLat = Math.sin(topLat);
|
||||
double cosTopLat = Math.cos(topLat);
|
||||
double sinBottomLat = Math.sin(bottomLat);
|
||||
double cosBottomLat = Math.cos(bottomLat);
|
||||
double sinLeftLon = Math.sin(leftLon);
|
||||
double cosLeftLon = Math.cos(leftLon);
|
||||
double sinRightLon = Math.sin(rightLon);
|
||||
double cosRightLon = Math.cos(rightLon);
|
||||
final double sinTopLat = Math.sin(topLat);
|
||||
final double cosTopLat = Math.cos(topLat);
|
||||
final double sinBottomLat = Math.sin(bottomLat);
|
||||
final double cosBottomLat = Math.cos(bottomLat);
|
||||
final double sinLeftLon = Math.sin(leftLon);
|
||||
final double cosLeftLon = Math.cos(leftLon);
|
||||
final double sinRightLon = Math.sin(rightLon);
|
||||
final double cosRightLon = Math.cos(rightLon);
|
||||
|
||||
// Now build the four points
|
||||
this.ULHC = new GeoPoint(sinTopLat,sinLeftLon,cosTopLat,cosLeftLon);
|
||||
|
@ -83,31 +85,32 @@ public class GeoRectangle implements GeoBBox
|
|||
this.LRHC = new GeoPoint(sinBottomLat,sinRightLon,cosBottomLat,cosRightLon);
|
||||
this.LLHC = new GeoPoint(sinBottomLat,sinLeftLon,cosBottomLat,cosLeftLon);
|
||||
|
||||
double middleLat = (topLat + bottomLat) * 0.5;
|
||||
double sinMiddleLat = Math.sin(middleLat);
|
||||
cosMiddleLat = Math.cos(middleLat);
|
||||
final double middleLat = (topLat + bottomLat) * 0.5;
|
||||
final double sinMiddleLat = Math.sin(middleLat);
|
||||
this.cosMiddleLat = Math.cos(middleLat);
|
||||
// Normalize
|
||||
while (leftLon > rightLon) {
|
||||
rightLon += Math.PI * 2.0;
|
||||
}
|
||||
double middleLon = (leftLon + rightLon) * 0.5;
|
||||
double sinMiddleLon = Math.sin(middleLon);
|
||||
double cosMiddleLon = Math.cos(middleLon);
|
||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||
final double sinMiddleLon = Math.sin(middleLon);
|
||||
final double cosMiddleLon = Math.cos(middleLon);
|
||||
|
||||
centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon);
|
||||
this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon);
|
||||
|
||||
this.topPlane = new SidedPlane(centerPoint,sinTopLat);
|
||||
this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat);
|
||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
|
||||
this.edgePoints = new GeoPoint[]{ULHC};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = topLat + angle;
|
||||
double newBottomLat = bottomLat - angle;
|
||||
final double newTopLat = topLat + angle;
|
||||
final double newBottomLat = bottomLat - angle;
|
||||
// Figuring out when we escalate to a special case requires some prefiguring
|
||||
double currentLonSpan = rightLon - leftLon;
|
||||
if (currentLonSpan < 0.0)
|
||||
|
@ -122,7 +125,7 @@ public class GeoRectangle implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return topPlane.isWithin(point) &&
|
||||
bottomPlane.isWithin(point) &&
|
||||
|
@ -131,7 +134,7 @@ public class GeoRectangle implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return topPlane.isWithin(x,y,z) &&
|
||||
bottomPlane.isWithin(x,y,z) &&
|
||||
|
@ -145,20 +148,20 @@ public class GeoRectangle implements GeoBBox
|
|||
// 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.
|
||||
double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
|
||||
double topAngle = centerPoint.arcDistance(URHC);
|
||||
double bottomAngle = centerPoint.arcDistance(LLHC);
|
||||
final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
|
||||
final double topAngle = centerPoint.arcDistance(URHC);
|
||||
final double bottomAngle = centerPoint.arcDistance(LLHC);
|
||||
return Math.max(centerAngle,Math.max(topAngle,bottomAngle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return p.intersects(topPlane,bounds,bottomPlane,leftPlane,rightPlane) ||
|
||||
p.intersects(bottomPlane,bounds,topPlane,leftPlane,rightPlane) ||
|
||||
|
@ -184,17 +187,26 @@ public class GeoRectangle implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == 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))
|
||||
return OVERLAPS;
|
||||
|
||||
if (isWithin(path.getInteriorPoint()))
|
||||
if (insideRectangle == ALL_INSIDE)
|
||||
return WITHIN;
|
||||
|
||||
if (path.isWithin(centerPoint))
|
||||
if (insideShape)
|
||||
return CONTAINS;
|
||||
|
||||
return DISJOINT;
|
||||
|
@ -215,5 +227,10 @@ public class GeoRectangle implements GeoBBox
|
|||
result = 31 * result + LRHC.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoRectangle: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,20 +23,25 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
*/
|
||||
public interface GeoShape extends Membership {
|
||||
|
||||
/** Return a sample point that is inside the shape.
|
||||
*@return an interior point.
|
||||
/** Return a sample point that is on the outside edge/boundary of the shape.
|
||||
*@return samples of all edge points from distinct edge sections. Typically one point
|
||||
* is returned, but zero or two are also possible.
|
||||
*/
|
||||
public GeoPoint getInteriorPoint();
|
||||
public GeoPoint[] getEdgePoints();
|
||||
|
||||
/** Assess whether a plane, within the provided bounds, intersects
|
||||
* with the shape.
|
||||
* with the shape. Note well that this method is allowed to return "true"
|
||||
* if there are internal edges of a composite shape which intersect the plane.
|
||||
* Doing this can cause getRelationship() for most GeoBBox shapes to return
|
||||
* OVERLAPS rather than the more correct CONTAINS, but that cannot be
|
||||
* helped for some complex shapes that are built out of overlapping parts.
|
||||
*@param plane is the plane to assess for intersection with the shape's edges or
|
||||
* bounding curves.
|
||||
*@param 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(Plane plane, Membership... bounds);
|
||||
public boolean intersects(final Plane plane, final Membership... bounds);
|
||||
|
||||
/** Compute longitude/latitude bounds for the shape.
|
||||
*@param bounds is the optional input bounds object. If this is null,
|
||||
|
@ -45,7 +50,7 @@ public interface GeoShape extends Membership {
|
|||
* be computed, then return a Bounds object with noLongitudeBound,
|
||||
* noTopLatitudeBound, and noBottomLatitudeBound.
|
||||
*/
|
||||
public Bounds getBounds(Bounds bounds);
|
||||
public Bounds getBounds(final Bounds bounds);
|
||||
|
||||
/** Equals */
|
||||
public boolean equals(Object o);
|
||||
|
|
|
@ -19,7 +19,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
|
||||
/** Degenerate bounding box wider than PI and limited on two sides (left lon, right lon).
|
||||
*/
|
||||
public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
||||
public class GeoWideDegenerateHorizontalLine extends GeoBBoxBase
|
||||
{
|
||||
public final double latitude;
|
||||
public final double leftLon;
|
||||
|
@ -36,10 +36,12 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
|
||||
public final EitherBound eitherBound;
|
||||
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}.
|
||||
* Horizontal angle must be greater than or equal to PI.
|
||||
*/
|
||||
public GeoWideDegenerateHorizontalLine(double latitude, double leftLon, double rightLon)
|
||||
public GeoWideDegenerateHorizontalLine(final double latitude, final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5)
|
||||
|
@ -59,12 +61,12 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
this.leftLon = leftLon;
|
||||
this.rightLon = rightLon;
|
||||
|
||||
double sinLatitude = Math.sin(latitude);
|
||||
double cosLatitude = Math.cos(latitude);
|
||||
double sinLeftLon = Math.sin(leftLon);
|
||||
double cosLeftLon = Math.cos(leftLon);
|
||||
double sinRightLon = Math.sin(rightLon);
|
||||
double cosRightLon = Math.cos(rightLon);
|
||||
final double sinLatitude = Math.sin(latitude);
|
||||
final double cosLatitude = Math.cos(latitude);
|
||||
final double sinLeftLon = Math.sin(leftLon);
|
||||
final double cosLeftLon = Math.cos(leftLon);
|
||||
final double sinRightLon = Math.sin(rightLon);
|
||||
final double cosRightLon = Math.cos(rightLon);
|
||||
|
||||
// Now build the two points
|
||||
this.LHC = new GeoPoint(sinLatitude,sinLeftLon,cosLatitude,cosLeftLon);
|
||||
|
@ -80,19 +82,21 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
double sinMiddleLon = Math.sin(middleLon);
|
||||
double cosMiddleLon = Math.cos(middleLon);
|
||||
|
||||
centerPoint = new GeoPoint(sinLatitude,sinMiddleLon,cosLatitude,cosMiddleLon);
|
||||
this.centerPoint = new GeoPoint(sinLatitude,sinMiddleLon,cosLatitude,cosMiddleLon);
|
||||
|
||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
|
||||
this.eitherBound = new EitherBound();
|
||||
|
||||
this.edgePoints = new GeoPoint[]{centerPoint};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = latitude + angle;
|
||||
double newBottomLat = latitude - angle;
|
||||
final double newTopLat = latitude + angle;
|
||||
final double newBottomLat = latitude - angle;
|
||||
// Figuring out when we escalate to a special case requires some prefiguring
|
||||
double currentLonSpan = rightLon - leftLon;
|
||||
if (currentLonSpan < 0.0)
|
||||
|
@ -107,15 +111,17 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
if (point == null)
|
||||
return false;
|
||||
return plane.evaluate(point) == 0.0 &&
|
||||
(leftPlane.isWithin(point) ||
|
||||
rightPlane.isWithin(point));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return plane.evaluate(x,y,z) == 0.0 &&
|
||||
(leftPlane.isWithin(x,y,z) ||
|
||||
|
@ -128,19 +134,19 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
// 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.
|
||||
double topAngle = centerPoint.arcDistance(RHC);
|
||||
double bottomAngle = centerPoint.arcDistance(LHC);
|
||||
final double topAngle = centerPoint.arcDistance(RHC);
|
||||
final double bottomAngle = centerPoint.arcDistance(LHC);
|
||||
return Math.max(topAngle,bottomAngle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, 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.
|
||||
|
@ -165,8 +171,7 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.intersects(plane,eitherBound)) {
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
@ -194,17 +199,22 @@ public class GeoWideDegenerateHorizontalLine implements GeoBBox
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoWideDegenerateHorizontalLine: {latitude="+latitude+"("+latitude*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightLon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
||||
}
|
||||
|
||||
protected class EitherBound implements Membership {
|
||||
public EitherBound() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector v) {
|
||||
public boolean isWithin(final Vector v) {
|
||||
return leftPlane.isWithin(v) || rightPlane.isWithin(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z) {
|
||||
public boolean isWithin(final double x, final double y, final double z) {
|
||||
return leftPlane.isWithin(x,y,z) || rightPlane.isWithin(x,y,z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
/** Bounding box wider than PI but limited on left and right sides (
|
||||
* left lon, right lon).
|
||||
*/
|
||||
public class GeoWideLongitudeSlice implements GeoBBox
|
||||
public class GeoWideLongitudeSlice extends GeoBBoxBase
|
||||
{
|
||||
public final double leftLon;
|
||||
public final double rightLon;
|
||||
|
@ -29,11 +29,13 @@ public class GeoWideLongitudeSlice implements GeoBBox
|
|||
public final SidedPlane rightPlane;
|
||||
|
||||
public final GeoPoint centerPoint;
|
||||
|
||||
public final GeoPoint northPole = new GeoPoint(0.0,0.0,1.0);
|
||||
public final GeoPoint[] edgePoints = new GeoPoint[]{northPole};
|
||||
|
||||
/** Accepts only values in the following ranges: lon: {@code -PI -> PI}.
|
||||
* Horizantal angle must be greater than or equal to PI.
|
||||
*/
|
||||
public GeoWideLongitudeSlice(double leftLon, double rightLon)
|
||||
public GeoWideLongitudeSlice(final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (leftLon < -Math.PI || leftLon > Math.PI)
|
||||
|
@ -50,24 +52,24 @@ public class GeoWideLongitudeSlice implements GeoBBox
|
|||
this.leftLon = leftLon;
|
||||
this.rightLon = rightLon;
|
||||
|
||||
double sinLeftLon = Math.sin(leftLon);
|
||||
double cosLeftLon = Math.cos(leftLon);
|
||||
double sinRightLon = Math.sin(rightLon);
|
||||
double cosRightLon = Math.cos(rightLon);
|
||||
final double sinLeftLon = Math.sin(leftLon);
|
||||
final double cosLeftLon = Math.cos(leftLon);
|
||||
final double sinRightLon = Math.sin(rightLon);
|
||||
final double cosRightLon = Math.cos(rightLon);
|
||||
|
||||
// Normalize
|
||||
while (leftLon > rightLon) {
|
||||
rightLon += Math.PI * 2.0;
|
||||
}
|
||||
double middleLon = (leftLon + rightLon) * 0.5;
|
||||
centerPoint = new GeoPoint(0.0,middleLon);
|
||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||
this.centerPoint = new GeoPoint(0.0,middleLon);
|
||||
|
||||
this.leftPlane = new SidedPlane(centerPoint,cosLeftLon,sinLeftLon);
|
||||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
// Figuring out when we escalate to a special case requires some prefiguring
|
||||
double currentLonSpan = rightLon - leftLon;
|
||||
|
@ -83,14 +85,14 @@ public class GeoWideLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return leftPlane.isWithin(point) ||
|
||||
rightPlane.isWithin(point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return leftPlane.isWithin(x,y,z) ||
|
||||
rightPlane.isWithin(x,y,z);
|
||||
|
@ -107,13 +109,13 @@ public class GeoWideLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, 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.
|
||||
|
@ -139,15 +141,24 @@ public class GeoWideLongitudeSlice implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(northPole);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.intersects(leftPlane) ||
|
||||
path.intersects(rightPlane))
|
||||
return OVERLAPS;
|
||||
|
||||
if (isWithin(path.getInteriorPoint()))
|
||||
if (insideRectangle == ALL_INSIDE)
|
||||
return WITHIN;
|
||||
|
||||
if (path.isWithin(centerPoint))
|
||||
if (insideShape)
|
||||
return CONTAINS;
|
||||
|
||||
return DISJOINT;
|
||||
|
@ -172,5 +183,10 @@ public class GeoWideLongitudeSlice implements GeoBBox
|
|||
result = 31 * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoWideLongitudeSlice: {leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
/** Bounding box wider than PI but limited on four sides (top lat,
|
||||
* bottom lat, left lon, right lon).
|
||||
*/
|
||||
public class GeoWideRectangle implements GeoBBox
|
||||
public class GeoWideRectangle extends GeoBBoxBase
|
||||
{
|
||||
public final double topLat;
|
||||
public final double bottomLat;
|
||||
|
@ -43,10 +43,12 @@ public class GeoWideRectangle implements GeoBBox
|
|||
|
||||
public final EitherBound eitherBound;
|
||||
|
||||
public final GeoPoint[] edgePoints;
|
||||
|
||||
/** Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}.
|
||||
* Horizontal angle must be greater than or equal to PI.
|
||||
*/
|
||||
public GeoWideRectangle(double topLat, double bottomLat, double leftLon, double rightLon)
|
||||
public GeoWideRectangle(final double topLat, final double bottomLat, final double leftLon, double rightLon)
|
||||
{
|
||||
// Argument checking
|
||||
if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
|
||||
|
@ -71,14 +73,14 @@ public class GeoWideRectangle implements GeoBBox
|
|||
this.leftLon = leftLon;
|
||||
this.rightLon = rightLon;
|
||||
|
||||
double sinTopLat = Math.sin(topLat);
|
||||
double cosTopLat = Math.cos(topLat);
|
||||
double sinBottomLat = Math.sin(bottomLat);
|
||||
double cosBottomLat = Math.cos(bottomLat);
|
||||
double sinLeftLon = Math.sin(leftLon);
|
||||
double cosLeftLon = Math.cos(leftLon);
|
||||
double sinRightLon = Math.sin(rightLon);
|
||||
double cosRightLon = Math.cos(rightLon);
|
||||
final double sinTopLat = Math.sin(topLat);
|
||||
final double cosTopLat = Math.cos(topLat);
|
||||
final double sinBottomLat = Math.sin(bottomLat);
|
||||
final double cosBottomLat = Math.cos(bottomLat);
|
||||
final double sinLeftLon = Math.sin(leftLon);
|
||||
final double cosLeftLon = Math.cos(leftLon);
|
||||
final double sinRightLon = Math.sin(rightLon);
|
||||
final double cosRightLon = Math.cos(rightLon);
|
||||
|
||||
// Now build the four points
|
||||
this.ULHC = new GeoPoint(sinTopLat,sinLeftLon,cosTopLat,cosLeftLon);
|
||||
|
@ -86,18 +88,18 @@ public class GeoWideRectangle implements GeoBBox
|
|||
this.LRHC = new GeoPoint(sinBottomLat,sinRightLon,cosBottomLat,cosRightLon);
|
||||
this.LLHC = new GeoPoint(sinBottomLat,sinLeftLon,cosBottomLat,cosLeftLon);
|
||||
|
||||
double middleLat = (topLat + bottomLat) * 0.5;
|
||||
double sinMiddleLat = Math.sin(middleLat);
|
||||
cosMiddleLat = Math.cos(middleLat);
|
||||
final double middleLat = (topLat + bottomLat) * 0.5;
|
||||
final double sinMiddleLat = Math.sin(middleLat);
|
||||
this.cosMiddleLat = Math.cos(middleLat);
|
||||
// Normalize
|
||||
while (leftLon > rightLon) {
|
||||
rightLon += Math.PI * 2.0;
|
||||
}
|
||||
double middleLon = (leftLon + rightLon) * 0.5;
|
||||
double sinMiddleLon = Math.sin(middleLon);
|
||||
double cosMiddleLon = Math.cos(middleLon);
|
||||
final double middleLon = (leftLon + rightLon) * 0.5;
|
||||
final double sinMiddleLon = Math.sin(middleLon);
|
||||
final double cosMiddleLon = Math.cos(middleLon);
|
||||
|
||||
centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon);
|
||||
this.centerPoint = new GeoPoint(sinMiddleLat,sinMiddleLon,cosMiddleLat,cosMiddleLon);
|
||||
|
||||
this.topPlane = new SidedPlane(centerPoint,sinTopLat);
|
||||
this.bottomPlane = new SidedPlane(centerPoint,sinBottomLat);
|
||||
|
@ -105,13 +107,15 @@ public class GeoWideRectangle implements GeoBBox
|
|||
this.rightPlane = new SidedPlane(centerPoint,cosRightLon,sinRightLon);
|
||||
|
||||
this.eitherBound = new EitherBound();
|
||||
|
||||
this.edgePoints = new GeoPoint[]{ULHC};
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
double newTopLat = topLat + angle;
|
||||
double newBottomLat = bottomLat - angle;
|
||||
final double newTopLat = topLat + angle;
|
||||
final double newBottomLat = bottomLat - angle;
|
||||
// Figuring out when we escalate to a special case requires some prefiguring
|
||||
double currentLonSpan = rightLon - leftLon;
|
||||
if (currentLonSpan < 0.0)
|
||||
|
@ -126,7 +130,7 @@ public class GeoWideRectangle implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return topPlane.isWithin(point) &&
|
||||
bottomPlane.isWithin(point) &&
|
||||
|
@ -135,7 +139,7 @@ public class GeoWideRectangle implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return topPlane.isWithin(x,y,z) &&
|
||||
bottomPlane.isWithin(x,y,z) &&
|
||||
|
@ -149,20 +153,20 @@ public class GeoWideRectangle implements GeoBBox
|
|||
// 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.
|
||||
double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
|
||||
double topAngle = centerPoint.arcDistance(URHC);
|
||||
double bottomAngle = centerPoint.arcDistance(LLHC);
|
||||
final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
|
||||
final double topAngle = centerPoint.arcDistance(URHC);
|
||||
final double bottomAngle = centerPoint.arcDistance(LLHC);
|
||||
return Math.max(centerAngle,Math.max(topAngle,bottomAngle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return centerPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, 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.
|
||||
|
@ -190,19 +194,31 @@ public class GeoWideRectangle implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
public int getRelationship(final GeoShape path) {
|
||||
final int insideRectangle = isShapeInsideBBox(path);
|
||||
if (insideRectangle == SOME_INSIDE)
|
||||
return OVERLAPS;
|
||||
|
||||
final boolean insideShape = path.isWithin(ULHC);
|
||||
|
||||
if (insideRectangle == ALL_INSIDE && insideShape)
|
||||
return OVERLAPS;
|
||||
|
||||
if (path.intersects(topPlane,bottomPlane,eitherBound) ||
|
||||
path.intersects(bottomPlane,topPlane,eitherBound) ||
|
||||
path.intersects(leftPlane,topPlane,bottomPlane) ||
|
||||
path.intersects(rightPlane,topPlane,bottomPlane))
|
||||
path.intersects(rightPlane,topPlane,bottomPlane)) {
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
||||
if (isWithin(path.getInteriorPoint()))
|
||||
if (insideRectangle == ALL_INSIDE) {
|
||||
return WITHIN;
|
||||
}
|
||||
|
||||
if (path.isWithin(centerPoint))
|
||||
if (insideShape) {
|
||||
return CONTAINS;
|
||||
|
||||
}
|
||||
|
||||
return DISJOINT;
|
||||
}
|
||||
|
||||
|
@ -222,17 +238,22 @@ public class GeoWideRectangle implements GeoBBox
|
|||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoWideRectangle: {toplat="+topLat+"("+topLat*180.0/Math.PI+"), bottomlat="+bottomLat+"("+bottomLat*180.0/Math.PI+"), leftlon="+leftLon+"("+leftLon*180.0/Math.PI+"), rightlon="+rightLon+"("+rightLon*180.0/Math.PI+")}";
|
||||
}
|
||||
|
||||
protected class EitherBound implements Membership {
|
||||
public EitherBound() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector v) {
|
||||
public boolean isWithin(final Vector v) {
|
||||
return leftPlane.isWithin(v) || rightPlane.isWithin(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z) {
|
||||
public boolean isWithin(final double x, final double y, final double z) {
|
||||
return leftPlane.isWithin(x,y,z) || rightPlane.isWithin(x,y,z);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,16 +19,17 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
|
||||
/** Bounding box including the entire world.
|
||||
*/
|
||||
public class GeoWorld implements GeoBBox
|
||||
public class GeoWorld extends GeoBBoxBase
|
||||
{
|
||||
protected final static GeoPoint originPoint = new GeoPoint(1.0,0.0,0.0);
|
||||
|
||||
protected final static GeoPoint[] edgePoints = new GeoPoint[0];
|
||||
|
||||
public GeoWorld()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoBBox expand(double angle)
|
||||
public GeoBBox expand(final double angle)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
@ -40,25 +41,25 @@ public class GeoWorld implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(Vector point)
|
||||
public boolean isWithin(final Vector point)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWithin(double x, double y, double z)
|
||||
public boolean isWithin(final double x, final double y, final double z)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeoPoint getInteriorPoint()
|
||||
public GeoPoint[] getEdgePoints()
|
||||
{
|
||||
return originPoint;
|
||||
return edgePoints;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(Plane p, Membership... bounds)
|
||||
public boolean intersects(final Plane p, final Membership... bounds)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -80,9 +81,12 @@ public class GeoWorld implements GeoBBox
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getRelationship(GeoShape path) {
|
||||
// Path is always within the world
|
||||
return WITHIN;
|
||||
public int getRelationship(final GeoShape path) {
|
||||
if (path.getEdgePoints().length > 0)
|
||||
// Path is always within the world
|
||||
return WITHIN;
|
||||
|
||||
return OVERLAPS;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,4 +101,9 @@ public class GeoWorld implements GeoBBox
|
|||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GeoWorld";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public interface Membership {
|
|||
*@param point is the point to check.
|
||||
*@return true if the point is within this shape
|
||||
*/
|
||||
public boolean isWithin(Vector point);
|
||||
public boolean isWithin(final Vector point);
|
||||
|
||||
/** Check if a point is within this shape.
|
||||
*@param x is x coordinate of point to check.
|
||||
|
@ -33,6 +33,6 @@ public interface Membership {
|
|||
*@param z is z coordinate of point to check.
|
||||
*@return true if the point is within this shape
|
||||
*/
|
||||
public boolean isWithin(double x, double y, double z);
|
||||
public boolean isWithin(final double x, final double y, final double z);
|
||||
|
||||
}
|
||||
|
|
|
@ -22,13 +22,16 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
*/
|
||||
public class Plane extends Vector
|
||||
{
|
||||
protected final static GeoPoint[] NO_POINTS = new GeoPoint[0];
|
||||
protected final static Membership[] NO_BOUNDS = new Membership[0];
|
||||
|
||||
public final double D;
|
||||
|
||||
/** Construct a plane through two points and origin.
|
||||
*@param A is the first point (origin based).
|
||||
*@param B is the second point (origin based).
|
||||
*/
|
||||
public Plane(Vector A, Vector B) {
|
||||
public Plane(final Vector A, final Vector B) {
|
||||
super(A,B);
|
||||
D = 0.0;
|
||||
}
|
||||
|
@ -36,7 +39,7 @@ public class Plane extends Vector
|
|||
/** Construct a horizontal plane at a specified Z.
|
||||
*@param height is the specified Z coordinate.
|
||||
*/
|
||||
public Plane(double height) {
|
||||
public Plane(final double height) {
|
||||
super(0.0,0.0,1.0);
|
||||
D = -height;
|
||||
}
|
||||
|
@ -46,7 +49,7 @@ public class Plane extends Vector
|
|||
*@param x is the specified x value.
|
||||
*@param y is the specified y value.
|
||||
*/
|
||||
public Plane(double x, double y) {
|
||||
public Plane(final double x, final double y) {
|
||||
super(y,-x,0.0);
|
||||
D = 0.0;
|
||||
}
|
||||
|
@ -55,7 +58,7 @@ public class Plane extends Vector
|
|||
* from origin.
|
||||
*@param D is the D offset from the origin.
|
||||
*/
|
||||
public Plane(Vector v, double D) {
|
||||
public Plane(final Vector v, final double D) {
|
||||
super(v.x,v.y,v.z);
|
||||
this.D = D;
|
||||
}
|
||||
|
@ -65,7 +68,7 @@ public class Plane extends Vector
|
|||
*@param v is the vector.
|
||||
*@return the result of the evaluation.
|
||||
*/
|
||||
public double evaluate(Vector v) {
|
||||
public double evaluate(final Vector v) {
|
||||
return super.evaluate(v) + D;
|
||||
}
|
||||
|
||||
|
@ -85,11 +88,11 @@ public class Plane extends Vector
|
|||
*@param moreBounds is another set of bounds.
|
||||
*@return the intersection point(s) on the unit sphere, if there are any.
|
||||
*/
|
||||
protected GeoPoint[] findIntersections(Plane q, Membership[] bounds, Membership[] moreBounds) {
|
||||
Vector lineVector = new Vector(this,q);
|
||||
protected GeoPoint[] findIntersections(final Plane q, final Membership[] bounds, final Membership[] moreBounds) {
|
||||
final Vector lineVector = new Vector(this,q);
|
||||
if (lineVector.x == 0.0 && lineVector.y == 0.0 && lineVector.z == 0.0) {
|
||||
// Degenerate case: parallel planes
|
||||
return new GeoPoint[0];
|
||||
return NO_POINTS;
|
||||
}
|
||||
|
||||
// The line will have the equation: A t + A0 = x, B t + B0 = y, C t + C0 = z.
|
||||
|
@ -115,30 +118,30 @@ public class Plane extends Vector
|
|||
double y0;
|
||||
double z0;
|
||||
// We try to maximize the determinant in the denominator
|
||||
double denomYZ = this.y*q.z - this.z*q.y;
|
||||
double denomXZ = this.x*q.z - this.z*q.x;
|
||||
double denomXY = this.x*q.y - this.y*q.x;
|
||||
final double denomYZ = this.y*q.z - this.z*q.y;
|
||||
final double denomXZ = this.x*q.z - this.z*q.x;
|
||||
final double denomXY = this.x*q.y - this.y*q.x;
|
||||
if (Math.abs(denomYZ) >= Math.abs(denomXZ) && Math.abs(denomYZ) >= Math.abs(denomXY)) {
|
||||
// X is the biggest, so our point will have x0 = 0.0
|
||||
if (Math.abs(denomYZ) < 1.0e-35)
|
||||
return new GeoPoint[0];
|
||||
double denom = 1.0 / denomYZ;
|
||||
return NO_POINTS;
|
||||
final double denom = 1.0 / denomYZ;
|
||||
x0 = 0.0;
|
||||
y0 = (-this.D * q.z - this.z * -q.D) * denom;
|
||||
z0 = (this.y * -q.D + this.D * q.y) * denom;
|
||||
} else if (Math.abs(denomXZ) >= Math.abs(denomXY) && Math.abs(denomXZ) >= Math.abs(denomYZ)) {
|
||||
// Y is the biggest, so y0 = 0.0
|
||||
if (Math.abs(denomXZ) < 1.0e-35)
|
||||
return new GeoPoint[0];
|
||||
double denom = 1.0 / denomXZ;
|
||||
return NO_POINTS;
|
||||
final double denom = 1.0 / denomXZ;
|
||||
x0 = (-this.D * q.z - this.z * -q.D) * denom;
|
||||
y0 = 0.0;
|
||||
z0 = (this.x * -q.D + this.D * q.x) * denom;
|
||||
} else {
|
||||
// Z is the biggest, so Z0 = 0.0
|
||||
if (Math.abs(denomXY) < 1.0e-35)
|
||||
return new GeoPoint[0];
|
||||
double denom = 1.0 / denomXY;
|
||||
return NO_POINTS;
|
||||
final double denom = 1.0 / denomXY;
|
||||
x0 = (-this.D * q.y - this.y * -q.D) * denom;
|
||||
y0 = (this.x * -q.D + this.D * q.x) * denom;
|
||||
z0 = 0.0;
|
||||
|
@ -151,26 +154,26 @@ public class Plane extends Vector
|
|||
// A^2 t^2 + 2AA0t + A0^2 + B^2 t^2 + 2BB0t + B0^2 + C^2 t^2 + 2CC0t + C0^2 - 1,0 = 0.0
|
||||
// [A^2 + B^2 + C^2] t^2 + [2AA0 + 2BB0 + 2CC0] t + [A0^2 + B0^2 + C0^2 - 1,0] = 0.0
|
||||
// Use the quadratic formula to determine t values and candidate point(s)
|
||||
double A = lineVector.x * lineVector.x + lineVector.y * lineVector.y + lineVector.z * lineVector.z;
|
||||
double B = 2.0*(lineVector.x * x0 + lineVector.y * y0 + lineVector.z * z0);
|
||||
double C = x0*x0 + y0*y0 + z0*z0 - 1.0;
|
||||
final double A = lineVector.x * lineVector.x + lineVector.y * lineVector.y + lineVector.z * lineVector.z;
|
||||
final double B = 2.0*(lineVector.x * x0 + lineVector.y * y0 + lineVector.z * z0);
|
||||
final double C = x0*x0 + y0*y0 + z0*z0 - 1.0;
|
||||
|
||||
double BsquaredMinus = B * B - 4.0 * A * C;
|
||||
final double BsquaredMinus = B * B - 4.0 * A * C;
|
||||
if (BsquaredMinus < 0.0)
|
||||
return new GeoPoint[0];
|
||||
double inverse2A = 1.0 / (2.0 * A);
|
||||
return NO_POINTS;
|
||||
final double inverse2A = 1.0 / (2.0 * A);
|
||||
if (BsquaredMinus == 0.0) {
|
||||
// One solution only
|
||||
double t = -B * inverse2A;
|
||||
final double t = -B * inverse2A;
|
||||
GeoPoint point = new GeoPoint(lineVector.x * t + x0, lineVector.y * t + y0, lineVector.z * t + z0);
|
||||
if (point.isWithin(bounds,moreBounds))
|
||||
return new GeoPoint[]{point};
|
||||
return new GeoPoint[0];
|
||||
return NO_POINTS;
|
||||
} else {
|
||||
// Two solutions
|
||||
double sqrtTerm = Math.sqrt(BsquaredMinus);
|
||||
double t1 = (-B + sqrtTerm) * inverse2A;
|
||||
double t2 = (-B - sqrtTerm) * inverse2A;
|
||||
final double sqrtTerm = Math.sqrt(BsquaredMinus);
|
||||
final double t1 = (-B + sqrtTerm) * inverse2A;
|
||||
final double t2 = (-B - sqrtTerm) * inverse2A;
|
||||
GeoPoint point1 = new GeoPoint(lineVector.x * t1 + x0, lineVector.y * t1 + y0, lineVector.z * t1 + z0);
|
||||
GeoPoint point2 = new GeoPoint(lineVector.x * t2 + x0, lineVector.y * t2 + y0, lineVector.z * t2 + z0);
|
||||
if (point1.isWithin(bounds,moreBounds)) {
|
||||
|
@ -180,7 +183,7 @@ public class Plane extends Vector
|
|||
}
|
||||
if (point2.isWithin(bounds,moreBounds))
|
||||
return new GeoPoint[]{point2};
|
||||
return new GeoPoint[0];
|
||||
return NO_POINTS;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,8 +195,8 @@ public class Plane extends Vector
|
|||
*@param boundsInfo is the info to update with additional bounding information.
|
||||
*@param bounds are the surfaces delineating what's inside the shape.
|
||||
*/
|
||||
public void recordBounds(Plane q, Bounds boundsInfo, Membership... bounds) {
|
||||
GeoPoint[] intersectionPoints = findIntersections(q,bounds,new Membership[0]);
|
||||
public void recordBounds(final Plane q, final Bounds boundsInfo, final Membership... bounds) {
|
||||
final GeoPoint[] intersectionPoints = findIntersections(q,bounds,NO_BOUNDS);
|
||||
for (GeoPoint intersectionPoint : intersectionPoints) {
|
||||
boundsInfo.addPoint(intersectionPoint);
|
||||
}
|
||||
|
@ -205,7 +208,7 @@ public class Plane extends Vector
|
|||
*@param boundsInfo is the info to update with additional bounding information.
|
||||
*@param bounds are the surfaces delineating what's inside the shape.
|
||||
*/
|
||||
public void recordBounds(Bounds boundsInfo, Membership... bounds) {
|
||||
public void recordBounds(final Bounds boundsInfo, final Membership... bounds) {
|
||||
// For clarity, load local variables with good names
|
||||
double A = this.x;
|
||||
double B = this.y;
|
||||
|
@ -617,7 +620,7 @@ public class Plane extends Vector
|
|||
|
||||
}
|
||||
|
||||
protected static void addPoint(Bounds boundsInfo, Membership[] bounds, double x, double y, double z) {
|
||||
protected static void addPoint(final Bounds boundsInfo, final Membership[] bounds, final double x, final double y, final double z) {
|
||||
// Make sure the discovered point is within the bounds
|
||||
for (Membership bound : bounds) {
|
||||
if (!bound.isWithin(x,y,z))
|
||||
|
@ -635,10 +638,19 @@ public class Plane extends Vector
|
|||
*@param moreBounds are more bounds.
|
||||
*@return true if there's an intersection.
|
||||
*/
|
||||
public boolean intersects(Plane q, Membership[] bounds, Membership... moreBounds) {
|
||||
public boolean intersects(final Plane q, final Membership[] bounds, final Membership... moreBounds) {
|
||||
return findIntersections(q,bounds,moreBounds).length > 0;
|
||||
}
|
||||
|
||||
/** Find a sample point on the intersection between two planes and the unit sphere.
|
||||
*/
|
||||
public GeoPoint getSampleIntersectionPoint(final Plane q) {
|
||||
final GeoPoint[] intersections = findIntersections(q, NO_BOUNDS, NO_BOUNDS);
|
||||
if (intersections.length == 0)
|
||||
return null;
|
||||
return intersections[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[A="+x+", B="+y+"; C="+z+"; D="+D+"]";
|
||||
|
|
|
@ -40,7 +40,7 @@ public class Vector
|
|||
*@param A is the first vector
|
||||
*@param B is the second
|
||||
*/
|
||||
public Vector(Vector A, Vector B) {
|
||||
public Vector(final Vector A, final Vector B) {
|
||||
// x = u2v3 - u3v2
|
||||
// y = u3v1 - u1v3
|
||||
// z = u1v2 - u2v1
|
||||
|
@ -67,7 +67,7 @@ public class Vector
|
|||
*@param v is the vector to evaluate.
|
||||
*@return the result.
|
||||
*/
|
||||
public double evaluate(Vector v) {
|
||||
public double evaluate(final Vector v) {
|
||||
return this.x * v.x + this.y * v.y + this.z * v.z;
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ public class Vector
|
|||
*@param z is the x value of the vector to evaluate.
|
||||
*@return the result.
|
||||
*/
|
||||
public double evaluate(double x, double y, double z) {
|
||||
public double evaluate(final double x, final double y, final double z) {
|
||||
return this.x * x + this.y * y + this.z * z;
|
||||
}
|
||||
|
||||
|
@ -87,14 +87,14 @@ public class Vector
|
|||
*@param moreBounds is the second part of the set of planes.
|
||||
*@return true if the point is within the bounds.
|
||||
*/
|
||||
public boolean isWithin(Membership[] bounds, Membership[] moreBounds) {
|
||||
public boolean isWithin(final Membership[] bounds, final Membership[] moreBounds) {
|
||||
// Return true if the point described is within all provided bounds
|
||||
for (Membership bound : bounds) {
|
||||
if (!bound.isWithin(this))
|
||||
if (bound != null && !bound.isWithin(this))
|
||||
return false;
|
||||
}
|
||||
for (Membership bound : moreBounds) {
|
||||
if (!bound.isWithin(this))
|
||||
if (bound != null && !bound.isWithin(this))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
@ -106,7 +106,7 @@ public class Vector
|
|||
*@param v is the vector to compute a distance to.
|
||||
*@return the square of the linear distance.
|
||||
*/
|
||||
public double linearDistanceSquared(Vector v) {
|
||||
public double linearDistanceSquared(final Vector v) {
|
||||
double deltaX = this.x - v.x;
|
||||
double deltaY = this.y - v.y;
|
||||
double deltaZ = this.z - v.z;
|
||||
|
@ -121,7 +121,7 @@ public class Vector
|
|||
*@param z is the z part of the vector to compute a distance to.
|
||||
*@return the square of the linear distance.
|
||||
*/
|
||||
public double linearDistanceSquared(double x, double y, double z) {
|
||||
public double linearDistanceSquared(final double x, final double y, final double z) {
|
||||
double deltaX = this.x - x;
|
||||
double deltaY = this.y - y;
|
||||
double deltaZ = this.z - z;
|
||||
|
@ -134,7 +134,7 @@ public class Vector
|
|||
*@param v is the vector to compute a distance to.
|
||||
*@return the linear distance.
|
||||
*/
|
||||
public double linearDistance(Vector v) {
|
||||
public double linearDistance(final Vector v) {
|
||||
return Math.sqrt(linearDistanceSquared(v));
|
||||
}
|
||||
|
||||
|
@ -146,7 +146,7 @@ public class Vector
|
|||
*@param z is the z part of the vector to compute a distance to.
|
||||
*@return the linear distance.
|
||||
*/
|
||||
public double linearDistance(double x, double y, double z) {
|
||||
public double linearDistance(final double x, final double y, final double z) {
|
||||
return Math.sqrt(linearDistanceSquared(x,y,z));
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ public class Vector
|
|||
*@param v is the vector to compute a distance to.
|
||||
*@return the square of the normal distance.
|
||||
*/
|
||||
public double normalDistanceSquared(Vector v) {
|
||||
public double normalDistanceSquared(final Vector v) {
|
||||
double t = this.evaluate(v);
|
||||
double deltaX = this.x * t - v.x;
|
||||
double deltaY = this.y * t - v.y;
|
||||
|
@ -172,7 +172,7 @@ public class Vector
|
|||
*@param z is the z part of the vector to compute a distance to.
|
||||
*@return the square of the normal distance.
|
||||
*/
|
||||
public double normalDistanceSquared(double x, double y, double z) {
|
||||
public double normalDistanceSquared(final double x, final double y, final double z) {
|
||||
double t = this.evaluate(x,y,z);
|
||||
double deltaX = this.x * t - x;
|
||||
double deltaY = this.y * t - y;
|
||||
|
@ -186,7 +186,7 @@ public class Vector
|
|||
*@param v is the vector to compute a distance to.
|
||||
*@return the normal distance.
|
||||
*/
|
||||
public double normalDistance(Vector v) {
|
||||
public double normalDistance(final Vector v) {
|
||||
return Math.sqrt(normalDistanceSquared(v));
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ public class Vector
|
|||
*@param z is the z part of the vector to compute a distance to.
|
||||
*@return the normal distance.
|
||||
*/
|
||||
public double normalDistance(double x, double y, double z) {
|
||||
public double normalDistance(final double x, final double y, final double z) {
|
||||
return Math.sqrt(normalDistanceSquared(x,y,z));
|
||||
}
|
||||
|
||||
|
@ -208,7 +208,7 @@ public class Vector
|
|||
public double magnitude() {
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Vector))
|
||||
|
|
|
@ -21,10 +21,8 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.carrotsearch.randomizedtesting.annotations.Repeat;
|
||||
import com.carrotsearch.randomizedtesting.annotations.Seed;
|
||||
import com.spatial4j.core.context.SpatialContext;
|
||||
import com.spatial4j.core.distance.DistanceUtils;
|
||||
import com.spatial4j.core.shape.Point;
|
||||
import com.spatial4j.core.shape.Rectangle;
|
||||
import com.spatial4j.core.shape.Shape;
|
||||
|
@ -34,17 +32,15 @@ import org.apache.lucene.spatial.prefix.RandomSpatialOpStrategyTestCase;
|
|||
import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
|
||||
import org.apache.lucene.spatial.prefix.tree.GeohashPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.SpatialPrefixTree;
|
||||
import org.apache.lucene.spatial.prefix.tree.Cell;
|
||||
import org.apache.lucene.spatial.prefix.tree.CellIterator;
|
||||
import org.apache.lucene.spatial.query.SpatialOperation;
|
||||
import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
|
||||
import org.apache.lucene.spatial.spatial4j.Geo3dShape;
|
||||
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;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPoint;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPolygonFactory;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoShape;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoCircle;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoPath;
|
||||
import org.apache.lucene.spatial.spatial4j.geo3d.GeoBBoxFactory;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.spatial4j.core.distance.DistanceUtils.DEGREES_TO_RADIANS;
|
||||
|
@ -84,8 +80,8 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Repeat(iterations = 20)
|
||||
//Seed("A9C215F48F200BB0")
|
||||
//@Repeat(iterations = 2000)
|
||||
@Seed("B808B88D6F8E285C")
|
||||
public void testOperations() throws IOException {
|
||||
setupStrategy();
|
||||
|
||||
|
@ -93,15 +89,31 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testTriangleDisjointRect() throws IOException {
|
||||
setupStrategy();
|
||||
Rectangle rect = ctx.makeRectangle(-180, 180, 77, 84);
|
||||
Shape triangle = makeTriangle(-149, 35, 88, -11, -27, -18);
|
||||
assertTrue(rect.relate(triangle).intersects()); //unsure if this is correct or not but passes
|
||||
//if they intersect, then the following rect cell can be "within" the triangle
|
||||
final Rectangle cellRect = ctx.makeRectangle(-180, -168.75, 73.125, 78.75);
|
||||
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(triangle) != SpatialRelation.WITHIN);
|
||||
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) {
|
||||
|
@ -163,8 +175,14 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
case 2: {
|
||||
// Rectangles
|
||||
while (true) {
|
||||
final Point ulhcPoint = randomPoint();
|
||||
final Point lrhcPoint = randomPoint();
|
||||
Point ulhcPoint = randomPoint();
|
||||
Point lrhcPoint = randomPoint();
|
||||
if (ulhcPoint.getY() < lrhcPoint.getY()) {
|
||||
//swap
|
||||
Point temp = ulhcPoint;
|
||||
ulhcPoint = lrhcPoint;
|
||||
lrhcPoint = temp;
|
||||
}
|
||||
try {
|
||||
final GeoShape shape = GeoBBoxFactory.makeGeoBBox(ulhcPoint.getY() * DEGREES_TO_RADIANS,
|
||||
lrhcPoint.getY() * DEGREES_TO_RADIANS,
|
||||
|
@ -181,7 +199,7 @@ public class Geo3dRptTest extends RandomSpatialOpStrategyTestCase {
|
|||
case 3: {
|
||||
// Paths
|
||||
final int pointCount = random().nextInt(5) + 1;
|
||||
final double width = random().nextInt(90) * DEGREES_TO_RADIANS;
|
||||
final double width = (random().nextInt(89)+1) * DEGREES_TO_RADIANS;
|
||||
while (true) {
|
||||
try {
|
||||
final GeoPath path = new GeoPath(width);
|
||||
|
|
|
@ -17,10 +17,12 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GeoConvexPolygonTest {
|
||||
|
||||
|
||||
|
@ -29,10 +31,10 @@ public class GeoConvexPolygonTest {
|
|||
GeoConvexPolygon c;
|
||||
GeoPoint gp;
|
||||
c = new GeoConvexPolygon(-0.1,-0.5);
|
||||
c.addPoint(0.0,-0.6);
|
||||
c.addPoint(0.1,-0.5);
|
||||
c.addPoint(0.0,-0.4);
|
||||
c.donePoints();
|
||||
c.addPoint(0.0,-0.6,false);
|
||||
c.addPoint(0.1,-0.5,false);
|
||||
c.addPoint(0.0,-0.4,false);
|
||||
c.donePoints(false);
|
||||
// Sample some points within
|
||||
gp = new GeoPoint(0.0,-0.5);
|
||||
assertTrue(c.isWithin(gp));
|
||||
|
@ -68,10 +70,10 @@ public class GeoConvexPolygonTest {
|
|||
Bounds b;
|
||||
|
||||
c = new GeoConvexPolygon(-0.1,-0.5);
|
||||
c.addPoint(0.0,-0.6);
|
||||
c.addPoint(0.1,-0.5);
|
||||
c.addPoint(0.0,-0.4);
|
||||
c.donePoints();
|
||||
c.addPoint(0.0,-0.6,false);
|
||||
c.addPoint(0.1,-0.5,false);
|
||||
c.addPoint(0.0,-0.4,false);
|
||||
c.donePoints(false);
|
||||
|
||||
b = c.getBounds(null);
|
||||
assertFalse(b.checkNoLongitudeBound());
|
||||
|
|
|
@ -17,10 +17,12 @@ package org.apache.lucene.spatial.spatial4j.geo3d;
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GeoPathTest {
|
||||
|
||||
@Test
|
||||
|
@ -32,6 +34,7 @@ public class GeoPathTest {
|
|||
p.addPoint(0.0,0.0);
|
||||
p.addPoint(0.0,0.1);
|
||||
p.addPoint(0.0,0.2);
|
||||
p.done();
|
||||
gp = new GeoPoint(Math.PI * 0.5,0.15);
|
||||
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
||||
gp = new GeoPoint(0.05,0.15);
|
||||
|
@ -50,6 +53,7 @@ public class GeoPathTest {
|
|||
p.addPoint(0.0,0.0);
|
||||
p.addPoint(0.0,0.1);
|
||||
p.addPoint(0.0,0.2);
|
||||
p.done();
|
||||
gp = new GeoPoint(0.05,0.15);
|
||||
assertEquals(0.15 + 0.05, p.computeArcDistance(gp), 0.000001);
|
||||
gp = new GeoPoint(0.0,0.12);
|
||||
|
@ -59,6 +63,7 @@ public class GeoPathTest {
|
|||
p = new GeoPath(0.1);
|
||||
p.addPoint(-Math.PI * 0.25,-0.5);
|
||||
p.addPoint(Math.PI * 0.25,-0.5);
|
||||
p.done();
|
||||
gp = new GeoPoint(0.0,0.0);
|
||||
assertEquals(Double.MAX_VALUE, p.computeArcDistance(gp), 0.0);
|
||||
gp = new GeoPoint(-0.1,-1.0);
|
||||
|
@ -78,6 +83,7 @@ public class GeoPathTest {
|
|||
// Build a diagonal path crossing the equator
|
||||
p.addPoint(-0.2,-0.2);
|
||||
p.addPoint(0.2,0.2);
|
||||
p.done();
|
||||
// Test points on the path
|
||||
gp = new GeoPoint(-0.2,-0.2);
|
||||
assertTrue(p.isWithin(gp));
|
||||
|
@ -128,6 +134,7 @@ public class GeoPathTest {
|
|||
p = new GeoPath(0.1);
|
||||
p.addPoint(-0.3,-0.3);
|
||||
p.addPoint(0.3,0.3);
|
||||
p.done();
|
||||
// Easiest: The path is wholly contains the georect
|
||||
rect = new GeoRectangle(0.05,-0.05,-0.05,0.05);
|
||||
assertEquals(GeoArea.CONTAINS, rect.getRelationship(p));
|
||||
|
@ -161,7 +168,8 @@ public class GeoPathTest {
|
|||
c = new GeoPath(0.1);
|
||||
c.addPoint(-0.3,-0.3);
|
||||
c.addPoint(0.3,0.3);
|
||||
|
||||
c.done();
|
||||
|
||||
b = c.getBounds(null);
|
||||
assertFalse(b.checkNoLongitudeBound());
|
||||
assertFalse(b.checkNoTopLatitudeBound());
|
||||
|
|
Loading…
Reference in New Issue