You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2016/03/08 02:20:04 UTC
[23/32] lucene-solr git commit: LUCENE-7056: Geo3D package re-org
(cherry picked from commit 3a31a8c)
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java
new file mode 100755
index 0000000..1420c11
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Bounding box limited on four sides (top lat, bottom lat, left lon, right lon).
+ * The left-right maximum extent for this shape is PI; for anything larger, use
+ * GeoWideRectangle.
+ *
+ * @lucene.internal
+ */
+public class GeoRectangle extends GeoBaseBBox {
+ /** The top latitude of the rect */
+ protected final double topLat;
+ /** The bottom latitude of the rect */
+ protected final double bottomLat;
+ /** The left longitude of the rect */
+ protected final double leftLon;
+ /** The right longitude of the rect */
+ protected final double rightLon;
+ /** The cosine of a middle latitude */
+ protected final double cosMiddleLat;
+
+ /** The upper left hand corner point */
+ protected final GeoPoint ULHC;
+ /** The upper right hand corner point */
+ protected final GeoPoint URHC;
+ /** The lower right hand corner point */
+ protected final GeoPoint LRHC;
+ /** The lower left hand corner point */
+ protected final GeoPoint LLHC;
+
+ /** The top plane */
+ protected final SidedPlane topPlane;
+ /** The bottom plane */
+ protected final SidedPlane bottomPlane;
+ /** The left plane */
+ protected final SidedPlane leftPlane;
+ /** The right plane */
+ protected final SidedPlane rightPlane;
+
+ /** Notable points for the top plane */
+ protected final GeoPoint[] topPlanePoints;
+ /** Notable points for the bottom plane */
+ protected final GeoPoint[] bottomPlanePoints;
+ /** Notable points for the left plane */
+ protected final GeoPoint[] leftPlanePoints;
+ /** Notable points for the right plane */
+ protected final GeoPoint[] rightPlanePoints;
+
+ /** Center point */
+ protected final GeoPoint centerPoint;
+
+ /** Edge point for this rectangle */
+ protected final GeoPoint[] edgePoints;
+
+ /**
+ * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}
+ *@param planetModel is the planet model.
+ *@param topLat is the top latitude.
+ *@param bottomLat is the bottom latitude.
+ *@param leftLon is the left longitude.
+ *@param rightLon is the right longitude.
+ */
+ public GeoRectangle(final PlanetModel planetModel, final double topLat, final double bottomLat, final double leftLon, double rightLon) {
+ super(planetModel);
+ // Argument checking
+ if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Top latitude out of range");
+ if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Bottom latitude out of range");
+ if (topLat < bottomLat)
+ throw new IllegalArgumentException("Top latitude less than bottom latitude");
+ if (leftLon < -Math.PI || leftLon > Math.PI)
+ throw new IllegalArgumentException("Left longitude out of range");
+ if (rightLon < -Math.PI || rightLon > Math.PI)
+ throw new IllegalArgumentException("Right longitude out of range");
+ double extent = rightLon - leftLon;
+ if (extent < 0.0) {
+ extent += 2.0 * Math.PI;
+ }
+ if (extent > Math.PI)
+ throw new IllegalArgumentException("Width of rectangle too great");
+
+ this.topLat = topLat;
+ this.bottomLat = bottomLat;
+ this.leftLon = leftLon;
+ this.rightLon = rightLon;
+
+ final double sinTopLat = Math.sin(topLat);
+ final double cosTopLat = Math.cos(topLat);
+ final double sinBottomLat = Math.sin(bottomLat);
+ final double cosBottomLat = Math.cos(bottomLat);
+ final double sinLeftLon = Math.sin(leftLon);
+ final double cosLeftLon = Math.cos(leftLon);
+ final double sinRightLon = Math.sin(rightLon);
+ final double cosRightLon = Math.cos(rightLon);
+
+ // Now build the four points
+ this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+ this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
+ this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+ this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
+
+ final double middleLat = (topLat + bottomLat) * 0.5;
+ final double sinMiddleLat = Math.sin(middleLat);
+ this.cosMiddleLat = Math.cos(middleLat);
+ // Normalize
+ while (leftLon > rightLon) {
+ rightLon += Math.PI * 2.0;
+ }
+ final double middleLon = (leftLon + rightLon) * 0.5;
+ final double sinMiddleLon = Math.sin(middleLon);
+ final double cosMiddleLon = Math.cos(middleLon);
+
+ this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon);
+
+ this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat);
+ this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat);
+ this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
+ this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
+
+ this.topPlanePoints = new GeoPoint[]{ULHC, URHC};
+ this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC};
+ this.leftPlanePoints = new GeoPoint[]{ULHC, LLHC};
+ this.rightPlanePoints = new GeoPoint[]{URHC, LRHC};
+
+ this.edgePoints = new GeoPoint[]{ULHC};
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ final double newTopLat = topLat + angle;
+ final double newBottomLat = bottomLat - angle;
+ // Figuring out when we escalate to a special case requires some prefiguring
+ double currentLonSpan = rightLon - leftLon;
+ if (currentLonSpan < 0.0)
+ currentLonSpan += Math.PI * 2.0;
+ double newLeftLon = leftLon - angle;
+ double newRightLon = rightLon + angle;
+ if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
+ newLeftLon = -Math.PI;
+ newRightLon = Math.PI;
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return topPlane.isWithin(x, y, z) &&
+ bottomPlane.isWithin(x, y, z) &&
+ leftPlane.isWithin(x, y, z) &&
+ rightPlane.isWithin(x, y, z);
+ }
+
+ @Override
+ public double getRadius() {
+ // Here we compute the distance from the middle point to one of the corners. However, we need to be careful
+ // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and
+ // the distance to the right or left edge from the center.
+ final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
+ final double topAngle = centerPoint.arcDistance(URHC);
+ final double bottomAngle = centerPoint.arcDistance(LLHC);
+ return Math.max(centerAngle, Math.max(topAngle, bottomAngle));
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public GeoPoint getCenter() {
+ return centerPoint;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, leftPlane, rightPlane) ||
+ p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, leftPlane, rightPlane) ||
+ p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane, bottomPlane) ||
+ p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane, bottomPlane);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds.addHorizontalPlane(planetModel, topLat, topPlane, bottomPlane, leftPlane, rightPlane)
+ .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane, bottomPlane, leftPlane)
+ .addHorizontalPlane(planetModel, bottomLat, bottomPlane, topPlane, leftPlane, rightPlane)
+ .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane, bottomPlane, rightPlane)
+ .addPoint(ULHC).addPoint(URHC).addPoint(LLHC).addPoint(LRHC);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ //System.err.println(this+" getrelationship with "+path);
+ final int insideRectangle = isShapeInsideBBox(path);
+ if (insideRectangle == SOME_INSIDE) {
+ //System.err.println(" some inside");
+ return OVERLAPS;
+ }
+
+ final boolean insideShape = path.isWithin(ULHC);
+
+ if (insideRectangle == ALL_INSIDE && insideShape) {
+ //System.err.println(" inside of each other");
+ return OVERLAPS;
+ }
+
+ if (path.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) ||
+ path.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) ||
+ path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane, rightPlane) ||
+ path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane, bottomPlane)) {
+ //System.err.println(" edges intersect");
+ return OVERLAPS;
+ }
+
+ if (insideRectangle == ALL_INSIDE) {
+ //System.err.println(" shape inside rectangle");
+ return WITHIN;
+ }
+
+ if (insideShape) {
+ //System.err.println(" shape contains rectangle");
+ return CONTAINS;
+ }
+ //System.err.println(" disjoint");
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, leftPlane, rightPlane);
+ final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane, leftPlane, rightPlane);
+ final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane, topPlane, bottomPlane);
+ final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, leftPlane, topPlane, bottomPlane);
+
+ final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+ final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+ final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+ final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+
+ return Math.min(
+ Math.min(
+ Math.min(topDistance, bottomDistance),
+ Math.min(leftDistance, rightDistance)),
+ Math.min(
+ Math.min(ULHCDistance, URHCDistance),
+ Math.min(LRHCDistance, LLHCDistance)));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoRectangle))
+ return false;
+ GeoRectangle other = (GeoRectangle) o;
+ return super.equals(other) && other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + ULHC.hashCode();
+ result = 31 * result + LRHC.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoRectangle: {planetmodel="+planetModel+", 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 + ")}";
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoShape.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoShape.java
new file mode 100755
index 0000000..a2d3947
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoShape.java
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Generic shape. This describes methods that help GeoAreas figure out
+ * how they interact with a shape, for the purposes of coming up with a
+ * set of geo hash values.
+ *
+ * @lucene.experimental
+ */
+public interface GeoShape extends Membership {
+
+ /**
+ * Return a sample point that is on the outside edge/boundary of the shape.
+ *
+ * @return samples of all edge points from distinct edge sections. Typically one point
+ * is returned, but zero or two are also possible.
+ */
+ public GeoPoint[] getEdgePoints();
+
+ /**
+ * Assess whether a plane, within the provided bounds, intersects
+ * with the shape. Note well that this method is allowed to return "true"
+ * if there are internal edges of a composite shape which intersect the plane.
+ * Doing this can cause getRelationship() for most GeoBBox shapes to return
+ * OVERLAPS rather than the more correct CONTAINS, but that cannot be
+ * helped for some complex shapes that are built out of overlapping parts.
+ *
+ * @param plane is the plane to assess for intersection with the shape's edges or
+ * bounding curves.
+ * @param notablePoints represents the intersections of the plane with the supplied
+ * bounds. These are used to disambiguate when two planes are identical and it needs
+ * to be determined whether any points exist that fulfill all the bounds.
+ * @param bounds are a set of bounds that define an area that an
+ * intersection must be within in order to qualify (provided by a GeoArea).
+ * @return true if there's such an intersection, false if not.
+ */
+ public boolean intersects(final Plane plane, final GeoPoint[] notablePoints, final Membership... bounds);
+
+ /**
+ * Compute bounds for the shape.
+ *
+ * @param bounds is the input bounds object.
+ * The input object will be modified.
+ */
+ public void getBounds(final Bounds bounds);
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSizeable.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSizeable.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSizeable.java
new file mode 100755
index 0000000..3c7e2ef
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSizeable.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Some shapes can compute radii of a geocircle in which they are inscribed.
+ *
+ * @lucene.experimental
+ */
+public interface GeoSizeable {
+ /**
+ * Returns the radius of a circle into which the GeoSizeable area can
+ * be inscribed.
+ *
+ * @return the radius.
+ */
+ public double getRadius();
+
+ /**
+ * Returns the center of a circle into which the area will be inscribed.
+ *
+ * @return the center.
+ */
+ public GeoPoint getCenter();
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java
new file mode 100644
index 0000000..a1d8967
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * This GeoBBox represents an area rectangle limited only in north latitude.
+ *
+ * @lucene.internal
+ */
+public class GeoSouthLatitudeZone extends GeoBaseBBox {
+ /** The top latitude of the zone */
+ protected final double topLat;
+ /** The cosine of the top latitude of the zone */
+ protected final double cosTopLat;
+ /** The top plane of the zone */
+ protected final SidedPlane topPlane;
+ /** An interior point of the zone */
+ protected final GeoPoint interiorPoint;
+ /** Notable points for the plane (none) */
+ protected final static GeoPoint[] planePoints = new GeoPoint[0];
+ /** A point on the top boundary */
+ protected final GeoPoint topBoundaryPoint;
+ /** Edge points; a reference to the topBoundaryPoint */
+ protected final GeoPoint[] edgePoints;
+
+ /** Constructor.
+ *@param planetModel is the planet model.
+ *@param topLat is the top latitude of the zone.
+ */
+ public GeoSouthLatitudeZone(final PlanetModel planetModel, final double topLat) {
+ super(planetModel);
+ this.topLat = topLat;
+
+ final double sinTopLat = Math.sin(topLat);
+ this.cosTopLat = Math.cos(topLat);
+
+ // Compute an interior point. Pick one whose lat is between top and bottom.
+ final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
+ final double sinMiddleLat = Math.sin(middleLat);
+ this.interiorPoint = new GeoPoint(planetModel, sinMiddleLat, 0.0, Math.sqrt(1.0 - sinMiddleLat * sinMiddleLat), 1.0);
+ this.topBoundaryPoint = new GeoPoint(planetModel, sinTopLat, 0.0, Math.sqrt(1.0 - sinTopLat * sinTopLat), 1.0);
+
+ this.topPlane = new SidedPlane(interiorPoint, planetModel, sinTopLat);
+
+ this.edgePoints = new GeoPoint[]{topBoundaryPoint};
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ final double newTopLat = topLat + angle;
+ final double newBottomLat = -Math.PI * 0.5;
+ return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, -Math.PI, Math.PI);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return topPlane.isWithin(x, y, z);
+ }
+
+ @Override
+ public double getRadius() {
+ // This is a bit tricky. I guess we should interpret this as meaning the angle of a circle that
+ // would contain all the bounding box points, when starting in the "center".
+ if (topLat > 0.0)
+ return Math.PI;
+ double maxCosLat = cosTopLat;
+ return maxCosLat * Math.PI;
+ }
+
+ /**
+ * Returns the center of a circle into which the area will be inscribed.
+ *
+ * @return the center.
+ */
+ @Override
+ public GeoPoint getCenter() {
+ return interiorPoint;
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ return p.intersects(planetModel, topPlane, notablePoints, planePoints, bounds);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds
+ .addHorizontalPlane(planetModel, topLat, topPlane);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ final int insideRectangle = isShapeInsideBBox(path);
+ if (insideRectangle == SOME_INSIDE)
+ return OVERLAPS;
+
+ final boolean insideShape = path.isWithin(topBoundaryPoint);
+
+ if (insideRectangle == ALL_INSIDE && insideShape)
+ return OVERLAPS;
+
+ // Second, the shortcut of seeing whether endpoints are in/out is not going to
+ // work with no area endpoints. So we rely entirely on intersections.
+
+ if (path.intersects(topPlane, planePoints))
+ return OVERLAPS;
+
+ // There is another case for latitude zones only. This is when the boundaries of the shape all fit
+ // within the zone, but the shape includes areas outside the zone crossing a pole.
+ // In this case, the above "overlaps" check is insufficient. We also need to check a point on either boundary
+ // whether it is within the shape. If both such points are within, then CONTAINS is the right answer. If
+ // one such point is within, then OVERLAPS is the right answer.
+
+ if (insideShape)
+ return CONTAINS;
+
+ if (insideRectangle == ALL_INSIDE)
+ return WITHIN;
+
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ return distanceStyle.computeDistance(planetModel, topPlane, x,y,z);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoSouthLatitudeZone))
+ return false;
+ GeoSouthLatitudeZone other = (GeoSouthLatitudeZone) o;
+ return super.equals(other) && other.topBoundaryPoint.equals(topBoundaryPoint);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + topBoundaryPoint.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoSouthLatitudeZone: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + ")}";
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java
new file mode 100644
index 0000000..806535e
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java
@@ -0,0 +1,259 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Bounding box limited on three sides (top lat, left lon, right lon). The
+ * other corner is the south pole.
+ * The left-right maximum extent for this shape is PI; for anything larger, use
+ * {@link GeoWideSouthRectangle}.
+ *
+ * @lucene.internal
+ */
+public class GeoSouthRectangle extends GeoBaseBBox {
+ /** The top latitude of the rect */
+ protected final double topLat;
+ /** The left longitude of the rect */
+ protected final double leftLon;
+ /** The right longitude of the rect */
+ protected final double rightLon;
+ /** The cosine of a middle latitude */
+ protected final double cosMiddleLat;
+ /** The upper left hand corner of the rectangle */
+ protected final GeoPoint ULHC;
+ /** The upper right hand corner of the rectangle */
+ protected final GeoPoint URHC;
+
+ /** The top plane */
+ protected final SidedPlane topPlane;
+ /** The left plane */
+ protected final SidedPlane leftPlane;
+ /** The right plane */
+ protected final SidedPlane rightPlane;
+
+ /** Notable points for the top plane */
+ protected final GeoPoint[] topPlanePoints;
+ /** Notable points for the left plane */
+ protected final GeoPoint[] leftPlanePoints;
+ /** Notable points for the right plane */
+ protected final GeoPoint[] rightPlanePoints;
+
+ /** The center point */
+ protected final GeoPoint centerPoint;
+
+ /** A point on the edge */
+ protected final GeoPoint[] edgePoints;
+
+ /**
+ * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}
+ *@param planetModel is the planet model.
+ *@param topLat is the top latitude.
+ *@param leftLon is the left longitude.
+ *@param rightLon is the right longitude.
+ */
+ public GeoSouthRectangle(final PlanetModel planetModel, final double topLat, final double leftLon, double rightLon) {
+ super(planetModel);
+ // Argument checking
+ if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Top latitude out of range");
+ if (leftLon < -Math.PI || leftLon > Math.PI)
+ throw new IllegalArgumentException("Left longitude out of range");
+ if (rightLon < -Math.PI || rightLon > Math.PI)
+ throw new IllegalArgumentException("Right longitude out of range");
+ double extent = rightLon - leftLon;
+ if (extent < 0.0) {
+ extent += 2.0 * Math.PI;
+ }
+ if (extent > Math.PI)
+ throw new IllegalArgumentException("Width of rectangle too great");
+
+ this.topLat = topLat;
+ this.leftLon = leftLon;
+ this.rightLon = rightLon;
+
+ final double sinTopLat = Math.sin(topLat);
+ final double cosTopLat = Math.cos(topLat);
+ final double sinLeftLon = Math.sin(leftLon);
+ final double cosLeftLon = Math.cos(leftLon);
+ final double sinRightLon = Math.sin(rightLon);
+ final double cosRightLon = Math.cos(rightLon);
+
+ // Now build the four points
+ this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+ this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
+
+ final double middleLat = (topLat - Math.PI * 0.5) * 0.5;
+ final double sinMiddleLat = Math.sin(middleLat);
+ this.cosMiddleLat = Math.cos(middleLat);
+ // Normalize
+ while (leftLon > rightLon) {
+ rightLon += Math.PI * 2.0;
+ }
+ final double middleLon = (leftLon + rightLon) * 0.5;
+ final double sinMiddleLon = Math.sin(middleLon);
+ final double cosMiddleLon = Math.cos(middleLon);
+
+ this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon);
+
+ this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat);
+ this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
+ this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
+
+ this.topPlanePoints = new GeoPoint[]{ULHC, URHC};
+ this.leftPlanePoints = new GeoPoint[]{ULHC, planetModel.SOUTH_POLE};
+ this.rightPlanePoints = new GeoPoint[]{URHC, planetModel.SOUTH_POLE};
+
+ this.edgePoints = new GeoPoint[]{planetModel.SOUTH_POLE};
+
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ final double newTopLat = topLat + angle;
+ final double newBottomLat = -Math.PI * 0.5;
+ // Figuring out when we escalate to a special case requires some prefiguring
+ double currentLonSpan = rightLon - leftLon;
+ if (currentLonSpan < 0.0)
+ currentLonSpan += Math.PI * 2.0;
+ double newLeftLon = leftLon - angle;
+ double newRightLon = rightLon + angle;
+ if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
+ newLeftLon = -Math.PI;
+ newRightLon = Math.PI;
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return topPlane.isWithin(x, y, z) &&
+ leftPlane.isWithin(x, y, z) &&
+ rightPlane.isWithin(x, y, z);
+ }
+
+ @Override
+ public double getRadius() {
+ // Here we compute the distance from the middle point to one of the corners. However, we need to be careful
+ // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and
+ // the distance to the right or left edge from the center.
+ final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
+ final double topAngle = centerPoint.arcDistance(URHC);
+ return Math.max(centerAngle, topAngle);
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public GeoPoint getCenter() {
+ return centerPoint;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, leftPlane, rightPlane) ||
+ p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, rightPlane, topPlane) ||
+ p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, leftPlane, topPlane);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds
+ .addHorizontalPlane(planetModel, topLat, topPlane, leftPlane, rightPlane)
+ .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane, rightPlane)
+ .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane, leftPlane)
+ .addPoint(URHC).addPoint(ULHC).addPoint(planetModel.SOUTH_POLE);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ //System.err.println(this+" getrelationship with "+path);
+ final int insideRectangle = isShapeInsideBBox(path);
+ if (insideRectangle == SOME_INSIDE) {
+ //System.err.println(" some inside");
+ return OVERLAPS;
+ }
+
+ final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE);
+
+ if (insideRectangle == ALL_INSIDE && insideShape) {
+ //System.err.println(" inside of each other");
+ return OVERLAPS;
+ }
+
+ if (path.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) ||
+ path.intersects(leftPlane, leftPlanePoints, topPlane, rightPlane) ||
+ path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane)) {
+ //System.err.println(" edges intersect");
+ return OVERLAPS;
+ }
+
+ if (insideRectangle == ALL_INSIDE) {
+ //System.err.println(" shape inside rectangle");
+ return WITHIN;
+ }
+
+ if (insideShape) {
+ //System.err.println(" shape contains rectangle");
+ return CONTAINS;
+ }
+ //System.err.println(" disjoint");
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, leftPlane, rightPlane);
+ final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane, topPlane);
+ final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, leftPlane, topPlane);
+
+ final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+ final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+
+ return Math.min(
+ Math.min(
+ topDistance,
+ Math.min(leftDistance, rightDistance)),
+ Math.min(ULHCDistance, URHCDistance));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoSouthRectangle))
+ return false;
+ GeoSouthRectangle other = (GeoSouthRectangle) o;
+ return super.equals(other) && other.ULHC.equals(ULHC) && other.URHC.equals(URHC);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + ULHC.hashCode();
+ result = 31 * result + URHC.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoSouthRectangle: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java
new file mode 100755
index 0000000..bbf5046
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java
@@ -0,0 +1,168 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Circular area with a center and radius.
+ *
+ * @lucene.experimental
+ */
+public class GeoStandardCircle extends GeoBaseCircle {
+ /** Center of circle */
+ protected final GeoPoint center;
+ /** Cutoff angle of circle (not quite the same thing as radius) */
+ protected final double cutoffAngle;
+ /** The plane describing the circle (really an ellipse on a non-spherical world) */
+ protected final SidedPlane circlePlane;
+ /** A point that is on the world and on the circle plane */
+ protected final GeoPoint[] edgePoints;
+ /** Notable points for a circle -- there aren't any */
+ protected static final GeoPoint[] circlePoints = new GeoPoint[0];
+
+ /** Constructor.
+ *@param planetModel is the planet model.
+ *@param lat is the center latitude.
+ *@param lon is the center longitude.
+ *@param cutoffAngle is the cutoff angle for the circle.
+ */
+ public GeoStandardCircle(final PlanetModel planetModel, final double lat, final double lon, final double cutoffAngle) {
+ super(planetModel);
+ if (lat < -Math.PI * 0.5 || lat > Math.PI * 0.5)
+ throw new IllegalArgumentException("Latitude out of bounds");
+ if (lon < -Math.PI || lon > Math.PI)
+ throw new IllegalArgumentException("Longitude out of bounds");
+ if (cutoffAngle < 0.0 || cutoffAngle > Math.PI)
+ throw new IllegalArgumentException("Cutoff angle out of bounds");
+ if (cutoffAngle < Vector.MINIMUM_RESOLUTION)
+ throw new IllegalArgumentException("Cutoff angle cannot be effectively zero");
+ this.center = new GeoPoint(planetModel, lat, lon);
+ // In an ellipsoidal world, cutoff distances make no sense, unfortunately. Only membership
+ // can be used to make in/out determination.
+ this.cutoffAngle = cutoffAngle;
+ // Compute two points on the circle, with the right angle from the center. We'll use these
+ // to obtain the perpendicular plane to the circle.
+ double upperLat = lat + cutoffAngle;
+ double upperLon = lon;
+ if (upperLat > Math.PI * 0.5) {
+ upperLon += Math.PI;
+ if (upperLon > Math.PI)
+ upperLon -= 2.0 * Math.PI;
+ upperLat = Math.PI - upperLat;
+ }
+ double lowerLat = lat - cutoffAngle;
+ double lowerLon = lon;
+ if (lowerLat < -Math.PI * 0.5) {
+ lowerLon += Math.PI;
+ if (lowerLon > Math.PI)
+ lowerLon -= 2.0 * Math.PI;
+ lowerLat = -Math.PI - lowerLat;
+ }
+ final GeoPoint upperPoint = new GeoPoint(planetModel, upperLat, upperLon);
+ final GeoPoint lowerPoint = new GeoPoint(planetModel, lowerLat, lowerLon);
+ if (Math.abs(cutoffAngle - Math.PI) < Vector.MINIMUM_RESOLUTION) {
+ // Circle is the whole world
+ this.circlePlane = null;
+ this.edgePoints = new GeoPoint[0];
+ } else {
+ // Construct normal plane
+ final Plane normalPlane = Plane.constructNormalizedZPlane(upperPoint, lowerPoint, center);
+ // Construct a sided plane that goes through the two points and whose normal is in the normalPlane.
+ this.circlePlane = SidedPlane.constructNormalizedPerpendicularSidedPlane(center, normalPlane, upperPoint, lowerPoint);
+ if (circlePlane == null)
+ throw new IllegalArgumentException("Couldn't construct circle plane, probably too small? Cutoff angle = "+cutoffAngle+"; upperPoint = "+upperPoint+"; lowerPoint = "+lowerPoint);
+ final GeoPoint recomputedIntersectionPoint = circlePlane.getSampleIntersectionPoint(planetModel, normalPlane);
+ if (recomputedIntersectionPoint == null)
+ throw new IllegalArgumentException("Couldn't construct intersection point, probably circle too small? Plane = "+circlePlane);
+ this.edgePoints = new GeoPoint[]{recomputedIntersectionPoint};
+ }
+ }
+
+ @Override
+ public double getRadius() {
+ return cutoffAngle;
+ }
+
+ @Override
+ public GeoPoint getCenter() {
+ return center;
+ }
+
+ @Override
+ protected double distance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ return distanceStyle.computeDistance(this.center, x, y, z);
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ return distanceStyle.computeDistance(planetModel, circlePlane, x, y, z);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ if (circlePlane == null) {
+ return true;
+ }
+ // Fastest way of determining membership
+ return circlePlane.isWithin(x, y, z);
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ if (circlePlane == null) {
+ return false;
+ }
+ return circlePlane.intersects(planetModel, p, notablePoints, circlePoints, bounds);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ if (circlePlane == null) {
+ // Entire world; should already be covered
+ return;
+ }
+ bounds.addPoint(center);
+ bounds.addPlane(planetModel, circlePlane);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoStandardCircle))
+ return false;
+ GeoStandardCircle other = (GeoStandardCircle) o;
+ return super.equals(other) && other.center.equals(center) && other.cutoffAngle == cutoffAngle;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + center.hashCode();
+ long temp = Double.doubleToLongBits(cutoffAngle);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoStandardCircle: {planetmodel=" + planetModel+", center=" + center + ", radius=" + cutoffAngle + "(" + cutoffAngle * 180.0 / Math.PI + ")}";
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java
new file mode 100644
index 0000000..48a73af
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Degenerate bounding box wider than PI and limited on two sides (left lon, right lon).
+ *
+ * @lucene.internal
+ */
+public class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
+ /** The latitude of the line */
+ protected final double latitude;
+ /** The left longitude cutoff of the line */
+ protected final double leftLon;
+ /** The right longitude cutoff of the line */
+ protected final double rightLon;
+
+ /** The left end of the line */
+ protected final GeoPoint LHC;
+ /** The right end of the line */
+ protected final GeoPoint RHC;
+
+ /** The plane the line is in */
+ protected final Plane plane;
+ /** The left cutoff plane */
+ protected final SidedPlane leftPlane;
+ /** The right cutoff plane */
+ protected final SidedPlane rightPlane;
+
+ /** Notable points for the line */
+ protected final GeoPoint[] planePoints;
+
+ /** Center point for the line */
+ protected final GeoPoint centerPoint;
+
+ /** Left/right combination bound */
+ protected final EitherBound eitherBound;
+
+ /** A point on the line */
+ protected 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.
+ *@param planetModel is the planet model.
+ *@param latitude is the line latitude.
+ *@param leftLon is the left cutoff longitude.
+ *@param rightLon is the right cutoff longitude.
+ */
+ public GeoWideDegenerateHorizontalLine(final PlanetModel planetModel, final double latitude, final double leftLon, double rightLon) {
+ super(planetModel);
+ // Argument checking
+ if (latitude > Math.PI * 0.5 || latitude < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Latitude out of range");
+ if (leftLon < -Math.PI || leftLon > Math.PI)
+ throw new IllegalArgumentException("Left longitude out of range");
+ if (rightLon < -Math.PI || rightLon > Math.PI)
+ throw new IllegalArgumentException("Right longitude out of range");
+ double extent = rightLon - leftLon;
+ if (extent < 0.0) {
+ extent += 2.0 * Math.PI;
+ }
+ if (extent < Math.PI)
+ throw new IllegalArgumentException("Width of rectangle too small");
+
+ this.latitude = latitude;
+ this.leftLon = leftLon;
+ this.rightLon = rightLon;
+
+ final double sinLatitude = Math.sin(latitude);
+ final double cosLatitude = Math.cos(latitude);
+ final double sinLeftLon = Math.sin(leftLon);
+ final double cosLeftLon = Math.cos(leftLon);
+ final double sinRightLon = Math.sin(rightLon);
+ final double cosRightLon = Math.cos(rightLon);
+
+ // Now build the two points
+ this.LHC = new GeoPoint(planetModel, sinLatitude, sinLeftLon, cosLatitude, cosLeftLon, latitude, leftLon);
+ this.RHC = new GeoPoint(planetModel, sinLatitude, sinRightLon, cosLatitude, cosRightLon, latitude, rightLon);
+
+ this.plane = new Plane(planetModel, sinLatitude);
+
+ // Normalize
+ while (leftLon > rightLon) {
+ rightLon += Math.PI * 2.0;
+ }
+ double middleLon = (leftLon + rightLon) * 0.5;
+ double sinMiddleLon = Math.sin(middleLon);
+ double cosMiddleLon = Math.cos(middleLon);
+
+ this.centerPoint = new GeoPoint(planetModel, sinLatitude, sinMiddleLon, cosLatitude, cosMiddleLon);
+
+ this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
+ this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
+
+ this.planePoints = new GeoPoint[]{LHC, RHC};
+
+ this.eitherBound = new EitherBound();
+
+ this.edgePoints = new GeoPoint[]{centerPoint};
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ final double newTopLat = latitude + angle;
+ final double newBottomLat = latitude - angle;
+ // Figuring out when we escalate to a special case requires some prefiguring
+ double currentLonSpan = rightLon - leftLon;
+ if (currentLonSpan < 0.0)
+ currentLonSpan += Math.PI * 2.0;
+ double newLeftLon = leftLon - angle;
+ double newRightLon = rightLon + angle;
+ if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
+ newLeftLon = -Math.PI;
+ newRightLon = Math.PI;
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return plane.evaluateIsZero(x, y, z) &&
+ (leftPlane.isWithin(x, y, z) ||
+ rightPlane.isWithin(x, y, z));
+ }
+
+ @Override
+ public double getRadius() {
+ // Here we compute the distance from the middle point to one of the corners. However, we need to be careful
+ // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and
+ // the distance to the right or left edge from the center.
+ final double topAngle = centerPoint.arcDistance(RHC);
+ final double bottomAngle = centerPoint.arcDistance(LHC);
+ return Math.max(topAngle, bottomAngle);
+ }
+
+ @Override
+ public GeoPoint getCenter() {
+ return centerPoint;
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+ // requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
+ return p.intersects(planetModel, plane, notablePoints, planePoints, bounds, eitherBound);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds.isWide()
+ .addHorizontalPlane(planetModel, latitude, plane, eitherBound)
+ .addPoint(LHC)
+ .addPoint(RHC);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ if (path.intersects(plane, planePoints, eitherBound)) {
+ return OVERLAPS;
+ }
+
+ if (path.isWithin(centerPoint)) {
+ return CONTAINS;
+ }
+
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ final double distance = distanceStyle.computeDistance(planetModel, plane, x,y,z, eitherBound);
+
+ final double LHCDistance = distanceStyle.computeDistance(LHC, x,y,z);
+ final double RHCDistance = distanceStyle.computeDistance(RHC, x,y,z);
+
+ return Math.min(
+ distance,
+ Math.min(LHCDistance, RHCDistance));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoWideDegenerateHorizontalLine))
+ return false;
+ GeoWideDegenerateHorizontalLine other = (GeoWideDegenerateHorizontalLine) o;
+ return super.equals(other) && other.LHC.equals(LHC) && other.RHC.equals(RHC);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + LHC.hashCode();
+ result = 31 * result + RHC.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoWideDegenerateHorizontalLine: {planetmodel="+planetModel+", latitude=" + latitude + "(" + latitude * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightLon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
+ }
+
+ /** Membership implementation representing a wide cutoff (more than 180 degrees).
+ */
+ protected class EitherBound implements Membership {
+ /** Constructor.
+ */
+ public EitherBound() {
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
+ }
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java
new file mode 100755
index 0000000..1d61876
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java
@@ -0,0 +1,208 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Bounding box wider than PI but limited on left and right sides (
+ * left lon, right lon).
+ *
+ * @lucene.internal
+ */
+public class GeoWideLongitudeSlice extends GeoBaseBBox {
+ /** The left longitude */
+ protected final double leftLon;
+ /** The right longitude */
+ protected final double rightLon;
+
+ /** The left plane */
+ protected final SidedPlane leftPlane;
+ /** The right plane */
+ protected final SidedPlane rightPlane;
+
+ /** Notable points for the shape */
+ protected final GeoPoint[] planePoints;
+
+ /** Center point for the shape */
+ protected final GeoPoint centerPoint;
+
+ /** A point on the edge of the shape */
+ protected final GeoPoint[] edgePoints;
+
+ /**
+ * Accepts only values in the following ranges: lon: {@code -PI -> PI}.
+ * Horizantal angle must be greater than or equal to PI.
+ *@param planetModel is the planet model.
+ *@param leftLon is the left longitude.
+ *@param rightLon is the right longitude.
+ */
+ public GeoWideLongitudeSlice(final PlanetModel planetModel, final double leftLon, double rightLon) {
+ super(planetModel);
+ // Argument checking
+ if (leftLon < -Math.PI || leftLon > Math.PI)
+ throw new IllegalArgumentException("Left longitude out of range");
+ if (rightLon < -Math.PI || rightLon > Math.PI)
+ throw new IllegalArgumentException("Right longitude out of range");
+ double extent = rightLon - leftLon;
+ if (extent < 0.0) {
+ extent += 2.0 * Math.PI;
+ }
+ if (extent < Math.PI)
+ throw new IllegalArgumentException("Width of rectangle too small");
+
+ this.leftLon = leftLon;
+ this.rightLon = rightLon;
+
+ final double sinLeftLon = Math.sin(leftLon);
+ final double cosLeftLon = Math.cos(leftLon);
+ final double sinRightLon = Math.sin(rightLon);
+ final double cosRightLon = Math.cos(rightLon);
+
+ // Normalize
+ while (leftLon > rightLon) {
+ rightLon += Math.PI * 2.0;
+ }
+ final double middleLon = (leftLon + rightLon) * 0.5;
+ this.centerPoint = new GeoPoint(planetModel, 0.0, middleLon);
+
+ this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
+ this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
+
+ this.planePoints = new GeoPoint[]{planetModel.NORTH_POLE, planetModel.SOUTH_POLE};
+ this.edgePoints = new GeoPoint[]{planetModel.NORTH_POLE};
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ // Figuring out when we escalate to a special case requires some prefiguring
+ double currentLonSpan = rightLon - leftLon;
+ if (currentLonSpan < 0.0)
+ currentLonSpan += Math.PI * 2.0;
+ double newLeftLon = leftLon - angle;
+ double newRightLon = rightLon + angle;
+ if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
+ newLeftLon = -Math.PI;
+ newRightLon = Math.PI;
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, Math.PI * 0.5, -Math.PI * 0.5, newLeftLon, newRightLon);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return leftPlane.isWithin(x, y, z) ||
+ rightPlane.isWithin(x, y, z);
+ }
+
+ @Override
+ public double getRadius() {
+ // Compute the extent and divide by two
+ double extent = rightLon - leftLon;
+ if (extent < 0.0)
+ extent += Math.PI * 2.0;
+ return Math.max(Math.PI * 0.5, extent * 0.5);
+ }
+
+ @Override
+ public GeoPoint getCenter() {
+ return centerPoint;
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+ // requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
+ return p.intersects(planetModel, leftPlane, notablePoints, planePoints, bounds) ||
+ p.intersects(planetModel, rightPlane, notablePoints, planePoints, bounds);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds.isWide()
+ .addVerticalPlane(planetModel, leftLon, leftPlane)
+ .addVerticalPlane(planetModel, rightLon, rightPlane)
+ .addPoint(planetModel.NORTH_POLE)
+ .addPoint(planetModel.SOUTH_POLE);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ final int insideRectangle = isShapeInsideBBox(path);
+ if (insideRectangle == SOME_INSIDE)
+ return OVERLAPS;
+
+ final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
+
+ if (insideRectangle == ALL_INSIDE && insideShape)
+ return OVERLAPS;
+
+ if (path.intersects(leftPlane, planePoints) ||
+ path.intersects(rightPlane, planePoints))
+ return OVERLAPS;
+
+ if (insideRectangle == ALL_INSIDE)
+ return WITHIN;
+
+ if (insideShape)
+ return CONTAINS;
+
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally
+ // unbounded distance to both the left and the right and only take the minimum of the two.
+ final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z);
+ final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z);
+
+ final double northDistance = distanceStyle.computeDistance(planetModel.NORTH_POLE, x,y,z);
+ final double southDistance = distanceStyle.computeDistance(planetModel.SOUTH_POLE, x,y,z);
+
+ return Math.min(
+ Math.min(leftDistance, rightDistance),
+ Math.min(northDistance, southDistance));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoWideLongitudeSlice))
+ return false;
+ GeoWideLongitudeSlice other = (GeoWideLongitudeSlice) o;
+ return super.equals(other) && other.leftLon == leftLon && other.rightLon == rightLon;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ long temp = Double.doubleToLongBits(leftLon);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(rightLon);
+ result = 31 * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoWideLongitudeSlice: {planetmodel="+planetModel+", leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java
new file mode 100644
index 0000000..9f9dd49
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java
@@ -0,0 +1,286 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Bounding box wider than PI but limited on three sides (
+ * bottom lat, left lon, right lon).
+ *
+ * @lucene.internal
+ */
+public class GeoWideNorthRectangle extends GeoBaseBBox {
+ /** Bottom latitude */
+ protected final double bottomLat;
+ /** Left longitude */
+ protected final double leftLon;
+ /** Right longitude */
+ protected final double rightLon;
+
+ /** The cosine of the middle latitude */
+ protected final double cosMiddleLat;
+
+ /** The lower right hand corner point */
+ protected final GeoPoint LRHC;
+ /** The lower left hand corner point */
+ protected final GeoPoint LLHC;
+
+ /** The bottom plane */
+ protected final SidedPlane bottomPlane;
+ /** The left plane */
+ protected final SidedPlane leftPlane;
+ /** The right plane */
+ protected final SidedPlane rightPlane;
+
+ /** Notable points for the bottom plane */
+ protected final GeoPoint[] bottomPlanePoints;
+ /** Notable points for the left plane */
+ protected final GeoPoint[] leftPlanePoints;
+ /** Notable points for the right plane */
+ protected final GeoPoint[] rightPlanePoints;
+
+ /** Center point */
+ protected final GeoPoint centerPoint;
+
+ /** Composite left/right bounds */
+ protected final EitherBound eitherBound;
+
+ /** A point on the edge */
+ protected 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 GeoWideNorthRectangle(final PlanetModel planetModel, final double bottomLat, final double leftLon, double rightLon) {
+ super(planetModel);
+ // Argument checking
+ if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Bottom latitude out of range");
+ if (leftLon < -Math.PI || leftLon > Math.PI)
+ throw new IllegalArgumentException("Left longitude out of range");
+ if (rightLon < -Math.PI || rightLon > Math.PI)
+ throw new IllegalArgumentException("Right longitude out of range");
+ double extent = rightLon - leftLon;
+ if (extent < 0.0) {
+ extent += 2.0 * Math.PI;
+ }
+ if (extent < Math.PI)
+ throw new IllegalArgumentException("Width of rectangle too small");
+
+ this.bottomLat = bottomLat;
+ this.leftLon = leftLon;
+ this.rightLon = rightLon;
+
+ final double sinBottomLat = Math.sin(bottomLat);
+ final double cosBottomLat = Math.cos(bottomLat);
+ final double sinLeftLon = Math.sin(leftLon);
+ final double cosLeftLon = Math.cos(leftLon);
+ final double sinRightLon = Math.sin(rightLon);
+ final double cosRightLon = Math.cos(rightLon);
+
+ // Now build the four points
+ this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+ this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
+
+ final double middleLat = (Math.PI * 0.5 + bottomLat) * 0.5;
+ final double sinMiddleLat = Math.sin(middleLat);
+ this.cosMiddleLat = Math.cos(middleLat);
+ // Normalize
+ while (leftLon > rightLon) {
+ rightLon += Math.PI * 2.0;
+ }
+ final double middleLon = (leftLon + rightLon) * 0.5;
+ final double sinMiddleLon = Math.sin(middleLon);
+ final double cosMiddleLon = Math.cos(middleLon);
+
+ this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon);
+
+ this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat);
+ this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
+ this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
+
+ this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC};
+ this.leftPlanePoints = new GeoPoint[]{planetModel.NORTH_POLE, LLHC};
+ this.rightPlanePoints = new GeoPoint[]{planetModel.NORTH_POLE, LRHC};
+
+ this.eitherBound = new EitherBound();
+ this.edgePoints = new GeoPoint[]{planetModel.NORTH_POLE};
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ final double newTopLat = Math.PI * 0.5;
+ final double newBottomLat = bottomLat - angle;
+ // Figuring out when we escalate to a special case requires some prefiguring
+ double currentLonSpan = rightLon - leftLon;
+ if (currentLonSpan < 0.0)
+ currentLonSpan += Math.PI * 2.0;
+ double newLeftLon = leftLon - angle;
+ double newRightLon = rightLon + angle;
+ if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
+ newLeftLon = -Math.PI;
+ newRightLon = Math.PI;
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return
+ bottomPlane.isWithin(x, y, z) &&
+ (leftPlane.isWithin(x, y, z) ||
+ rightPlane.isWithin(x, y, z));
+ }
+
+ @Override
+ public double getRadius() {
+ // Here we compute the distance from the middle point to one of the corners. However, we need to be careful
+ // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and
+ // the distance to the right or left edge from the center.
+ final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
+ final double bottomAngle = centerPoint.arcDistance(LLHC);
+ return Math.max(centerAngle, bottomAngle);
+ }
+
+ @Override
+ public GeoPoint getCenter() {
+ return centerPoint;
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+ // requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
+ return
+ p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, eitherBound) ||
+ p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, bottomPlane) ||
+ p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, bottomPlane);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds.isWide()
+ .addHorizontalPlane(planetModel, bottomLat, bottomPlane, eitherBound)
+ .addVerticalPlane(planetModel, leftLon, leftPlane, bottomPlane)
+ .addVerticalPlane(planetModel, rightLon, rightPlane, bottomPlane)
+ .addPoint(LLHC).addPoint(LRHC).addPoint(planetModel.NORTH_POLE);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ //System.err.println(this+" comparing to "+path);
+ final int insideRectangle = isShapeInsideBBox(path);
+ if (insideRectangle == SOME_INSIDE) {
+ //System.err.println(" some inside");
+ return OVERLAPS;
+ }
+
+ final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
+
+ if (insideRectangle == ALL_INSIDE && insideShape) {
+ //System.err.println(" both inside each other");
+ return OVERLAPS;
+ }
+
+ if (
+ path.intersects(bottomPlane, bottomPlanePoints, eitherBound) ||
+ path.intersects(leftPlane, leftPlanePoints, bottomPlane) ||
+ path.intersects(rightPlane, rightPlanePoints, bottomPlane)) {
+ //System.err.println(" edges intersect");
+ return OVERLAPS;
+ }
+
+ if (insideRectangle == ALL_INSIDE) {
+ //System.err.println(" shape inside rectangle");
+ return WITHIN;
+ }
+
+ if (insideShape) {
+ //System.err.println(" rectangle inside shape");
+ return CONTAINS;
+ }
+
+ //System.err.println(" disjoint");
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, eitherBound);
+ // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally
+ // unbounded distance to both the left and the right and only take the minimum of the two.
+ final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, bottomPlane);
+ final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, bottomPlane);
+
+ final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+ final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+
+ return Math.min(
+ Math.min(
+ bottomDistance,
+ Math.min(leftDistance, rightDistance)),
+ Math.min(LRHCDistance, LLHCDistance));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoWideNorthRectangle))
+ return false;
+ GeoWideNorthRectangle other = (GeoWideNorthRectangle) o;
+ return super.equals(other) && other.LLHC.equals(LLHC) && other.LRHC.equals(LRHC);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + LLHC.hashCode();
+ result = 31 * result + LRHC.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoWideNorthRectangle: {planetmodel="+planetModel+", bottomlat=" + bottomLat + "(" + bottomLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
+ }
+
+ /** Membership implementation representing a wide (more than 180 degree) bound.
+ */
+ protected class EitherBound implements Membership {
+ /** Constructor.
+ */
+ public EitherBound() {
+ }
+
+ @Override
+ public boolean isWithin(final Vector v) {
+ return leftPlane.isWithin(v) || rightPlane.isWithin(v);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
+ }
+ }
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java
new file mode 100755
index 0000000..c561747
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java
@@ -0,0 +1,319 @@
+/*
+ * 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.
+ */
+package org.apache.lucene.spatial3d.geom;
+
+/**
+ * Bounding box wider than PI but limited on four sides (top lat,
+ * bottom lat, left lon, right lon).
+ *
+ * @lucene.internal
+ */
+public class GeoWideRectangle extends GeoBaseBBox {
+ /** The top latitude */
+ protected final double topLat;
+ /** The bottom latitude */
+ protected final double bottomLat;
+ /** The left longitude */
+ protected final double leftLon;
+ /** The right longitude */
+ protected final double rightLon;
+
+ /** Cosine of the middle latitude */
+ protected final double cosMiddleLat;
+
+ /** Upper left hand corner point */
+ protected final GeoPoint ULHC;
+ /** Lower right hand corner point */
+ protected final GeoPoint URHC;
+ /** Lower right hand corner point */
+ protected final GeoPoint LRHC;
+ /** Lower left hand corner point */
+ protected final GeoPoint LLHC;
+
+ /** Top plane */
+ protected final SidedPlane topPlane;
+ /** Bottom plane */
+ protected final SidedPlane bottomPlane;
+ /** Left plane */
+ protected final SidedPlane leftPlane;
+ /** Right plane */
+ protected final SidedPlane rightPlane;
+
+ /** Top plane's notable points */
+ protected final GeoPoint[] topPlanePoints;
+ /** Bottom plane's notable points */
+ protected final GeoPoint[] bottomPlanePoints;
+ /** Left plane's notable points */
+ protected final GeoPoint[] leftPlanePoints;
+ /** Right plane's notable points */
+ protected final GeoPoint[] rightPlanePoints;
+
+ /** Center point */
+ protected final GeoPoint centerPoint;
+
+ /** Combined left/right bounds */
+ protected final EitherBound eitherBound;
+
+ /** A point on the edge */
+ protected final GeoPoint[] edgePoints;
+
+ /**
+ * Accepts only values in the following ranges: lat: {@code -PI/2 -> PI/2}, lon: {@code -PI -> PI}.
+ * Horizontal angle must be greater than or equal to PI.
+ */
+ public GeoWideRectangle(final PlanetModel planetModel, final double topLat, final double bottomLat, final double leftLon, double rightLon) {
+ super(planetModel);
+ // Argument checking
+ if (topLat > Math.PI * 0.5 || topLat < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Top latitude out of range");
+ if (bottomLat > Math.PI * 0.5 || bottomLat < -Math.PI * 0.5)
+ throw new IllegalArgumentException("Bottom latitude out of range");
+ if (topLat < bottomLat)
+ throw new IllegalArgumentException("Top latitude less than bottom latitude");
+ if (leftLon < -Math.PI || leftLon > Math.PI)
+ throw new IllegalArgumentException("Left longitude out of range");
+ if (rightLon < -Math.PI || rightLon > Math.PI)
+ throw new IllegalArgumentException("Right longitude out of range");
+ double extent = rightLon - leftLon;
+ if (extent < 0.0) {
+ extent += 2.0 * Math.PI;
+ }
+ if (extent < Math.PI)
+ throw new IllegalArgumentException("Width of rectangle too small");
+
+ this.topLat = topLat;
+ this.bottomLat = bottomLat;
+ this.leftLon = leftLon;
+ this.rightLon = rightLon;
+
+ final double sinTopLat = Math.sin(topLat);
+ final double cosTopLat = Math.cos(topLat);
+ final double sinBottomLat = Math.sin(bottomLat);
+ final double cosBottomLat = Math.cos(bottomLat);
+ final double sinLeftLon = Math.sin(leftLon);
+ final double cosLeftLon = Math.cos(leftLon);
+ final double sinRightLon = Math.sin(rightLon);
+ final double cosRightLon = Math.cos(rightLon);
+
+ // Now build the four points
+ this.ULHC = new GeoPoint(planetModel, sinTopLat, sinLeftLon, cosTopLat, cosLeftLon, topLat, leftLon);
+ this.URHC = new GeoPoint(planetModel, sinTopLat, sinRightLon, cosTopLat, cosRightLon, topLat, rightLon);
+ this.LRHC = new GeoPoint(planetModel, sinBottomLat, sinRightLon, cosBottomLat, cosRightLon, bottomLat, rightLon);
+ this.LLHC = new GeoPoint(planetModel, sinBottomLat, sinLeftLon, cosBottomLat, cosLeftLon, bottomLat, leftLon);
+
+ final double middleLat = (topLat + bottomLat) * 0.5;
+ final double sinMiddleLat = Math.sin(middleLat);
+ this.cosMiddleLat = Math.cos(middleLat);
+ // Normalize
+ while (leftLon > rightLon) {
+ rightLon += Math.PI * 2.0;
+ }
+ final double middleLon = (leftLon + rightLon) * 0.5;
+ final double sinMiddleLon = Math.sin(middleLon);
+ final double cosMiddleLon = Math.cos(middleLon);
+
+ this.centerPoint = new GeoPoint(planetModel, sinMiddleLat, sinMiddleLon, cosMiddleLat, cosMiddleLon);
+
+ this.topPlane = new SidedPlane(centerPoint, planetModel, sinTopLat);
+ this.bottomPlane = new SidedPlane(centerPoint, planetModel, sinBottomLat);
+ this.leftPlane = new SidedPlane(centerPoint, cosLeftLon, sinLeftLon);
+ this.rightPlane = new SidedPlane(centerPoint, cosRightLon, sinRightLon);
+
+ this.topPlanePoints = new GeoPoint[]{ULHC, URHC};
+ this.bottomPlanePoints = new GeoPoint[]{LLHC, LRHC};
+ this.leftPlanePoints = new GeoPoint[]{ULHC, LLHC};
+ this.rightPlanePoints = new GeoPoint[]{URHC, LRHC};
+
+ this.eitherBound = new EitherBound();
+
+ this.edgePoints = new GeoPoint[]{ULHC};
+ }
+
+ @Override
+ public GeoBBox expand(final double angle) {
+ final double newTopLat = topLat + angle;
+ final double newBottomLat = bottomLat - angle;
+ // Figuring out when we escalate to a special case requires some prefiguring
+ double currentLonSpan = rightLon - leftLon;
+ if (currentLonSpan < 0.0)
+ currentLonSpan += Math.PI * 2.0;
+ double newLeftLon = leftLon - angle;
+ double newRightLon = rightLon + angle;
+ if (currentLonSpan + 2.0 * angle >= Math.PI * 2.0) {
+ newLeftLon = -Math.PI;
+ newRightLon = Math.PI;
+ }
+ return GeoBBoxFactory.makeGeoBBox(planetModel, newTopLat, newBottomLat, newLeftLon, newRightLon);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return topPlane.isWithin(x, y, z) &&
+ bottomPlane.isWithin(x, y, z) &&
+ (leftPlane.isWithin(x, y, z) ||
+ rightPlane.isWithin(x, y, z));
+ }
+
+ @Override
+ public double getRadius() {
+ // Here we compute the distance from the middle point to one of the corners. However, we need to be careful
+ // to use the longest of three distances: the distance to a corner on the top; the distnace to a corner on the bottom, and
+ // the distance to the right or left edge from the center.
+ final double centerAngle = (rightLon - (rightLon + leftLon) * 0.5) * cosMiddleLat;
+ final double topAngle = centerPoint.arcDistance(URHC);
+ final double bottomAngle = centerPoint.arcDistance(LLHC);
+ return Math.max(centerAngle, Math.max(topAngle, bottomAngle));
+ }
+
+ @Override
+ public GeoPoint[] getEdgePoints() {
+ return edgePoints;
+ }
+
+ /**
+ * Returns the center of a circle into which the area will be inscribed.
+ *
+ * @return the center.
+ */
+ @Override
+ public GeoPoint getCenter() {
+ return centerPoint;
+ }
+
+ @Override
+ public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+ // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+ // requires crossing into the right part of the other. So intersection can ignore the left/right bounds.
+ return p.intersects(planetModel, topPlane, notablePoints, topPlanePoints, bounds, bottomPlane, eitherBound) ||
+ p.intersects(planetModel, bottomPlane, notablePoints, bottomPlanePoints, bounds, topPlane, eitherBound) ||
+ p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, topPlane, bottomPlane) ||
+ p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane, bottomPlane);
+ }
+
+ @Override
+ public void getBounds(Bounds bounds) {
+ super.getBounds(bounds);
+ bounds.isWide()
+ .addHorizontalPlane(planetModel, topLat, topPlane, bottomPlane, eitherBound)
+ .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane, bottomPlane)
+ .addHorizontalPlane(planetModel, bottomLat, bottomPlane, topPlane, eitherBound)
+ .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane, bottomPlane)
+ .addPoint(ULHC).addPoint(URHC).addPoint(LRHC).addPoint(LLHC);
+ }
+
+ @Override
+ public int getRelationship(final GeoShape path) {
+ //System.err.println(this+" comparing to "+path);
+ final int insideRectangle = isShapeInsideBBox(path);
+ if (insideRectangle == SOME_INSIDE) {
+ //System.err.println(" some inside");
+ return OVERLAPS;
+ }
+
+ final boolean insideShape = path.isWithin(ULHC);
+
+ if (insideRectangle == ALL_INSIDE && insideShape) {
+ //System.err.println(" both inside each other");
+ return OVERLAPS;
+ }
+
+ if (path.intersects(topPlane, topPlanePoints, bottomPlane, eitherBound) ||
+ path.intersects(bottomPlane, bottomPlanePoints, topPlane, eitherBound) ||
+ path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane) ||
+ path.intersects(rightPlane, rightPlanePoints, topPlane, bottomPlane)) {
+ //System.err.println(" edges intersect");
+ return OVERLAPS;
+ }
+
+ if (insideRectangle == ALL_INSIDE) {
+ //System.err.println(" shape inside rectangle");
+ return WITHIN;
+ }
+
+ if (insideShape) {
+ //System.err.println(" rectangle inside shape");
+ return CONTAINS;
+ }
+
+ //System.err.println(" disjoint");
+ return DISJOINT;
+ }
+
+ @Override
+ protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+ final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, eitherBound);
+ final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane, eitherBound);
+ // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally
+ // unbounded distance to both the left and the right and only take the minimum of the two.
+ final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, topPlane, bottomPlane);
+ final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, topPlane, bottomPlane);
+
+ final double ULHCDistance = distanceStyle.computeDistance(ULHC, x,y,z);
+ final double URHCDistance = distanceStyle.computeDistance(URHC, x,y,z);
+ final double LRHCDistance = distanceStyle.computeDistance(LRHC, x,y,z);
+ final double LLHCDistance = distanceStyle.computeDistance(LLHC, x,y,z);
+
+ return Math.min(
+ Math.min(
+ Math.min(topDistance, bottomDistance),
+ Math.min(leftDistance, rightDistance)),
+ Math.min(
+ Math.min(ULHCDistance, URHCDistance),
+ Math.min(LRHCDistance, LLHCDistance)));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof GeoWideRectangle))
+ return false;
+ GeoWideRectangle other = (GeoWideRectangle) o;
+ return super.equals(other) && other.ULHC.equals(ULHC) && other.LRHC.equals(LRHC);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + ULHC.hashCode();
+ result = 31 * result + LRHC.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "GeoWideRectangle: {planetmodel=" + planetModel + ", 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 + ")}";
+ }
+
+ /** A membership implementation representing a wide (more than 180) left/right bound.
+ */
+ protected class EitherBound implements Membership {
+ /** Constructor.
+ */
+ public EitherBound() {
+ }
+
+ @Override
+ public boolean isWithin(final Vector v) {
+ return leftPlane.isWithin(v) || rightPlane.isWithin(v);
+ }
+
+ @Override
+ public boolean isWithin(final double x, final double y, final double z) {
+ return leftPlane.isWithin(x, y, z) || rightPlane.isWithin(x, y, z);
+ }
+ }
+}
+