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:03 UTC

[22/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/GeoWideSouthRectangle.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java
new file mode 100644
index 0000000..da9799a
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java
@@ -0,0 +1,284 @@
+/*
+ * 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 (top lat,
+ * left lon, right lon).
+ *
+ * @lucene.internal
+ */
+public class GeoWideSouthRectangle extends GeoBaseBBox {
+  /** Top latitude of rect */
+  protected final double topLat;
+  /** Left longitude of rect */
+  protected final double leftLon;
+  /** Right longitude of rect */
+  protected final double rightLon;
+
+  /** Cosine of middle latitude */
+  protected final double cosMiddleLat;
+
+  /** Upper left hand corner */
+  protected final GeoPoint ULHC;
+  /** Upper right hand corner */
+  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 top plane */
+  protected final GeoPoint[] topPlanePoints;
+  /** Notable points for left plane */
+  protected final GeoPoint[] leftPlanePoints;
+  /** Notable points for right plane */
+  protected final GeoPoint[] rightPlanePoints;
+
+  /** Center point */
+  protected final GeoPoint centerPoint;
+
+  /** 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 GeoWideSouthRectangle(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 small");
+
+    this.topLat = topLat;
+    this.leftLon = leftLon;
+    this.rightLon = rightLon;
+
+    final double sinTopLat = Math.sin(topLat);
+    final double cosTopLat = Math.cos(topLat);
+    final double sinLeftLon = Math.sin(leftLon);
+    final double cosLeftLon = Math.cos(leftLon);
+    final double sinRightLon = Math.sin(rightLon);
+    final double cosRightLon = Math.cos(rightLon);
+
+    // Now build the four points
+    this.ULHC = new GeoPoint(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.eitherBound = new EitherBound();
+    
+    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 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, topPlane, notablePoints, topPlanePoints, bounds, eitherBound) ||
+        p.intersects(planetModel, leftPlane, notablePoints, leftPlanePoints, bounds, topPlane) ||
+        p.intersects(planetModel, rightPlane, notablePoints, rightPlanePoints, bounds, topPlane);
+  }
+
+  @Override
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    bounds.isWide()
+      .addHorizontalPlane(planetModel, topLat, topPlane, eitherBound)
+      .addVerticalPlane(planetModel, rightLon, rightPlane, topPlane)
+      .addVerticalPlane(planetModel, leftLon, leftPlane, topPlane)
+      .addPoint(ULHC).addPoint(URHC).addPoint(planetModel.SOUTH_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.SOUTH_POLE);
+
+    if (insideRectangle == ALL_INSIDE && insideShape) {
+      //System.err.println(" both inside each other");
+      return OVERLAPS;
+    }
+
+    if (path.intersects(topPlane, topPlanePoints, eitherBound) ||
+        path.intersects(leftPlane, leftPlanePoints, topPlane) ||
+        path.intersects(rightPlane, rightPlanePoints, topPlane)) {
+      //System.err.println(" edges intersect");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE) {
+      //System.err.println(" shape inside rectangle");
+      return WITHIN;
+    }
+
+    if (insideShape) {
+      //System.err.println(" rectangle inside shape");
+      return CONTAINS;
+    }
+
+    //System.err.println(" disjoint");
+    return DISJOINT;
+  }
+
+  @Override
+  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, 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);
+    final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, 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 GeoWideSouthRectangle))
+      return false;
+    GeoWideSouthRectangle other = (GeoWideSouthRectangle) o;
+    return super.equals(o) && 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 "GeoWideSouthRectangle: {planetmodel="+planetModel+", toplat=" + topLat + "(" + topLat * 180.0 / Math.PI + "), leftlon=" + leftLon + "(" + leftLon * 180.0 / Math.PI + "), rightlon=" + rightLon + "(" + rightLon * 180.0 / Math.PI + ")}";
+  }
+
+  /** Membership implementation representing width more than 180.
+   */
+  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/GeoWorld.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java
new file mode 100755
index 0000000..25bdc96
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java
@@ -0,0 +1,106 @@
+/*
+ * 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 including the entire world.
+ *
+ * @lucene.internal
+ */
+public class GeoWorld extends GeoBaseBBox {
+  /** No points on the edge of the shape */
+  protected final static GeoPoint[] edgePoints = new GeoPoint[0];
+  /** Point in the middle of the world */
+  protected final GeoPoint originPoint;
+  
+  /** Constructor.
+   *@param planetModel is the planet model.
+   */
+  public GeoWorld(final PlanetModel planetModel) {
+    super(planetModel);
+    originPoint = new GeoPoint(planetModel.ab, 1.0, 0.0, 0.0);
+  }
+
+  @Override
+  public GeoBBox expand(final double angle) {
+    return this;
+  }
+
+  @Override
+  public double getRadius() {
+    return Math.PI;
+  }
+
+  @Override
+  public GeoPoint getCenter() {
+    // Totally arbitrary
+    return originPoint;
+  }
+
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    return true;
+  }
+
+  @Override
+  public GeoPoint[] getEdgePoints() {
+    return edgePoints;
+  }
+
+  @Override
+  public boolean intersects(final Plane p, final GeoPoint[] notablePoints, final Membership... bounds) {
+    return false;
+  }
+
+  @Override
+  public void getBounds(Bounds bounds) {
+    super.getBounds(bounds);
+    // Unnecessary
+    //bounds.noLongitudeBound().noTopLatitudeBound().noBottomLatitudeBound();
+  }
+
+  @Override
+  public int getRelationship(final GeoShape path) {
+    if (path.getEdgePoints().length > 0)
+      // Path is always within the world
+      return WITHIN;
+
+    return OVERLAPS;
+  }
+
+  @Override
+  protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
+    return 0.0;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof GeoWorld))
+      return false;
+    return super.equals(o);
+  }
+
+  @Override
+  public int hashCode() {
+    return super.hashCode();
+  }
+
+  @Override
+  public String toString() {
+    return "GeoWorld: {planetmodel="+planetModel+"}";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LatLonBounds.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LatLonBounds.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LatLonBounds.java
new file mode 100644
index 0000000..627fdae
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LatLonBounds.java
@@ -0,0 +1,322 @@
+/*
+ * 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;
+
+/**
+ * An object for accumulating latitude/longitude bounds information.
+ *
+ * @lucene.experimental
+ */
+public class LatLonBounds implements Bounds {
+
+  /** Set to true if no longitude bounds can be stated */
+  protected boolean noLongitudeBound = false;
+  /** Set to true if no top latitude bound can be stated */
+  protected boolean noTopLatitudeBound = false;
+  /** Set to true if no bottom latitude bound can be stated */
+  protected boolean noBottomLatitudeBound = false;
+
+  /** If non-null, the minimum latitude bound */
+  protected Double minLatitude = null;
+  /** If non-null, the maximum latitude bound */
+  protected Double maxLatitude = null;
+
+  // For longitude bounds, this class needs to worry about keeping track of the distinction
+  // between left-side bounds and right-side bounds.  Points are always submitted in pairs
+  // which have a maximum longitude separation of Math.PI.  It's therefore always possible
+  // to determine which point represents a left bound, and which point represents a right
+  // bound.
+  //
+  // The next problem is how to compare two of the same kind of bound, e.g. two left bounds.
+  // We need to keep track of the leftmost longitude of the shape, but since this is a circle,
+  // this is arbitrary.  What we could try to do instead would be to find a pair of (left,right) bounds such
+  // that:
+  // (1) all other bounds are within, and
+  // (2) the left minus right distance is minimized
+  // Unfortunately, there are still shapes that cannot be summarized in this way correctly.
+  // For example. consider a spiral that entirely circles the globe; we might arbitrarily choose
+  // lat/lon bounds that do not in fact circle the globe.
+  //
+  // One way to handle the longitude issue correctly is therefore to stipulate that we
+  // walk the bounds of the shape in some kind of connected order.  Each point or circle is therefore
+  // added in a sequence.  We also need an interior point to make sure we have the right
+  // choice of longitude bounds.  But even with this, we still can't always choose whether the actual shape
+  // goes right or left.
+  //
+  // We can make the specification truly general by submitting the following in order:
+  // addSide(PlaneSide side, Membership... constraints)
+  // ...
+  // This is unambiguous, but I still can't see yet how this would help compute the bounds.  The plane
+  // solution would in general seem to boil down to the same logic that relies on points along the path
+  // to define the shape boundaries.  I guess the one thing that you do know for a bounded edge is that
+  // the endpoints are actually connected.  But it is not clear whether relationship helps in any way.
+  //
+  // In any case, if we specify shapes by a sequence of planes, we should stipulate that multiple sequences
+  // are allowed, provided they progressively tile an area of the sphere that is connected and sequential.
+  // For example, paths do alternating rectangles and circles, in sequence.  Each sequence member is
+  // described by a sequence of planes.  I think it would also be reasonable to insist that the first segment
+  // of a shape overlap or adjoin the previous shape.
+  //
+  // Here's a way to think about it that might help: Traversing every edge should grow the longitude bounds
+  // in the direction of the traversal.  So if the traversal is always known to be less than PI in total longitude
+  // angle, then it is possible to use the endpoints to determine the unambiguous extension of the envelope.
+  // For example, say you are currently at longitude -0.5.  The next point is at longitude PI-0.1.  You could say
+  // that the difference in longitude going one way around would be beter than the distance the other way
+  // around, and therefore the longitude envelope should be extended accordingly.  But in practice, when an
+  // edge goes near a pole and may be inclined as well, the longer longitude change might be the right path, even
+  // if the arc length is short.  So this too doesn't work.
+  //
+  // Given we have a hard time making an exact match, here's the current proposal.  The proposal is a
+  // heuristic, based on the idea that most areas are small compared to the circumference of the globe.
+  // We keep track of the last point we saw, and take each point as it arrives, and compute its longitude.
+  // Then, we have a choice as to which way to expand the envelope: we can expand by going to the left or
+  // to the right.  We choose the direction with the least longitude difference.  (If we aren't sure,
+  // and can recognize that, we can set "unconstrained in longitude".)
+
+  /** If non-null, the left longitude bound */
+  protected Double leftLongitude = null;
+  /** If non-null, the right longitude bound */
+  protected Double rightLongitude = null;
+
+  /** Construct an empty bounds object */
+  public LatLonBounds() {
+  }
+
+  // Accessor methods
+  
+  /** Get maximum latitude, if any.
+   *@return maximum latitude or null.
+   */
+  public Double getMaxLatitude() {
+    return maxLatitude;
+  }
+
+  /** Get minimum latitude, if any.
+   *@return minimum latitude or null.
+   */
+  public Double getMinLatitude() {
+    return minLatitude;
+  }
+
+  /** Get left longitude, if any.
+   *@return left longitude, or null.
+   */
+  public Double getLeftLongitude() {
+    return leftLongitude;
+  }
+
+  /** Get right longitude, if any.
+   *@return right longitude, or null.
+   */
+  public Double getRightLongitude() {
+    return rightLongitude;
+  }
+
+  // Degenerate case check
+  
+  /** Check if there's no longitude bound.
+   *@return true if no longitude bound.
+   */
+  public boolean checkNoLongitudeBound() {
+    return noLongitudeBound;
+  }
+
+  /** Check if there's no top latitude bound.
+   *@return true if no top latitude bound.
+   */
+  public boolean checkNoTopLatitudeBound() {
+    return noTopLatitudeBound;
+  }
+
+  /** Check if there's no bottom latitude bound.
+   *@return true if no bottom latitude bound.
+   */
+  public boolean checkNoBottomLatitudeBound() {
+    return noBottomLatitudeBound;
+  }
+
+  // Modification methods
+  
+  @Override
+  public Bounds addPlane(final PlanetModel planetModel, final Plane plane, final Membership... bounds) {
+    plane.recordBounds(planetModel, this, bounds);
+    return this;
+  }
+
+  @Override
+  public Bounds addHorizontalPlane(final PlanetModel planetModel,
+    final double latitude,
+    final Plane horizontalPlane,
+    final Membership... bounds) {
+    if (!noTopLatitudeBound || !noBottomLatitudeBound) {
+      addLatitudeBound(latitude);
+    }
+    return this;
+  }
+    
+  @Override
+  public Bounds addVerticalPlane(final PlanetModel planetModel,
+    final double longitude,
+    final Plane verticalPlane,
+    final Membership... bounds) {
+    if (!noLongitudeBound) {
+      addLongitudeBound(longitude);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds isWide() {
+    return noLongitudeBound();
+  }
+
+  @Override
+  public Bounds addXValue(final GeoPoint point) {
+    if (!noLongitudeBound) {
+      // Get a longitude value
+      addLongitudeBound(point.getLongitude());
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addYValue(final GeoPoint point) {
+    if (!noLongitudeBound) {
+      // Get a longitude value
+      addLongitudeBound(point.getLongitude());
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addZValue(final GeoPoint point) {
+    if (!noTopLatitudeBound || !noBottomLatitudeBound) {
+      // Compute a latitude value
+      double latitude = point.getLatitude();
+      addLatitudeBound(latitude);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addPoint(GeoPoint point) {
+    if (!noLongitudeBound) {
+      // Get a longitude value
+      addLongitudeBound(point.getLongitude());
+    }
+    if (!noTopLatitudeBound || !noBottomLatitudeBound) {
+      // Compute a latitude value
+      addLatitudeBound(point.getLatitude());
+    }
+    return this;
+  }
+  
+  @Override
+  public Bounds noLongitudeBound() {
+    noLongitudeBound = true;
+    leftLongitude = null;
+    rightLongitude = null;
+    return this;
+  }
+
+  @Override
+  public Bounds noTopLatitudeBound() {
+    noTopLatitudeBound = true;
+    maxLatitude = null;
+    return this;
+  }
+
+  @Override
+  public Bounds noBottomLatitudeBound() {
+    noBottomLatitudeBound = true;
+    minLatitude = null;
+    return this;
+  }
+
+  // Protected methods
+  
+  /** Update latitude bound.
+   *@param latitude is the latitude.
+   */
+  protected void addLatitudeBound(double latitude) {
+    if (!noTopLatitudeBound && (maxLatitude == null || latitude > maxLatitude))
+      maxLatitude = latitude;
+    if (!noBottomLatitudeBound && (minLatitude == null || latitude < minLatitude))
+      minLatitude = latitude;
+  }
+
+  /** Update longitude bound.
+   *@param longitude is the new longitude value.
+   */
+  protected void addLongitudeBound(double longitude) {
+    // If this point is within the current bounds, we're done; otherwise
+    // expand one side or the other.
+    if (leftLongitude == null && rightLongitude == null) {
+      leftLongitude = longitude;
+      rightLongitude = longitude;
+    } else {
+      // Compute whether we're to the right of the left value.  But the left value may be greater than
+      // the right value.
+      double currentLeftLongitude = leftLongitude;
+      double currentRightLongitude = rightLongitude;
+      if (currentRightLongitude < currentLeftLongitude)
+        currentRightLongitude += 2.0 * Math.PI;
+      // We have a range to look at that's going in the right way.
+      // Now, do the same trick with the computed longitude.
+      if (longitude < currentLeftLongitude)
+        longitude += 2.0 * Math.PI;
+
+      if (longitude < currentLeftLongitude || longitude > currentRightLongitude) {
+        // Outside of current bounds.  Consider carefully how we'll expand.
+        double leftExtensionAmt;
+        double rightExtensionAmt;
+        if (longitude < currentLeftLongitude) {
+          leftExtensionAmt = currentLeftLongitude - longitude;
+        } else {
+          leftExtensionAmt = currentLeftLongitude + 2.0 * Math.PI - longitude;
+        }
+        if (longitude > currentRightLongitude) {
+          rightExtensionAmt = longitude - currentRightLongitude;
+        } else {
+          rightExtensionAmt = longitude + 2.0 * Math.PI - currentRightLongitude;
+        }
+        if (leftExtensionAmt < rightExtensionAmt) {
+          currentLeftLongitude = leftLongitude - leftExtensionAmt;
+          while (currentLeftLongitude <= -Math.PI) {
+            currentLeftLongitude += 2.0 * Math.PI;
+          }
+          leftLongitude = currentLeftLongitude;
+        } else {
+          currentRightLongitude = rightLongitude + rightExtensionAmt;
+          while (currentRightLongitude > Math.PI) {
+            currentRightLongitude -= 2.0 * Math.PI;
+          }
+          rightLongitude = currentRightLongitude;
+        }
+      }
+    }
+    double testRightLongitude = rightLongitude;
+    if (testRightLongitude < leftLongitude)
+      testRightLongitude += Math.PI * 2.0;
+    if (testRightLongitude - leftLongitude >= Math.PI) {
+      noLongitudeBound = true;
+      leftLongitude = null;
+      rightLongitude = null;
+    }
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearDistance.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearDistance.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearDistance.java
new file mode 100644
index 0000000..0c89a16
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearDistance.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * Linear distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class LinearDistance implements DistanceStyle {
+  
+  /** A convenient instance */
+  public final static LinearDistance INSTANCE = new LinearDistance();
+  
+  /** Constructor.
+   */
+  public LinearDistance() {
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.linearDistance(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.linearDistance(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.linearDistance(planetModel, point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.linearDistance(planetModel, x,y,z, bounds);
+  }
+
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearSquaredDistance.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearSquaredDistance.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearSquaredDistance.java
new file mode 100644
index 0000000..3fc37da
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/LinearSquaredDistance.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * Linear squared distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class LinearSquaredDistance implements DistanceStyle {
+  
+  /** A convenient instance */
+  public final static LinearSquaredDistance INSTANCE = new LinearSquaredDistance();
+  
+  /** Constructor.
+   */
+  public LinearSquaredDistance() {
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.linearDistanceSquared(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.linearDistanceSquared(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.linearDistanceSquared(planetModel, point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.linearDistanceSquared(planetModel, x,y,z, bounds);
+  }
+
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Membership.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Membership.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Membership.java
new file mode 100755
index 0000000..0cf6ff0
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Membership.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+/**
+ * Implemented by Geo3D shapes that can calculate if a point is within it or not.
+ *
+ * @lucene.experimental
+ */
+public interface Membership {
+
+  /**
+   * Check if a point is within this shape.
+   *
+   * @param point is the point to check.
+   * @return true if the point is within this shape
+   */
+  public default boolean isWithin(final Vector point) {
+    return isWithin(point.x, point.y, point.z);
+  }
+
+  /**
+   * Check if a point is within this shape.
+   *
+   * @param x is x coordinate of point to check.
+   * @param y is y coordinate of point to check.
+   * @param z is z coordinate of point to check.
+   * @return true if the point is within this shape
+   */
+  public boolean isWithin(final double x, final double y, final double z);
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalDistance.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalDistance.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalDistance.java
new file mode 100644
index 0000000..50b2c7f
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalDistance.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * Normal distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class NormalDistance implements DistanceStyle {
+  
+  /** A convenient instance */
+  public final static NormalDistance INSTANCE = new NormalDistance();
+  
+  /** Constructor.
+   */
+  public NormalDistance() {
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.normalDistance(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.normalDistance(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.normalDistance(point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.normalDistance(x,y,z, bounds);
+  }
+
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalSquaredDistance.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalSquaredDistance.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalSquaredDistance.java
new file mode 100644
index 0000000..a355d09
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/NormalSquaredDistance.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * Normal squared distance computation style.
+ *
+ * @lucene.experimental
+ */
+public class NormalSquaredDistance implements DistanceStyle {
+  
+  /** A convenient instance */
+  public final static NormalSquaredDistance INSTANCE = new NormalSquaredDistance();
+  
+  /** Constructor.
+   */
+  public NormalSquaredDistance() {
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final GeoPoint point2) {
+    return point1.normalDistanceSquared(point2);
+  }
+  
+  @Override
+  public double computeDistance(final GeoPoint point1, final double x2, final double y2, final double z2) {
+    return point1.normalDistanceSquared(x2,y2,z2);
+  }
+
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final GeoPoint point, final Membership... bounds) {
+    return plane.normalDistanceSquared(point, bounds);
+  }
+  
+  @Override
+  public double computeDistance(final PlanetModel planetModel, final Plane plane, final double x, final double y, final double z, final Membership... bounds) {
+    return plane.normalDistanceSquared(x,y,z, bounds);
+  }
+
+}
+
+