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:19:45 UTC

[04/32] lucene-solr git commit: LUCENE-7056: Geo3D package re-org (cherry picked from commit 0093e26)

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
new file mode 100755
index 0000000..e080bc0
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/SidedPlane.java
@@ -0,0 +1,175 @@
+/*
+ * 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;
+
+/**
+ * Combination of a plane, and a sign value indicating what evaluation values are on the correct
+ * side of the plane.
+ *
+ * @lucene.experimental
+ */
+public class SidedPlane extends Plane implements Membership {
+  /** The sign value for evaluation of a point on the correct side of the plane */
+  public final double sigNum;
+
+  /**
+   * Construct a SidedPlane identical to an existing one, but reversed.
+   *
+   * @param sidedPlane is the existing plane.
+   */
+  public SidedPlane(SidedPlane sidedPlane) {
+    super(sidedPlane, sidedPlane.D);
+    this.sigNum = -sidedPlane.sigNum;
+  }
+
+  /**
+   * Construct a sided plane from a pair of vectors describing points, and including
+   * origin, plus a point p which describes the side.
+   *
+   * @param p point to evaluate
+   * @param A is the first in-plane point
+   * @param B is the second in-plane point
+   */
+  public SidedPlane(Vector p, Vector A, Vector B) {
+    super(A, B);
+    sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
+  }
+
+  /**
+   * Construct a sided plane from a point and a Z coordinate.
+   *
+   * @param p      point to evaluate.
+   * @param planetModel is the planet model.
+   * @param sinLat is the sin of the latitude of the plane.
+   */
+  public SidedPlane(Vector p, final PlanetModel planetModel, double sinLat) {
+    super(planetModel, sinLat);
+    sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
+  }
+
+  /**
+   * Construct a sided vertical plane from a point and specified x and y coordinates.
+   *
+   * @param p point to evaluate.
+   * @param x is the specified x.
+   * @param y is the specified y.
+   */
+  public SidedPlane(Vector p, double x, double y) {
+    super(x, y);
+    sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
+  }
+
+  /**
+   * Construct a sided plane with a normal vector and offset.
+   *
+   * @param p point to evaluate.
+   * @param v is the normal vector.
+   * @param D is the origin offset for the plan.
+   */
+  public SidedPlane(Vector p, Vector v, double D) {
+    super(v, D);
+    sigNum = Math.signum(evaluate(p));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
+  }
+
+  /**
+   * Construct a sided plane with a normal vector and offset.
+   *
+   * @param pX X coord of point to evaluate.
+   * @param pY Y coord of point to evaluate.
+   * @param pZ Z coord of point to evaluate.
+   * @param v is the normal vector.
+   * @param D is the origin offset for the plan.
+   */
+  public SidedPlane(double pX, double pY, double pZ, Vector v, double D) {
+    super(v, D);
+    sigNum = Math.signum(evaluate(pX,pY,pZ));
+    if (sigNum == 0.0)
+      throw new IllegalArgumentException("Cannot determine sidedness because check point is on plane.");
+  }
+
+  /** Construct a sided plane from two points and a third normal vector.
+   */
+  public static SidedPlane constructNormalizedPerpendicularSidedPlane(final Vector insidePoint,
+    final Vector normalVector, final Vector point1, final Vector point2) {
+    final Vector pointsVector = new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z);
+    final Vector newNormalVector = new Vector(normalVector, pointsVector);
+    try {
+      // To construct the plane, we now just need D, which is simply the negative of the evaluation of the circle normal vector at one of the points.
+      return new SidedPlane(insidePoint, newNormalVector, -newNormalVector.dotProduct(point1));
+    } catch (IllegalArgumentException e) {
+      return null;
+    }
+  }
+  
+  /** Construct a sided plane from three points.
+   */
+  public static SidedPlane constructNormalizedThreePointSidedPlane(final Vector insidePoint,
+    final Vector point1, final Vector point2, final Vector point3) {
+    try {
+      final Vector planeNormal = new Vector(
+        new Vector(point1.x - point2.x, point1.y - point2.y, point1.z - point2.z),
+        new Vector(point2.x - point3.x, point2.y - point3.y, point2.z - point3.z));
+      return new SidedPlane(insidePoint, planeNormal, -planeNormal.dotProduct(point2));
+    } catch (IllegalArgumentException e) {
+      return null;
+    }
+  }
+
+  @Override
+  public boolean isWithin(double x, double y, double z) {
+    double evalResult = evaluate(x, y, z);
+    if (Math.abs(evalResult) < MINIMUM_RESOLUTION)
+      return true;
+    double sigNum = Math.signum(evalResult);
+    return sigNum == this.sigNum;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (!(o instanceof SidedPlane)) return false;
+    if (!super.equals(o)) return false;
+
+    SidedPlane that = (SidedPlane) o;
+
+    return Double.compare(that.sigNum, sigNum) == 0;
+
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    long temp;
+    temp = Double.doubleToLongBits(sigNum);
+    result = 31 * result + (int) (temp ^ (temp >>> 32));
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "[A=" + x + ", B=" + y + ", C=" + z + ", D=" + D + ", side=" + sigNum + "]";
+  }
+}
+  

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/StandardXYZSolid.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/StandardXYZSolid.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/StandardXYZSolid.java
new file mode 100644
index 0000000..492f7b4
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/StandardXYZSolid.java
@@ -0,0 +1,417 @@
+/*
+ * 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;
+
+/**
+ * 3D rectangle, bounded on six sides by X,Y,Z limits
+ *
+ * @lucene.internal
+ */
+public class StandardXYZSolid extends BaseXYZSolid {
+
+  /** Whole world? */
+  protected final boolean isWholeWorld;
+  /** Min-X plane */
+  protected final SidedPlane minXPlane;
+  /** Max-X plane */
+  protected final SidedPlane maxXPlane;
+  /** Min-Y plane */
+  protected final SidedPlane minYPlane;
+  /** Max-Y plane */
+  protected final SidedPlane maxYPlane;
+  /** Min-Z plane */
+  protected final SidedPlane minZPlane;
+  /** Max-Z plane */
+  protected final SidedPlane maxZPlane;
+  
+  /** These are the edge points of the shape, which are defined to be at least one point on
+   * each surface area boundary.  In the case of a solid, this includes points which represent
+   * the intersection of XYZ bounding planes and the planet, as well as points representing
+   * the intersection of single bounding planes with the planet itself.
+   */
+  protected final GeoPoint[] edgePoints;
+
+  /** Notable points for minXPlane */
+  protected final GeoPoint[] notableMinXPoints;
+  /** Notable points for maxXPlane */
+  protected final GeoPoint[] notableMaxXPoints;
+  /** Notable points for minYPlane */
+  protected final GeoPoint[] notableMinYPoints;
+  /** Notable points for maxYPlane */
+  protected final GeoPoint[] notableMaxYPoints;
+  /** Notable points for minZPlane */
+  protected final GeoPoint[] notableMinZPoints;
+  /** Notable points for maxZPlane */
+  protected final GeoPoint[] notableMaxZPoints;
+
+  /**
+   * Sole constructor
+   *
+   *@param planetModel is the planet model.
+   *@param minX is the minimum X value.
+   *@param maxX is the maximum X value.
+   *@param minY is the minimum Y value.
+   *@param maxY is the maximum Y value.
+   *@param minZ is the minimum Z value.
+   *@param maxZ is the maximum Z value.
+   */
+  public StandardXYZSolid(final PlanetModel planetModel,
+    final double minX,
+    final double maxX,
+    final double minY,
+    final double maxY,
+    final double minZ,
+    final double maxZ) {
+    super(planetModel);
+    // Argument checking
+    if (maxX - minX < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("X values in wrong order or identical");
+    if (maxY - minY < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("Y values in wrong order or identical");
+    if (maxZ - minZ < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("Z values in wrong order or identical");
+
+    final double worldMinX = planetModel.getMinimumXValue();
+    final double worldMaxX = planetModel.getMaximumXValue();
+    final double worldMinY = planetModel.getMinimumYValue();
+    final double worldMaxY = planetModel.getMaximumYValue();
+    final double worldMinZ = planetModel.getMinimumZValue();
+    final double worldMaxZ = planetModel.getMaximumZValue();
+    
+    // We must distinguish between the case where the solid represents the entire world,
+    // and when the solid has no overlap with any part of the surface.  In both cases,
+    // there will be no edgepoints.
+    isWholeWorld =
+        (minX - worldMinX < -Vector.MINIMUM_RESOLUTION) &&
+        (maxX - worldMaxX > Vector.MINIMUM_RESOLUTION) &&
+        (minY - worldMinY < -Vector.MINIMUM_RESOLUTION) &&
+        (maxY - worldMaxY > Vector.MINIMUM_RESOLUTION) &&
+        (minZ - worldMinZ < -Vector.MINIMUM_RESOLUTION) &&
+        (maxZ - worldMaxZ > Vector.MINIMUM_RESOLUTION);
+
+    if (isWholeWorld) {
+      minXPlane = null;
+      maxXPlane = null;
+      minYPlane = null;
+      maxYPlane = null;
+      minZPlane = null;
+      maxZPlane = null;
+      notableMinXPoints = null;
+      notableMaxXPoints = null;
+      notableMinYPoints = null;
+      notableMaxYPoints = null;
+      notableMinZPoints = null;
+      notableMaxZPoints = null;
+      edgePoints = null;
+    } else {
+      // Construct the planes
+      minXPlane = new SidedPlane(maxX,0.0,0.0,xUnitVector,-minX);
+      maxXPlane = new SidedPlane(minX,0.0,0.0,xUnitVector,-maxX);
+      minYPlane = new SidedPlane(0.0,maxY,0.0,yUnitVector,-minY);
+      maxYPlane = new SidedPlane(0.0,minY,0.0,yUnitVector,-maxY);
+      minZPlane = new SidedPlane(0.0,0.0,maxZ,zUnitVector,-minZ);
+      maxZPlane = new SidedPlane(0.0,0.0,minZ,zUnitVector,-maxZ);
+      
+      // We need at least one point on the planet surface for each manifestation of the shape.
+      // There can be up to 2 (on opposite sides of the world).  But we have to go through
+      // 12 combinations of adjacent planes in order to find out if any have 2 intersection solution.
+      // Typically, this requires 12 square root operations. 
+      final GeoPoint[] minXminY = minXPlane.findIntersections(planetModel,minYPlane,maxXPlane,maxYPlane,minZPlane,maxZPlane);
+      final GeoPoint[] minXmaxY = minXPlane.findIntersections(planetModel,maxYPlane,maxXPlane,minYPlane,minZPlane,maxZPlane);
+      final GeoPoint[] minXminZ = minXPlane.findIntersections(planetModel,minZPlane,maxXPlane,maxZPlane,minYPlane,maxYPlane);
+      final GeoPoint[] minXmaxZ = minXPlane.findIntersections(planetModel,maxZPlane,maxXPlane,minZPlane,minYPlane,maxYPlane);
+
+      final GeoPoint[] maxXminY = maxXPlane.findIntersections(planetModel,minYPlane,minXPlane,maxYPlane,minZPlane,maxZPlane);
+      final GeoPoint[] maxXmaxY = maxXPlane.findIntersections(planetModel,maxYPlane,minXPlane,minYPlane,minZPlane,maxZPlane);
+      final GeoPoint[] maxXminZ = maxXPlane.findIntersections(planetModel,minZPlane,minXPlane,maxZPlane,minYPlane,maxYPlane);
+      final GeoPoint[] maxXmaxZ = maxXPlane.findIntersections(planetModel,maxZPlane,minXPlane,minZPlane,minYPlane,maxYPlane);
+      
+      final GeoPoint[] minYminZ = minYPlane.findIntersections(planetModel,minZPlane,maxYPlane,maxZPlane,minXPlane,maxXPlane);
+      final GeoPoint[] minYmaxZ = minYPlane.findIntersections(planetModel,maxZPlane,maxYPlane,minZPlane,minXPlane,maxXPlane);
+      final GeoPoint[] maxYminZ = maxYPlane.findIntersections(planetModel,minZPlane,minYPlane,maxZPlane,minXPlane,maxXPlane);
+      final GeoPoint[] maxYmaxZ = maxYPlane.findIntersections(planetModel,maxZPlane,minYPlane,minZPlane,minXPlane,maxXPlane);
+      
+      notableMinXPoints = glueTogether(minXminY, minXmaxY, minXminZ, minXmaxZ);
+      notableMaxXPoints = glueTogether(maxXminY, maxXmaxY, maxXminZ, maxXmaxZ);
+      notableMinYPoints = glueTogether(minXminY, maxXminY, minYminZ, minYmaxZ);
+      notableMaxYPoints = glueTogether(minXmaxY, maxXmaxY, maxYminZ, maxYmaxZ);
+      notableMinZPoints = glueTogether(minXminZ, maxXminZ, minYminZ, maxYminZ);
+      notableMaxZPoints = glueTogether(minXmaxZ, maxXmaxZ, minYmaxZ, maxYmaxZ);
+
+      // Now, compute the edge points.
+      // This is the trickiest part of setting up an XYZSolid.  We've computed intersections already, so
+      // we'll start there.
+      // There can be a number of shapes, each of which needs an edgepoint.  Each side by itself might contribute
+      // an edgepoint, for instance, if the plane describing that side intercepts the planet in such a way that the ellipse
+      // of interception does not meet any other planes.  Plane intersections can each contribute 0, 1, or 2 edgepoints.
+      //
+      // All of this makes for a lot of potential edgepoints, but I believe these can be pruned back with careful analysis.
+      // I haven't yet done that analysis, however, so I will treat them all as individual edgepoints.
+      
+      // The cases we are looking for are when the four corner points for any given
+      // plane are all outside of the world, AND that plane intersects the world.
+      // There are eight corner points all told; we must evaluate these WRT the planet surface.
+      final boolean minXminYminZ = planetModel.pointOutside(minX, minY, minZ);
+      final boolean minXminYmaxZ = planetModel.pointOutside(minX, minY, maxZ);
+      final boolean minXmaxYminZ = planetModel.pointOutside(minX, maxY, minZ);
+      final boolean minXmaxYmaxZ = planetModel.pointOutside(minX, maxY, maxZ);
+      final boolean maxXminYminZ = planetModel.pointOutside(maxX, minY, minZ);
+      final boolean maxXminYmaxZ = planetModel.pointOutside(maxX, minY, maxZ);
+      final boolean maxXmaxYminZ = planetModel.pointOutside(maxX, maxY, minZ);
+      final boolean maxXmaxYmaxZ = planetModel.pointOutside(maxX, maxY, maxZ);
+        
+      // Look at single-plane/world intersections.
+      // We detect these by looking at the world model and noting its x, y, and z bounds.
+
+      final GeoPoint[] minXEdges;
+      if (minX - worldMinX >= -Vector.MINIMUM_RESOLUTION && minX - worldMaxX <= Vector.MINIMUM_RESOLUTION &&
+        minY < 0.0 && maxY > 0.0 && minZ < 0.0 && maxZ > 0.0 &&
+        minXminYminZ && minXminYmaxZ && minXmaxYminZ && minXmaxYmaxZ) {
+        // Find any point on the minX plane that intersects the world
+        // First construct a perpendicular plane that will allow us to find a sample point.
+        // This plane is vertical and goes through the points (0,0,0) and (1,0,0)
+        // Then use it to compute a sample point.
+        final GeoPoint intPoint = minXPlane.getSampleIntersectionPoint(planetModel, xVerticalPlane);
+        if (intPoint != null) {
+          minXEdges = new GeoPoint[]{intPoint};
+        } else {
+          // No intersection found?
+          minXEdges = EMPTY_POINTS;
+        }
+      } else {
+        minXEdges = EMPTY_POINTS;
+      }
+      
+      final GeoPoint[] maxXEdges;
+      if (maxX - worldMinX >= -Vector.MINIMUM_RESOLUTION && maxX - worldMaxX <= Vector.MINIMUM_RESOLUTION &&
+        minY < 0.0 && maxY > 0.0 && minZ < 0.0 && maxZ > 0.0 &&
+        maxXminYminZ && maxXminYmaxZ && maxXmaxYminZ && maxXmaxYmaxZ) {
+        // Find any point on the maxX plane that intersects the world
+        // First construct a perpendicular plane that will allow us to find a sample point.
+        // This plane is vertical and goes through the points (0,0,0) and (1,0,0)
+        // Then use it to compute a sample point.
+        final GeoPoint intPoint = maxXPlane.getSampleIntersectionPoint(planetModel, xVerticalPlane);
+        if (intPoint != null) {
+          maxXEdges = new GeoPoint[]{intPoint};
+        } else {
+          maxXEdges = EMPTY_POINTS;
+        }
+      } else {
+        maxXEdges = EMPTY_POINTS;
+      }
+      
+      final GeoPoint[] minYEdges;
+      if (minY - worldMinY >= -Vector.MINIMUM_RESOLUTION && minY - worldMaxY <= Vector.MINIMUM_RESOLUTION &&
+        minX < 0.0 && maxX > 0.0 && minZ < 0.0 && maxZ > 0.0 &&
+        minXminYminZ && minXminYmaxZ && maxXminYminZ && maxXminYmaxZ) {
+        // Find any point on the minY plane that intersects the world
+        // First construct a perpendicular plane that will allow us to find a sample point.
+        // This plane is vertical and goes through the points (0,0,0) and (0,1,0)
+        // Then use it to compute a sample point.
+        final GeoPoint intPoint = minYPlane.getSampleIntersectionPoint(planetModel, yVerticalPlane);
+        if (intPoint != null) {
+          minYEdges = new GeoPoint[]{intPoint};
+        } else {
+          minYEdges = EMPTY_POINTS;
+        }
+      } else {
+        minYEdges = EMPTY_POINTS;
+      }
+      
+      final GeoPoint[] maxYEdges;
+      if (maxY - worldMinY >= -Vector.MINIMUM_RESOLUTION && maxY - worldMaxY <= Vector.MINIMUM_RESOLUTION &&
+        minX < 0.0 && maxX > 0.0 && minZ < 0.0 && maxZ > 0.0 &&
+        minXmaxYminZ && minXmaxYmaxZ && maxXmaxYminZ && maxXmaxYmaxZ) {
+        // Find any point on the maxY plane that intersects the world
+        // First construct a perpendicular plane that will allow us to find a sample point.
+        // This plane is vertical and goes through the points (0,0,0) and (0,1,0)
+        // Then use it to compute a sample point.
+        final GeoPoint intPoint = maxYPlane.getSampleIntersectionPoint(planetModel, yVerticalPlane);
+        if (intPoint != null) {
+          maxYEdges = new GeoPoint[]{intPoint};
+        } else {
+          maxYEdges = EMPTY_POINTS;
+        }
+      } else {
+        maxYEdges = EMPTY_POINTS;
+      }
+      
+      final GeoPoint[] minZEdges;
+      if (minZ - worldMinZ >= -Vector.MINIMUM_RESOLUTION && minZ - worldMaxZ <= Vector.MINIMUM_RESOLUTION &&
+        minX < 0.0 && maxX > 0.0 && minY < 0.0 && maxY > 0.0 &&
+        minXminYminZ && minXmaxYminZ && maxXminYminZ && maxXmaxYminZ) {
+        // Find any point on the minZ plane that intersects the world
+        // First construct a perpendicular plane that will allow us to find a sample point.
+        // This plane is vertical and goes through the points (0,0,0) and (1,0,0)
+        // Then use it to compute a sample point.
+        final GeoPoint intPoint = minZPlane.getSampleIntersectionPoint(planetModel, xVerticalPlane);
+        if (intPoint != null) {
+          minZEdges = new GeoPoint[]{intPoint};
+        } else {
+          minZEdges = EMPTY_POINTS;
+        }
+      } else {
+        minZEdges = EMPTY_POINTS;
+      }
+      
+      final GeoPoint[] maxZEdges;
+      if (maxZ - worldMinZ >= -Vector.MINIMUM_RESOLUTION && maxZ - worldMaxZ <= Vector.MINIMUM_RESOLUTION &&
+        minX < 0.0 && maxX > 0.0 && minY < 0.0 && maxY > 0.0 &&
+        minXminYmaxZ && minXmaxYmaxZ && maxXminYmaxZ && maxXmaxYmaxZ) {
+        // Find any point on the maxZ plane that intersects the world
+        // First construct a perpendicular plane that will allow us to find a sample point.
+        // This plane is vertical and goes through the points (0,0,0) and (1,0,0) (that is, its orientation doesn't matter)
+        // Then use it to compute a sample point.
+        final GeoPoint intPoint = maxZPlane.getSampleIntersectionPoint(planetModel, xVerticalPlane);
+        if (intPoint != null) {
+          maxZEdges = new GeoPoint[]{intPoint};
+        } else {
+          maxZEdges = EMPTY_POINTS;
+        }
+      } else {
+        maxZEdges = EMPTY_POINTS;
+      }
+      
+      // Glue everything together.  This is not a minimal set of edgepoints, as of now, but it does completely describe all shapes on the
+      // planet.
+      this.edgePoints = glueTogether(minXminY, minXmaxY, minXminZ, minXmaxZ,
+        maxXminY, maxXmaxY, maxXminZ, maxXmaxZ,
+        minYminZ, minYmaxZ, maxYminZ, maxYmaxZ,
+        minXEdges, maxXEdges, minYEdges, maxYEdges, minZEdges, maxZEdges);
+    }
+  }
+
+  @Override
+  protected GeoPoint[] getEdgePoints() {
+    return edgePoints;
+  }
+  
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    if (isWholeWorld) {
+      return true;
+    }
+    return minXPlane.isWithin(x, y, z) &&
+      maxXPlane.isWithin(x, y, z) &&
+      minYPlane.isWithin(x, y, z) &&
+      maxYPlane.isWithin(x, y, z) &&
+      minZPlane.isWithin(x, y, z) &&
+      maxZPlane.isWithin(x, y, z);
+  }
+
+  @Override
+  public int getRelationship(final GeoShape path) {
+    if (isWholeWorld) {
+      if (path.getEdgePoints().length > 0)
+        return WITHIN;
+      return OVERLAPS;
+    }
+    
+    /*
+    for (GeoPoint p : getEdgePoints()) {
+      System.err.println(" Edge point "+p+" path.isWithin()? "+path.isWithin(p));
+    }
+    
+    for (GeoPoint p : path.getEdgePoints()) {
+      System.err.println(" path edge point "+p+" isWithin()? "+isWithin(p)+" minx="+minXPlane.evaluate(p)+" maxx="+maxXPlane.evaluate(p)+" miny="+minYPlane.evaluate(p)+" maxy="+maxYPlane.evaluate(p)+" minz="+minZPlane.evaluate(p)+" maxz="+maxZPlane.evaluate(p));
+    }
+    */
+    
+    //System.err.println(this+" getrelationship with "+path);
+    final int insideRectangle = isShapeInsideArea(path);
+    if (insideRectangle == SOME_INSIDE) {
+      //System.err.println(" some shape points inside area");
+      return OVERLAPS;
+    }
+
+    // Figure out if the entire XYZArea is contained by the shape.
+    final int insideShape = isAreaInsideShape(path);
+    if (insideShape == SOME_INSIDE) {
+      //System.err.println(" some area points inside shape");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE && insideShape == ALL_INSIDE) {
+      //System.err.println(" inside of each other");
+      return OVERLAPS;
+    }
+
+    if (path.intersects(minXPlane, notableMinXPoints, maxXPlane, minYPlane, maxYPlane, minZPlane, maxZPlane) ||
+        path.intersects(maxXPlane, notableMaxXPoints, minXPlane, minYPlane, maxYPlane, minZPlane, maxZPlane) ||
+        path.intersects(minYPlane, notableMinYPoints, maxYPlane, minXPlane, maxXPlane, minZPlane, maxZPlane) ||
+        path.intersects(maxYPlane, notableMaxYPoints, minYPlane, minXPlane, maxXPlane, minZPlane, maxZPlane) ||
+        path.intersects(minZPlane, notableMinZPoints, maxZPlane, minXPlane, maxXPlane, minYPlane, maxYPlane) ||
+        path.intersects(maxZPlane, notableMaxZPoints, minZPlane, minXPlane, maxXPlane, minYPlane, maxYPlane)) {
+      //System.err.println(" edges intersect");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE) {
+      //System.err.println(" all shape points inside area");
+      return WITHIN;
+    }
+
+    if (insideShape == ALL_INSIDE) {
+      //System.err.println(" all area points inside shape");
+      return CONTAINS;
+    }
+    //System.err.println(" disjoint");
+    return DISJOINT;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof StandardXYZSolid))
+      return false;
+    StandardXYZSolid other = (StandardXYZSolid) o;
+    if (!super.equals(other) ||
+      other.isWholeWorld != isWholeWorld) {
+      return false;
+    }
+    if (!isWholeWorld) {
+      return other.minXPlane.equals(minXPlane) &&
+        other.maxXPlane.equals(maxXPlane) &&
+        other.minYPlane.equals(minYPlane) &&
+        other.maxYPlane.equals(maxYPlane) &&
+        other.minZPlane.equals(minZPlane) &&
+        other.maxZPlane.equals(maxZPlane);
+    }
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + (isWholeWorld?1:0);
+    if (!isWholeWorld) {
+      result = 31 * result  + minXPlane.hashCode();
+      result = 31 * result  + maxXPlane.hashCode();
+      result = 31 * result  + minYPlane.hashCode();
+      result = 31 * result  + maxYPlane.hashCode();
+      result = 31 * result  + minZPlane.hashCode();
+      result = 31 * result  + maxZPlane.hashCode();
+    }
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "StandardXYZSolid: {planetmodel="+planetModel+", isWholeWorld="+isWholeWorld+", minXplane="+minXPlane+", maxXplane="+maxXPlane+", minYplane="+minYPlane+", maxYplane="+maxYPlane+", minZplane="+minZPlane+", maxZplane="+maxZPlane+"}";
+  }
+  
+}
+  

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Tools.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Tools.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Tools.java
new file mode 100755
index 0000000..e8ee29e
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Tools.java
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+/**
+ * Static methods globally useful for 3d geometric work.
+ *
+ * @lucene.experimental
+ */
+public class Tools {
+  private Tools() {
+  }
+
+  /**
+   * Java acos yields a NAN if you take an arc-cos of an
+   * angle that's just a tiny bit greater than 1.0, so
+   * here's a more resilient version.
+   */
+  public static double safeAcos(double value) {
+    if (value > 1.0)
+      value = 1.0;
+    else if (value < -1.0)
+      value = -1.0;
+    return Math.acos(value);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Vector.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Vector.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Vector.java
new file mode 100755
index 0000000..3a1b233
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/Vector.java
@@ -0,0 +1,378 @@
+/*
+ * 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;
+
+/**
+ * A 3d vector in space, not necessarily
+ * going through the origin.
+ *
+ * @lucene.experimental
+ */
+public class Vector {
+  /**
+   * Values that are all considered to be essentially zero have a magnitude
+   * less than this.
+   */
+  public static final double MINIMUM_RESOLUTION = 1.0e-12;
+  /**
+   * For squared quantities, the bound is squared too.
+   */
+  public static final double MINIMUM_RESOLUTION_SQUARED = MINIMUM_RESOLUTION * MINIMUM_RESOLUTION;
+  /**
+   * For cubed quantities, cube the bound.
+   */
+  public static final double MINIMUM_RESOLUTION_CUBED = MINIMUM_RESOLUTION_SQUARED * MINIMUM_RESOLUTION;
+
+  /** The x value */
+  public final double x;
+  /** The y value */
+  public final double y;
+  /** The z value */
+  public final double z;
+
+  /**
+   * Construct from (U.S.) x,y,z coordinates.
+   *@param x is the x value.
+   *@param y is the y value.
+   *@param z is the z value.
+   */
+  public Vector(double x, double y, double z) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+  }
+
+  /**
+   * Construct a vector that is perpendicular to
+   * two other (non-zero) vectors.  If the vectors are parallel,
+   * IllegalArgumentException will be thrown.
+   * Produces a normalized final vector.
+   *
+   * @param A is the first vector
+   * @param B is the second
+   */
+  public Vector(final Vector A, final Vector B) {
+    // x = u2v3 - u3v2
+    // y = u3v1 - u1v3
+    // z = u1v2 - u2v1
+    final double thisX = A.y * B.z - A.z * B.y;
+    final double thisY = A.z * B.x - A.x * B.z;
+    final double thisZ = A.x * B.y - A.y * B.x;
+    final double magnitude = magnitude(thisX, thisY, thisZ);
+    if (Math.abs(magnitude) < MINIMUM_RESOLUTION) {
+      throw new IllegalArgumentException("Degenerate/parallel vector constructed");
+    }
+    final double inverseMagnitude = 1.0 / magnitude;
+    this.x = thisX * inverseMagnitude;
+    this.y = thisY * inverseMagnitude;
+    this.z = thisZ * inverseMagnitude;
+  }
+
+  /** Compute a magnitude of an x,y,z value.
+   */
+  public static double magnitude(final double x, final double y, final double z) {
+    return Math.sqrt(x*x + y*y + z*z);
+  }
+  
+  /**
+   * Compute a normalized unit vector based on the current vector.
+   *
+   * @return the normalized vector, or null if the current vector has
+   * a magnitude of zero.
+   */
+  public Vector normalize() {
+    double denom = magnitude();
+    if (denom < MINIMUM_RESOLUTION)
+      // Degenerate, can't normalize
+      return null;
+    double normFactor = 1.0 / denom;
+    return new Vector(x * normFactor, y * normFactor, z * normFactor);
+  }
+
+  /**
+   * Do a dot product.
+   *
+   * @param v is the vector to multiply.
+   * @return the result.
+   */
+  public double dotProduct(final Vector v) {
+    return this.x * v.x + this.y * v.y + this.z * v.z;
+  }
+
+  /**
+   * Do a dot product.
+   *
+   * @param x is the x value of the vector to multiply.
+   * @param y is the y value of the vector to multiply.
+   * @param z is the z value of the vector to multiply.
+   * @return the result.
+   */
+  public double dotProduct(final double x, final double y, final double z) {
+    return this.x * x + this.y * y + this.z * z;
+  }
+
+  /**
+   * Determine if this vector, taken from the origin,
+   * describes a point within a set of planes.
+   *
+   * @param bounds     is the first part of the set of planes.
+   * @param moreBounds is the second part of the set of planes.
+   * @return true if the point is within the bounds.
+   */
+  public boolean isWithin(final Membership[] bounds, final Membership[] moreBounds) {
+    // Return true if the point described is within all provided bounds
+    //System.err.println("  checking if "+this+" is within bounds");
+    for (Membership bound : bounds) {
+      if (bound != null && !bound.isWithin(this)) {
+        //System.err.println("    NOT within "+bound);
+        return false;
+      }
+    }
+    for (Membership bound : moreBounds) {
+      if (bound != null && !bound.isWithin(this)) {
+        //System.err.println("    NOT within "+bound);
+        return false;
+      }
+    }
+    //System.err.println("    is within");
+    return true;
+  }
+
+  /**
+   * Translate vector.
+   */
+  public Vector translate(final double xOffset, final double yOffset, final double zOffset) {
+    return new Vector(x - xOffset, y - yOffset, z - zOffset);
+  }
+
+  /**
+   * Rotate vector counter-clockwise in x-y by an angle.
+   */
+  public Vector rotateXY(final double angle) {
+    return rotateXY(Math.sin(angle), Math.cos(angle));
+  }
+
+  /**
+   * Rotate vector counter-clockwise in x-y by an angle, expressed as sin and cos.
+   */
+  public Vector rotateXY(final double sinAngle, final double cosAngle) {
+    return new Vector(x * cosAngle - y * sinAngle, x * sinAngle + y * cosAngle, z);
+  }
+
+  /**
+   * Rotate vector counter-clockwise in x-z by an angle.
+   */
+  public Vector rotateXZ(final double angle) {
+    return rotateXZ(Math.sin(angle), Math.cos(angle));
+  }
+
+  /**
+   * Rotate vector counter-clockwise in x-z by an angle, expressed as sin and cos.
+   */
+  public Vector rotateXZ(final double sinAngle, final double cosAngle) {
+    return new Vector(x * cosAngle - z * sinAngle, y, x * sinAngle + z * cosAngle);
+  }
+
+  /**
+   * Rotate vector counter-clockwise in z-y by an angle.
+   */
+  public Vector rotateZY(final double angle) {
+    return rotateZY(Math.sin(angle), Math.cos(angle));
+  }
+
+  /**
+   * Rotate vector counter-clockwise in z-y by an angle, expressed as sin and cos.
+   */
+  public Vector rotateZY(final double sinAngle, final double cosAngle) {
+    return new Vector(x, z * sinAngle + y * cosAngle, z * cosAngle - y * sinAngle);
+  }
+
+  /**
+   * Compute the square of a straight-line distance to a point described by the
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI.
+   *
+   * @param v is the vector to compute a distance to.
+   * @return the square of the linear distance.
+   */
+  public double linearDistanceSquared(final Vector v) {
+    double deltaX = this.x - v.x;
+    double deltaY = this.y - v.y;
+    double deltaZ = this.z - v.z;
+    return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
+  }
+
+  /**
+   * Compute the square of a straight-line distance to a point described by the
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI.
+   *
+   * @param x is the x part of the vector to compute a distance to.
+   * @param y is the y part of the vector to compute a distance to.
+   * @param z is the z part of the vector to compute a distance to.
+   * @return the square of the linear distance.
+   */
+  public double linearDistanceSquared(final double x, final double y, final double z) {
+    double deltaX = this.x - x;
+    double deltaY = this.y - y;
+    double deltaZ = this.z - z;
+    return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
+  }
+
+  /**
+   * Compute the straight-line distance to a point described by the
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI.
+   *
+   * @param v is the vector to compute a distance to.
+   * @return the linear distance.
+   */
+  public double linearDistance(final Vector v) {
+    return Math.sqrt(linearDistanceSquared(v));
+  }
+
+  /**
+   * Compute the straight-line distance to a point described by the
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI.
+   *
+   * @param x is the x part of the vector to compute a distance to.
+   * @param y is the y part of the vector to compute a distance to.
+   * @param z is the z part of the vector to compute a distance to.
+   * @return the linear distance.
+   */
+  public double linearDistance(final double x, final double y, final double z) {
+    return Math.sqrt(linearDistanceSquared(x, y, z));
+  }
+
+  /**
+   * Compute the square of the normal distance to a vector described by a
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI/2.
+   *
+   * @param v is the vector to compute a distance to.
+   * @return the square of the normal distance.
+   */
+  public double normalDistanceSquared(final Vector v) {
+    double t = dotProduct(v);
+    double deltaX = this.x * t - v.x;
+    double deltaY = this.y * t - v.y;
+    double deltaZ = this.z * t - v.z;
+    return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
+  }
+
+  /**
+   * Compute the square of the normal distance to a vector described by a
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI/2.
+   *
+   * @param x is the x part of the vector to compute a distance to.
+   * @param y is the y part of the vector to compute a distance to.
+   * @param z is the z part of the vector to compute a distance to.
+   * @return the square of the normal distance.
+   */
+  public double normalDistanceSquared(final double x, final double y, final double z) {
+    double t = dotProduct(x, y, z);
+    double deltaX = this.x * t - x;
+    double deltaY = this.y * t - y;
+    double deltaZ = this.z * t - z;
+    return deltaX * deltaX + deltaY * deltaY + deltaZ * deltaZ;
+  }
+
+  /**
+   * Compute the normal (perpendicular) distance to a vector described by a
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI/2.
+   *
+   * @param v is the vector to compute a distance to.
+   * @return the normal distance.
+   */
+  public double normalDistance(final Vector v) {
+    return Math.sqrt(normalDistanceSquared(v));
+  }
+
+  /**
+   * Compute the normal (perpendicular) distance to a vector described by a
+   * vector taken from the origin.
+   * Monotonically increasing for arc distances up to PI/2.
+   *
+   * @param x is the x part of the vector to compute a distance to.
+   * @param y is the y part of the vector to compute a distance to.
+   * @param z is the z part of the vector to compute a distance to.
+   * @return the normal distance.
+   */
+  public double normalDistance(final double x, final double y, final double z) {
+    return Math.sqrt(normalDistanceSquared(x, y, z));
+  }
+
+  /**
+   * Compute the magnitude of this vector.
+   *
+   * @return the magnitude.
+   */
+  public double magnitude() {
+    return magnitude(x,y,z);
+  }
+
+  /** Compute the desired magnitude of a unit vector projected to a given
+   * planet model.
+   * @param planetModel is the planet model.
+   * @param x is the unit vector x value.
+   * @param y is the unit vector y value.
+   * @param z is the unit vector z value.
+   * @return a magnitude value for that (x,y,z) that projects the vector onto the specified ellipsoid.
+   */
+  protected static double computeDesiredEllipsoidMagnitude(final PlanetModel planetModel, final double x, final double y, final double z) {
+    return 1.0 / Math.sqrt(x*x*planetModel.inverseAbSquared + y*y*planetModel.inverseAbSquared + z*z*planetModel.inverseCSquared);
+  }
+
+  /** Compute the desired magnitude of a unit vector projected to a given
+   * planet model.  The unit vector is specified only by a z value.
+   * @param planetModel is the planet model.
+   * @param z is the unit vector z value.
+   * @return a magnitude value for that z value that projects the vector onto the specified ellipsoid.
+   */
+  protected static double computeDesiredEllipsoidMagnitude(final PlanetModel planetModel, final double z) {
+    return 1.0 / Math.sqrt((1.0-z*z)*planetModel.inverseAbSquared + z*z*planetModel.inverseCSquared);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof Vector))
+      return false;
+    Vector other = (Vector) o;
+    return (other.x == x && other.y == y && other.z == z);
+  }
+
+  @Override
+  public int hashCode() {
+    int result;
+    long temp;
+    temp = Double.doubleToLongBits(x);
+    result = (int) (temp ^ (temp >>> 32));
+    temp = Double.doubleToLongBits(y);
+    result = 31 * result + (int) (temp ^ (temp >>> 32));
+    temp = Double.doubleToLongBits(z);
+    result = 31 * result + (int) (temp ^ (temp >>> 32));
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "[X=" + x + ", Y=" + y + ", Z=" + z + "]";
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZBounds.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZBounds.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZBounds.java
new file mode 100644
index 0000000..c3ee53d
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZBounds.java
@@ -0,0 +1,267 @@
+/*
+ * 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 XYZ bounds information.
+ *
+ * @lucene.experimental
+ */
+public class XYZBounds implements Bounds {
+
+  /** A 'fudge factor', which is added to maximums and subtracted from minimums,
+   * in order to compensate for potential error deltas.  This would not be necessary
+   * except that our 'bounds' is defined as always equaling or exceeding the boundary
+   * of the shape, and we cannot guarantee that without making MINIMUM_RESOLUTION
+   * unacceptably large.
+   */
+  protected static final double FUDGE_FACTOR = Vector.MINIMUM_RESOLUTION * 2.0;
+  
+  /** Minimum x */
+  protected Double minX = null;
+  /** Maximum x */
+  protected Double maxX = null;
+  /** Minimum y */
+  protected Double minY = null;
+  /** Maximum y */
+  protected Double maxY = null;
+  /** Minimum z */
+  protected Double minZ = null;
+  /** Maximum z */
+  protected Double maxZ = null;
+  
+  /** 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;
+
+  /** Construct an empty bounds object */
+  public XYZBounds() {
+  }
+
+  // Accessor methods
+  
+  /** Return the minimum X value.
+   *@return minimum X value.
+   */
+  public Double getMinimumX() {
+    return minX;
+  }
+
+  /** Return the maximum X value.
+   *@return maximum X value.
+   */
+  public Double getMaximumX() {
+    return maxX;
+  }
+
+  /** Return the minimum Y value.
+   *@return minimum Y value.
+   */
+  public Double getMinimumY() {
+    return minY;
+  }
+
+  /** Return the maximum Y value.
+   *@return maximum Y value.
+   */
+  public Double getMaximumY() {
+    return maxY;
+  }
+  
+  /** Return the minimum Z value.
+   *@return minimum Z value.
+   */
+  public Double getMinimumZ() {
+    return minZ;
+  }
+
+  /** Return the maximum Z value.
+   *@return maximum Z value.
+   */
+  public Double getMaximumZ() {
+    return maxZ;
+  }
+
+  /** Return true if minX is as small as the planet model allows.
+   *@return true if minX has reached its bound.
+   */
+  public boolean isSmallestMinX(final PlanetModel planetModel) {
+    if (minX == null)
+      return false;
+    return minX - planetModel.getMinimumXValue() < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if maxX is as large as the planet model allows.
+   *@return true if maxX has reached its bound.
+   */
+  public boolean isLargestMaxX(final PlanetModel planetModel) {
+    if (maxX == null)
+      return false;
+    return planetModel.getMaximumXValue() - maxX < Vector.MINIMUM_RESOLUTION;
+  }
+
+  /** Return true if minY is as small as the planet model allows.
+   *@return true if minY has reached its bound.
+   */
+  public boolean isSmallestMinY(final PlanetModel planetModel) {
+    if (minY == null)
+      return false;
+    return minY - planetModel.getMinimumYValue() < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if maxY is as large as the planet model allows.
+   *@return true if maxY has reached its bound.
+   */
+  public boolean isLargestMaxY(final PlanetModel planetModel) {
+    if (maxY == null)
+      return false;
+    return planetModel.getMaximumYValue() - maxY < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if minZ is as small as the planet model allows.
+   *@return true if minZ has reached its bound.
+   */
+  public boolean isSmallestMinZ(final PlanetModel planetModel) {
+    if (minZ == null)
+      return false;
+    return minZ - planetModel.getMinimumZValue() < Vector.MINIMUM_RESOLUTION;
+  }
+  
+  /** Return true if maxZ is as large as the planet model allows.
+   *@return true if maxZ has reached its bound.
+   */
+  public boolean isLargestMaxZ(final PlanetModel planetModel) {
+    if (maxZ == null)
+      return false;
+    return planetModel.getMaximumZValue() - maxZ < Vector.MINIMUM_RESOLUTION;
+  }
+
+  // Modification methods
+  
+  @Override
+  public Bounds addPlane(final PlanetModel planetModel, final Plane plane, final Membership... bounds) {
+    plane.recordBounds(planetModel, this, bounds);
+    return this;
+  }
+
+  /** Add a horizontal plane to the bounds description.
+   * This method should EITHER use the supplied latitude, OR use the supplied
+   * plane, depending on what is most efficient.
+   *@param planetModel is the planet model.
+   *@param latitude is the latitude.
+   *@param horizontalPlane is the plane.
+   *@param bounds are the constraints on the plane.
+   *@return updated Bounds object.
+   */
+  public Bounds addHorizontalPlane(final PlanetModel planetModel,
+    final double latitude,
+    final Plane horizontalPlane,
+    final Membership... bounds) {
+    return addPlane(planetModel, horizontalPlane, bounds);
+  }
+    
+  /** Add a vertical plane to the bounds description.
+   * This method should EITHER use the supplied longitude, OR use the supplied
+   * plane, depending on what is most efficient.
+   *@param planetModel is the planet model.
+   *@param longitude is the longitude.
+   *@param verticalPlane is the plane.
+   *@param bounds are the constraints on the plane.
+   *@return updated Bounds object.
+   */
+  public Bounds addVerticalPlane(final PlanetModel planetModel,
+    final double longitude,
+    final Plane verticalPlane,
+    final Membership... bounds) {
+    return addPlane(planetModel, verticalPlane, bounds);
+  }
+
+  @Override
+  public Bounds addXValue(final GeoPoint point) {
+    final double x = point.x;
+    final double small = x - FUDGE_FACTOR;
+    if (minX == null || minX > small) {
+      minX = new Double(small);
+    }
+    final double large = x + FUDGE_FACTOR;
+    if (maxX == null || maxX < large) {
+      maxX = new Double(large);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addYValue(final GeoPoint point) {
+    final double y = point.y;
+    final double small = y - FUDGE_FACTOR;
+    if (minY == null || minY > small) {
+      minY = new Double(small);
+    }
+    final double large = y + FUDGE_FACTOR;
+    if (maxY == null || maxY < large) {
+      maxY = new Double(large);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addZValue(final GeoPoint point) {
+    final double z = point.z;
+    final double small = z - FUDGE_FACTOR;
+    if (minZ == null || minZ > small) {
+      minZ = new Double(small);
+    }
+    final double large = z + FUDGE_FACTOR;
+    if (maxZ == null || maxZ < large) {
+      maxZ = new Double(large);
+    }
+    return this;
+  }
+
+  @Override
+  public Bounds addPoint(final GeoPoint point) {
+    return addXValue(point).addYValue(point).addZValue(point);
+  }
+
+  @Override
+  public Bounds isWide() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+  @Override
+  public Bounds noLongitudeBound() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+  @Override
+  public Bounds noTopLatitudeBound() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+  @Override
+  public Bounds noBottomLatitudeBound() {
+    // No specific thing we need to do.
+    return this;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolid.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolid.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolid.java
new file mode 100644
index 0000000..9298079
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolid.java
@@ -0,0 +1,26 @@
+/*
+ * 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;
+
+/**
+ * Interface for a family of 3D rectangles, bounded on six sides by X,Y,Z limits
+ *
+ * @lucene.internal
+ */
+public interface XYZSolid extends GeoArea {
+}
+  

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolidFactory.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolidFactory.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolidFactory.java
new file mode 100644
index 0000000..25ea400
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYZSolidFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+/**
+ * Factory for {@link XYZSolid}.
+ *
+ * @lucene.experimental
+ */
+public class XYZSolidFactory {
+  private XYZSolidFactory() {
+  }
+
+  /**
+   * Create a XYZSolid of the right kind given (x,y,z) bounds.
+   * @param planetModel is the planet model
+   * @param minX is the min X boundary
+   * @param maxX is the max X boundary
+   * @param minY is the min Y boundary
+   * @param maxY is the max Y boundary
+   * @param minZ is the min Z boundary
+   * @param maxZ is the max Z boundary
+   */
+  public static XYZSolid makeXYZSolid(final PlanetModel planetModel, final double minX, final double maxX, final double minY, final double maxY, final double minZ, final double maxZ) {
+    if (Math.abs(maxX - minX) < Vector.MINIMUM_RESOLUTION) {
+      if (Math.abs(maxY - minY) < Vector.MINIMUM_RESOLUTION) {
+        if (Math.abs(maxZ - minZ) < Vector.MINIMUM_RESOLUTION) {
+          return new dXdYdZSolid(planetModel, (minX+maxX) * 0.5, (minY+maxY) * 0.5, minZ);
+        } else {
+          return new dXdYZSolid(planetModel, (minX+maxX) * 0.5, (minY+maxY) * 0.5, minZ, maxZ);
+        }
+      } else {
+        if (Math.abs(maxZ - minZ) < Vector.MINIMUM_RESOLUTION) {
+          return new dXYdZSolid(planetModel, (minX+maxX) * 0.5, minY, maxY, (minZ+maxZ) * 0.5);
+        } else {
+          return new dXYZSolid(planetModel, (minX+maxX) * 0.5, minY, maxY, minZ, maxZ);
+        }
+      }
+    }
+    if (Math.abs(maxY - minY) < Vector.MINIMUM_RESOLUTION) {
+      if (Math.abs(maxZ - minZ) < Vector.MINIMUM_RESOLUTION) {
+        return new XdYdZSolid(planetModel, minX, maxX, (minY+maxY) * 0.5, (minZ+maxZ) * 0.5);
+      } else {
+        return new XdYZSolid(planetModel, minX, maxX, (minY+maxY) * 0.5, minZ, maxZ);
+      }
+    }
+    if (Math.abs(maxZ - minZ) < Vector.MINIMUM_RESOLUTION) {
+      return new XYdZSolid(planetModel, minX, maxX, minY, maxY, (minZ+maxZ) * 0.5);
+    }
+    return new StandardXYZSolid(planetModel, minX, maxX, minY, maxY, minZ, maxZ);
+  }
+  
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYdZSolid.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYdZSolid.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYdZSolid.java
new file mode 100644
index 0000000..66aac84
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XYdZSolid.java
@@ -0,0 +1,213 @@
+/*
+ * 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;
+
+/**
+ * 3D rectangle, bounded on six sides by X,Y,Z limits, degenerate in Z
+ *
+ * @lucene.internal
+ */
+public class XYdZSolid extends BaseXYZSolid {
+
+  /** Min-X plane */
+  protected final SidedPlane minXPlane;
+  /** Max-X plane */
+  protected final SidedPlane maxXPlane;
+  /** Min-Y plane */
+  protected final SidedPlane minYPlane;
+  /** Max-Y plane */
+  protected final SidedPlane maxYPlane;
+  /** Z plane */
+  protected final Plane zPlane;
+  
+  /** These are the edge points of the shape, which are defined to be at least one point on
+   * each surface area boundary.  In the case of a solid, this includes points which represent
+   * the intersection of XYZ bounding planes and the planet, as well as points representing
+   * the intersection of single bounding planes with the planet itself.
+   */
+  protected final GeoPoint[] edgePoints;
+
+  /** Notable points for ZPlane */
+  protected final GeoPoint[] notableZPoints;
+
+  /**
+   * Sole constructor
+   *
+   *@param planetModel is the planet model.
+   *@param minX is the minimum X value.
+   *@param maxX is the maximum X value.
+   *@param minY is the minimum Y value.
+   *@param maxY is the maximum Y value.
+   *@param Z is the Z value.
+   */
+  public XYdZSolid(final PlanetModel planetModel,
+    final double minX,
+    final double maxX,
+    final double minY,
+    final double maxY,
+    final double Z) {
+    super(planetModel);
+    // Argument checking
+    if (maxX - minX < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("X values in wrong order or identical");
+    if (maxY - minY < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("Y values in wrong order or identical");
+
+    final double worldMinZ = planetModel.getMinimumZValue();
+    final double worldMaxZ = planetModel.getMaximumZValue();
+    
+    // Construct the planes
+    minXPlane = new SidedPlane(maxX,0.0,0.0,xUnitVector,-minX);
+    maxXPlane = new SidedPlane(minX,0.0,0.0,xUnitVector,-maxX);
+    minYPlane = new SidedPlane(0.0,maxY,0.0,yUnitVector,-minY);
+    maxYPlane = new SidedPlane(0.0,minY,0.0,yUnitVector,-maxY);
+    zPlane = new Plane(zUnitVector,-Z);
+      
+    // We need at least one point on the planet surface for each manifestation of the shape.
+    // There can be up to 2 (on opposite sides of the world).  But we have to go through
+    // 4 combinations of adjacent planes in order to find out if any have 2 intersection solution.
+    // Typically, this requires 4 square root operations. 
+    final GeoPoint[] minXZ = minXPlane.findIntersections(planetModel,zPlane,maxXPlane,minYPlane,maxYPlane);
+    final GeoPoint[] maxXZ = maxXPlane.findIntersections(planetModel,zPlane,minXPlane,minYPlane,maxYPlane);
+    final GeoPoint[] minYZ = minYPlane.findIntersections(planetModel,zPlane,maxYPlane,minXPlane,maxXPlane);
+    final GeoPoint[] maxYZ = maxYPlane.findIntersections(planetModel,zPlane,minYPlane,minXPlane,maxXPlane);
+      
+    notableZPoints = glueTogether(minXZ, maxXZ, minYZ, maxYZ);
+
+    // Now, compute the edge points.
+    // This is the trickiest part of setting up an XYZSolid.  We've computed intersections already, so
+    // we'll start there.  We know that at most there will be two disconnected shapes on the planet surface.
+    // But there's also a case where exactly one plane slices through the world, and none of the bounding plane
+    // intersections do.  Thus, if we don't find any of the edge intersection cases, we have to look for that last case.
+      
+    // If we still haven't encountered anything, we need to look at single-plane/world intersections.
+    // We detect these by looking at the world model and noting its x, y, and z bounds.
+    // The cases we are looking for are when the four corner points for any given
+    // plane are all outside of the world, AND that plane intersects the world.
+    // There are four corner points all told; we must evaluate these WRT the planet surface.
+    final boolean minXminYZ = planetModel.pointOutside(minX, minY, Z);
+    final boolean minXmaxYZ = planetModel.pointOutside(minX, maxY, Z);
+    final boolean maxXminYZ = planetModel.pointOutside(maxX, minY, Z);
+    final boolean maxXmaxYZ = planetModel.pointOutside(maxX, maxY, Z);
+
+    final GeoPoint[] zEdges;
+    if (Z - worldMinZ >= -Vector.MINIMUM_RESOLUTION && Z - worldMaxZ <= Vector.MINIMUM_RESOLUTION &&
+      minX < 0.0 && maxX > 0.0 && minY < 0.0 && maxY > 0.0 &&
+      minXminYZ && minXmaxYZ && maxXminYZ && maxXmaxYZ) {
+      // Find any point on the minZ plane that intersects the world
+      // First construct a perpendicular plane that will allow us to find a sample point.
+      // This plane is vertical and goes through the points (0,0,0) and (1,0,0)
+      // Then use it to compute a sample point.
+      final GeoPoint intPoint = zPlane.getSampleIntersectionPoint(planetModel, xVerticalPlane);
+      if (intPoint != null) {
+        zEdges = new GeoPoint[]{intPoint};
+      } else {
+        zEdges = EMPTY_POINTS;
+      }
+    } else {
+      zEdges= EMPTY_POINTS;
+    }
+
+    this.edgePoints = glueTogether(minXZ, maxXZ, minYZ, maxYZ, zEdges);
+  }
+
+  @Override
+  protected GeoPoint[] getEdgePoints() {
+    return edgePoints;
+  }
+  
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    return minXPlane.isWithin(x, y, z) &&
+      maxXPlane.isWithin(x, y, z) &&
+      minYPlane.isWithin(x, y, z) &&
+      maxYPlane.isWithin(x, y, z) &&
+      zPlane.evaluateIsZero(x, y, z);
+  }
+
+  @Override
+  public int getRelationship(final GeoShape path) {
+    
+    //System.err.println(this+" getrelationship with "+path);
+    final int insideRectangle = isShapeInsideArea(path);
+    if (insideRectangle == SOME_INSIDE) {
+      //System.err.println(" some inside");
+      return OVERLAPS;
+    }
+
+    // Figure out if the entire XYZArea is contained by the shape.
+    final int insideShape = isAreaInsideShape(path);
+    if (insideShape == SOME_INSIDE) {
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE && insideShape == ALL_INSIDE) {
+      //System.err.println(" inside of each other");
+      return OVERLAPS;
+    }
+
+    if (path.intersects(zPlane, notableZPoints, minXPlane, maxXPlane, minYPlane, maxYPlane)) {
+      //System.err.println(" edges intersect");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE) {
+      //System.err.println(" shape inside rectangle");
+      return WITHIN;
+    }
+
+    if (insideShape == ALL_INSIDE) {
+      //System.err.println(" shape contains rectangle");
+      return CONTAINS;
+    }
+    //System.err.println(" disjoint");
+    return DISJOINT;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof XYdZSolid))
+      return false;
+    XYdZSolid other = (XYdZSolid) o;
+    if (!super.equals(other)) {
+      return false;
+    }
+    return other.minXPlane.equals(minXPlane) &&
+      other.maxXPlane.equals(maxXPlane) &&
+      other.minYPlane.equals(minYPlane) &&
+      other.maxYPlane.equals(maxYPlane) &&
+      other.zPlane.equals(zPlane);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result  + minXPlane.hashCode();
+    result = 31 * result  + maxXPlane.hashCode();
+    result = 31 * result  + minYPlane.hashCode();
+    result = 31 * result  + maxYPlane.hashCode();
+    result = 31 * result  + zPlane.hashCode();
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "XYdZSolid: {planetmodel="+planetModel+", minXplane="+minXPlane+", maxXplane="+maxXPlane+", minYplane="+minYPlane+", maxYplane="+maxYPlane+", zplane="+zPlane+"}";
+  }
+  
+}
+  

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYZSolid.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYZSolid.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYZSolid.java
new file mode 100644
index 0000000..d9e11b8
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYZSolid.java
@@ -0,0 +1,212 @@
+/*
+ * 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;
+
+/**
+ * 3D rectangle, bounded on six sides by X,Y,Z limits, degenerate in Y
+ *
+ * @lucene.internal
+ */
+public class XdYZSolid extends BaseXYZSolid {
+
+  /** Min-X plane */
+  protected final SidedPlane minXPlane;
+  /** Max-X plane */
+  protected final SidedPlane maxXPlane;
+  /** Y plane */
+  protected final Plane yPlane;
+  /** Min-Z plane */
+  protected final SidedPlane minZPlane;
+  /** Max-Z plane */
+  protected final SidedPlane maxZPlane;
+  
+  /** These are the edge points of the shape, which are defined to be at least one point on
+   * each surface area boundary.  In the case of a solid, this includes points which represent
+   * the intersection of XYZ bounding planes and the planet, as well as points representing
+   * the intersection of single bounding planes with the planet itself.
+   */
+  protected final GeoPoint[] edgePoints;
+
+  /** Notable points for YPlane */
+  protected final GeoPoint[] notableYPoints;
+
+  /**
+   * Sole constructor
+   *
+   *@param planetModel is the planet model.
+   *@param minX is the minimum X value.
+   *@param maxX is the maximum X value.
+   *@param Y is the Y value.
+   *@param minZ is the minimum Z value.
+   *@param maxZ is the maximum Z value.
+   */
+  public XdYZSolid(final PlanetModel planetModel,
+    final double minX,
+    final double maxX,
+    final double Y,
+    final double minZ,
+    final double maxZ) {
+    super(planetModel);
+    // Argument checking
+    if (maxX - minX < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("X values in wrong order or identical");
+    if (maxZ - minZ < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("Z values in wrong order or identical");
+
+    final double worldMinY = planetModel.getMinimumYValue();
+    final double worldMaxY = planetModel.getMaximumYValue();
+    
+    // Construct the planes
+    minXPlane = new SidedPlane(maxX,0.0,0.0,xUnitVector,-minX);
+    maxXPlane = new SidedPlane(minX,0.0,0.0,xUnitVector,-maxX);
+    yPlane = new Plane(yUnitVector,-Y);
+    minZPlane = new SidedPlane(0.0,0.0,maxZ,zUnitVector,-minZ);
+    maxZPlane = new SidedPlane(0.0,0.0,minZ,zUnitVector,-maxZ);
+      
+    // We need at least one point on the planet surface for each manifestation of the shape.
+    // There can be up to 2 (on opposite sides of the world).  But we have to go through
+    // 4 combinations of adjacent planes in order to find out if any have 2 intersection solution.
+    // Typically, this requires 4 square root operations. 
+    final GeoPoint[] minXY = minXPlane.findIntersections(planetModel,yPlane,maxXPlane,minZPlane,maxZPlane);
+    final GeoPoint[] maxXY = maxXPlane.findIntersections(planetModel,yPlane,minXPlane,minZPlane,maxZPlane);
+    final GeoPoint[] YminZ = yPlane.findIntersections(planetModel,minZPlane,maxZPlane,minXPlane,maxXPlane);
+    final GeoPoint[] YmaxZ = yPlane.findIntersections(planetModel,maxZPlane,minZPlane,minXPlane,maxXPlane);
+      
+    notableYPoints = glueTogether(minXY, maxXY, YminZ, YmaxZ);
+
+    // Now, compute the edge points.
+    // This is the trickiest part of setting up an XYZSolid.  We've computed intersections already, so
+    // we'll start there.  We know that at most there will be two disconnected shapes on the planet surface.
+    // But there's also a case where exactly one plane slices through the world, and none of the bounding plane
+    // intersections do.  Thus, if we don't find any of the edge intersection cases, we have to look for that last case.
+      
+    // We need to look at single-plane/world intersections.
+    // We detect these by looking at the world model and noting its x, y, and z bounds.
+    // The cases we are looking for are when the four corner points for any given
+    // plane are all outside of the world, AND that plane intersects the world.
+    // There are four corner points all told; we must evaluate these WRT the planet surface.
+    final boolean minXYminZ = planetModel.pointOutside(minX, Y, minZ);
+    final boolean minXYmaxZ = planetModel.pointOutside(minX, Y, maxZ);
+    final boolean maxXYminZ = planetModel.pointOutside(maxX, Y, minZ);
+    final boolean maxXYmaxZ = planetModel.pointOutside(maxX, Y, maxZ);
+
+    final GeoPoint[] yEdges;
+    if (Y - worldMinY >= -Vector.MINIMUM_RESOLUTION && Y - worldMaxY <= Vector.MINIMUM_RESOLUTION &&
+      minX < 0.0 && maxX > 0.0 && minZ < 0.0 && maxZ > 0.0 &&
+      minXYminZ && minXYmaxZ && maxXYminZ && maxXYmaxZ) {
+      // Find any point on the minY plane that intersects the world
+      // First construct a perpendicular plane that will allow us to find a sample point.
+      // This plane is vertical and goes through the points (0,0,0) and (0,1,0)
+      // Then use it to compute a sample point.
+      final GeoPoint intPoint = yPlane.getSampleIntersectionPoint(planetModel, yVerticalPlane);
+      if (intPoint != null) {
+        yEdges = new GeoPoint[]{intPoint};
+      } else {
+        yEdges = EMPTY_POINTS;
+      }
+    } else {
+      yEdges = EMPTY_POINTS;
+    }
+
+    this.edgePoints = glueTogether(minXY, maxXY, YminZ, YmaxZ, yEdges);
+  }
+
+  @Override
+  protected GeoPoint[] getEdgePoints() {
+    return edgePoints;
+  }
+  
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    return minXPlane.isWithin(x, y, z) &&
+      maxXPlane.isWithin(x, y, z) &&
+      yPlane.evaluateIsZero(x, y, z) &&
+      minZPlane.isWithin(x, y, z) &&
+      maxZPlane.isWithin(x, y, z);
+  }
+
+  @Override
+  public int getRelationship(final GeoShape path) {
+    //System.err.println(this+" getrelationship with "+path);
+    final int insideRectangle = isShapeInsideArea(path);
+    if (insideRectangle == SOME_INSIDE) {
+      //System.err.println(" some inside");
+      return OVERLAPS;
+    }
+
+    // Figure out if the entire XYZArea is contained by the shape.
+    final int insideShape = isAreaInsideShape(path);
+    if (insideShape == SOME_INSIDE) {
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE && insideShape == ALL_INSIDE) {
+      //System.err.println(" inside of each other");
+      return OVERLAPS;
+    }
+
+    if (path.intersects(yPlane, notableYPoints, minXPlane, maxXPlane, minZPlane, maxZPlane)) {
+      //System.err.println(" edges intersect");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE) {
+      //System.err.println(" shape inside rectangle");
+      return WITHIN;
+    }
+
+    if (insideShape == ALL_INSIDE) {
+      //System.err.println(" shape contains rectangle");
+      return CONTAINS;
+    }
+    //System.err.println(" disjoint");
+    return DISJOINT;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof XdYZSolid))
+      return false;
+    XdYZSolid other = (XdYZSolid) o;
+    if (!super.equals(other)) {
+      return false;
+    }
+    return other.minXPlane.equals(minXPlane) &&
+      other.maxXPlane.equals(maxXPlane) &&
+      other.yPlane.equals(yPlane) &&
+      other.minZPlane.equals(minZPlane) &&
+      other.maxZPlane.equals(maxZPlane);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result  + minXPlane.hashCode();
+    result = 31 * result  + maxXPlane.hashCode();
+    result = 31 * result  + yPlane.hashCode();
+    result = 31 * result  + minZPlane.hashCode();
+    result = 31 * result  + maxZPlane.hashCode();
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "XdYZSolid: {planetmodel="+planetModel+", minXplane="+minXPlane+", maxXplane="+maxXPlane+", yplane="+yPlane+", minZplane="+minZPlane+", maxZplane="+maxZPlane+"}";
+  }
+  
+}
+  

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYdZSolid.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYdZSolid.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYdZSolid.java
new file mode 100644
index 0000000..33d0bea
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/XdYdZSolid.java
@@ -0,0 +1,138 @@
+/*
+ * 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;
+
+/**
+ * 3D rectangle, bounded on six sides by X,Y,Z limits, degenerate in Y and Z.
+ * This figure, in fact, represents either zero, one, or two points, so the
+ * actual data stored is minimal.
+ *
+ * @lucene.internal
+ */
+public class XdYdZSolid extends BaseXYZSolid {
+
+  /** The points in this figure on the planet surface; also doubles for edge points */
+  protected final GeoPoint[] surfacePoints;
+  
+  /**
+   * Sole constructor
+   *
+   *@param planetModel is the planet model.
+   *@param minX is the minimum X value.
+   *@param maxX is the maximum X value.
+   *@param Y is the Y value.
+   *@param Z is the Z value.
+   */
+  public XdYdZSolid(final PlanetModel planetModel,
+    final double minX,
+    final double maxX,
+    final double Y,
+    final double Z) {
+    super(planetModel);
+    // Argument checking
+    if (maxX - minX < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("X values in wrong order or identical");
+
+    // Build the planes and intersect them.
+    final Plane yPlane = new Plane(yUnitVector,-Y);
+    final Plane zPlane = new Plane(zUnitVector,-Z);
+    final SidedPlane minXPlane = new SidedPlane(maxX,0.0,0.0,xUnitVector,-minX);
+    final SidedPlane maxXPlane = new SidedPlane(minX,0.0,0.0,xUnitVector,-maxX);
+    surfacePoints = yPlane.findIntersections(planetModel,zPlane,minXPlane,maxXPlane);
+  }
+
+  @Override
+  protected GeoPoint[] getEdgePoints() {
+    return surfacePoints;
+  }
+  
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    for (final GeoPoint p : surfacePoints) {
+      if (p.isIdentical(x,y,z))
+        return true;
+    }
+    return false;
+  }
+
+  @Override
+  public int getRelationship(final GeoShape path) {
+    //System.err.println(this+" getrelationship with "+path);
+    final int insideRectangle = isShapeInsideArea(path);
+    if (insideRectangle == SOME_INSIDE) {
+      //System.err.println(" some inside");
+      return OVERLAPS;
+    }
+
+    // Figure out if the entire XYZArea is contained by the shape.
+    final int insideShape = isAreaInsideShape(path);
+    if (insideShape == SOME_INSIDE) {
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE && insideShape == ALL_INSIDE) {
+      //System.err.println(" inside of each other");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE) {
+      return WITHIN;
+    }
+    
+    if (insideShape == ALL_INSIDE) {
+      //System.err.println(" shape contains rectangle");
+      return CONTAINS;
+    }
+    //System.err.println(" disjoint");
+    return DISJOINT;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof XdYdZSolid))
+      return false;
+    XdYdZSolid other = (XdYdZSolid) o;
+    if (!super.equals(other) || surfacePoints.length != other.surfacePoints.length ) {
+      return false;
+    }
+    for (int i = 0; i < surfacePoints.length; i++) {
+      if (!surfacePoints[i].equals(other.surfacePoints[i]))
+        return false;
+    }
+    return true;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    for (final GeoPoint p : surfacePoints) {
+      result = 31 * result  + p.hashCode();
+    }
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    final StringBuilder sb = new StringBuilder();
+    for (final GeoPoint p : surfacePoints) {
+      sb.append(" ").append(p).append(" ");
+    }
+    return "XdYdZSolid: {planetmodel="+planetModel+", "+sb.toString()+"}";
+  }
+  
+}
+  

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/3a31a8c7/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/dXYZSolid.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/dXYZSolid.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/dXYZSolid.java
new file mode 100644
index 0000000..48fe714
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/dXYZSolid.java
@@ -0,0 +1,216 @@
+/*
+ * 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;
+
+/**
+ * 3D rectangle, bounded on six sides by X,Y,Z limits, degenerate in X.
+ *
+ * @lucene.internal
+ */
+public class dXYZSolid extends BaseXYZSolid {
+
+  /** X plane */
+  protected final Plane xPlane;
+  /** Min-Y plane */
+  protected final SidedPlane minYPlane;
+  /** Max-Y plane */
+  protected final SidedPlane maxYPlane;
+  /** Min-Z plane */
+  protected final SidedPlane minZPlane;
+  /** Max-Z plane */
+  protected final SidedPlane maxZPlane;
+  
+  /** These are the edge points of the shape, which are defined to be at least one point on
+   * each surface area boundary.  In the case of a solid, this includes points which represent
+   * the intersection of XYZ bounding planes and the planet, as well as points representing
+   * the intersection of single bounding planes with the planet itself.
+   */
+  protected final GeoPoint[] edgePoints;
+
+  /** Notable points for XPlane */
+  protected final GeoPoint[] notableXPoints;
+
+  /**
+   * Sole constructor
+   *
+   *@param planetModel is the planet model.
+   *@param X is the X value.
+   *@param minY is the minimum Y value.
+   *@param maxY is the maximum Y value.
+   *@param minZ is the minimum Z value.
+   *@param maxZ is the maximum Z value.
+   */
+  public dXYZSolid(final PlanetModel planetModel,
+    final double X,
+    final double minY,
+    final double maxY,
+    final double minZ,
+    final double maxZ) {
+    super(planetModel);
+    // Argument checking
+    if (maxY - minY < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("Y values in wrong order or identical");
+    if (maxZ - minZ < Vector.MINIMUM_RESOLUTION)
+      throw new IllegalArgumentException("Z values in wrong order or identical");
+
+    final double worldMinX = planetModel.getMinimumXValue();
+    final double worldMaxX = planetModel.getMaximumXValue();
+    
+    // Construct the planes
+    xPlane = new Plane(xUnitVector,-X);
+    minYPlane = new SidedPlane(0.0,maxY,0.0,yUnitVector,-minY);
+    maxYPlane = new SidedPlane(0.0,minY,0.0,yUnitVector,-maxY);
+    minZPlane = new SidedPlane(0.0,0.0,maxZ,zUnitVector,-minZ);
+    maxZPlane = new SidedPlane(0.0,0.0,minZ,zUnitVector,-maxZ);
+      
+    // We need at least one point on the planet surface for each manifestation of the shape.
+    // There can be up to 2 (on opposite sides of the world).  But we have to go through
+    // 4 combinations of adjacent planes in order to find out if any have 2 intersection solution.
+    // Typically, this requires 4 square root operations. 
+    final GeoPoint[] XminY = xPlane.findIntersections(planetModel,minYPlane,maxYPlane,minZPlane,maxZPlane);
+    final GeoPoint[] XmaxY = xPlane.findIntersections(planetModel,maxYPlane,minYPlane,minZPlane,maxZPlane);
+    final GeoPoint[] XminZ = xPlane.findIntersections(planetModel,minZPlane,maxZPlane,minYPlane,maxYPlane);
+    final GeoPoint[] XmaxZ = xPlane.findIntersections(planetModel,maxZPlane,minZPlane,minYPlane,maxYPlane);
+
+    notableXPoints = glueTogether(XminY, XmaxY, XminZ, XmaxZ);
+
+    // Now, compute the edge points.
+    // This is the trickiest part of setting up an XYZSolid.  We've computed intersections already, so
+    // we'll start there.  We know that at most there will be two disconnected shapes on the planet surface.
+    // But there's also a case where exactly one plane slices through the world, and none of the bounding plane
+    // intersections do.  Thus, if we don't find any of the edge intersection cases, we have to look for that last case.
+      
+    // We need to look at single-plane/world intersections.
+    // We detect these by looking at the world model and noting its x, y, and z bounds.
+    // For the single-dimension degenerate case, there's really only one plane that can possibly intersect the world.
+    // The cases we are looking for are when the four corner points for any given
+    // plane are all outside of the world, AND that plane intersects the world.
+    // There are four corner points all told; we must evaluate these WRT the planet surface.
+    final boolean XminYminZ = planetModel.pointOutside(X, minY, minZ);
+    final boolean XminYmaxZ = planetModel.pointOutside(X, minY, maxZ);
+    final boolean XmaxYminZ = planetModel.pointOutside(X, maxY, minZ);
+    final boolean XmaxYmaxZ = planetModel.pointOutside(X, maxY, maxZ);
+
+    final GeoPoint[] xEdges;
+    if (X - worldMinX >= -Vector.MINIMUM_RESOLUTION && X - worldMaxX <= Vector.MINIMUM_RESOLUTION &&
+      minY < 0.0 && maxY > 0.0 && minZ < 0.0 && maxZ > 0.0 &&
+      XminYminZ && XminYmaxZ && XmaxYminZ && XmaxYmaxZ) {
+      // Find any point on the X plane that intersects the world
+      // First construct a perpendicular plane that will allow us to find a sample point.
+      // This plane is vertical and goes through the points (0,0,0) and (1,0,0)
+      // Then use it to compute a sample point.
+      final GeoPoint intPoint = xPlane.getSampleIntersectionPoint(planetModel, xVerticalPlane);
+      if (intPoint != null) {
+        xEdges = new GeoPoint[]{intPoint};
+      } else {
+        xEdges = EMPTY_POINTS;
+      }
+    } else {
+      xEdges = EMPTY_POINTS;
+    }
+
+    this.edgePoints = glueTogether(XminY,XmaxY,XminZ,XmaxZ,xEdges);
+  }
+
+  @Override
+  protected GeoPoint[] getEdgePoints() {
+    return edgePoints;
+  }
+  
+  @Override
+  public boolean isWithin(final double x, final double y, final double z) {
+    return xPlane.evaluateIsZero(x, y, z) &&
+      minYPlane.isWithin(x, y, z) &&
+      maxYPlane.isWithin(x, y, z) &&
+      minZPlane.isWithin(x, y, z) &&
+      maxZPlane.isWithin(x, y, z);
+  }
+
+  @Override
+  public int getRelationship(final GeoShape path) {
+    //System.err.println(this+" getrelationship with "+path);
+    final int insideRectangle = isShapeInsideArea(path);
+    if (insideRectangle == SOME_INSIDE) {
+      //System.err.println(" some shape points inside area");
+      return OVERLAPS;
+    }
+
+    // Figure out if the entire XYZArea is contained by the shape.
+    final int insideShape = isAreaInsideShape(path);
+    if (insideShape == SOME_INSIDE) {
+      //System.err.println(" some area points inside shape");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE && insideShape == ALL_INSIDE) {
+      //System.err.println(" inside of each other");
+      return OVERLAPS;
+    }
+
+    // The entire locus of points in this shape is on a single plane, so we only need ot look for an intersection with that plane.
+    //System.err.println("xPlane = "+xPlane);
+    if (path.intersects(xPlane, notableXPoints, minYPlane, maxYPlane, minZPlane, maxZPlane)) {
+      //System.err.println(" edges intersect");
+      return OVERLAPS;
+    }
+
+    if (insideRectangle == ALL_INSIDE) {
+      //System.err.println(" shape points inside area");
+      return WITHIN;
+    }
+
+    if (insideShape == ALL_INSIDE) {
+      //System.err.println(" shape contains all area");
+      return CONTAINS;
+    }
+    //System.err.println(" disjoint");
+    return DISJOINT;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (!(o instanceof dXYZSolid))
+      return false;
+    dXYZSolid other = (dXYZSolid) o;
+    if (!super.equals(other)) {
+      return false;
+    }
+    return other.xPlane.equals(xPlane) &&
+      other.minYPlane.equals(minYPlane) &&
+      other.maxYPlane.equals(maxYPlane) &&
+      other.minZPlane.equals(minZPlane) &&
+      other.maxZPlane.equals(maxZPlane);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result  + xPlane.hashCode();
+    result = 31 * result  + minYPlane.hashCode();
+    result = 31 * result  + maxYPlane.hashCode();
+    result = 31 * result  + minZPlane.hashCode();
+    result = 31 * result  + maxZPlane.hashCode();
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "dXYZSolid: {planetmodel="+planetModel+", xplane="+xPlane+", minYplane="+minYPlane+", maxYplane="+maxYPlane+", minZplane="+minZPlane+", maxZplane="+maxZPlane+"}";
+  }
+  
+}
+