You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by no...@apache.org on 2016/03/09 17:00:25 UTC

[14/50] [abbrv] lucene-solr git commit: LUCENE-7056: Geo3D package re-org

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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/f7f81c32/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);
+    }
+  }
+}
+