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

[17/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/test/org/apache/lucene/spatial3d/geom/GeoBBoxTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoBBoxTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoBBoxTest.java
new file mode 100755
index 0000000..f5a148f
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoBBoxTest.java
@@ -0,0 +1,364 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GeoBBoxTest {
+
+  protected final double DEGREES_TO_RADIANS = Math.PI / 180.0;
+
+  @Test
+  public void testBBoxDegenerate() {
+    GeoBBox box;
+    GeoConvexPolygon cp;
+    int relationship;
+    List<GeoPoint> points = new ArrayList<GeoPoint>();
+    points.add(new GeoPoint(PlanetModel.SPHERE, 24 * DEGREES_TO_RADIANS, -30 * DEGREES_TO_RADIANS));
+    points.add(new GeoPoint(PlanetModel.SPHERE, -11 * DEGREES_TO_RADIANS, 101 * DEGREES_TO_RADIANS));
+    points.add(new GeoPoint(PlanetModel.SPHERE, -49 * DEGREES_TO_RADIANS, -176 * DEGREES_TO_RADIANS));
+    GeoMembershipShape shape = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0);
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, -64 * DEGREES_TO_RADIANS, -64 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, 180 * DEGREES_TO_RADIANS);
+    relationship = box.getRelationship(shape);
+    assertEquals(GeoArea.CONTAINS, relationship);
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, -61.85 * DEGREES_TO_RADIANS, -67.5 * DEGREES_TO_RADIANS, -180 * DEGREES_TO_RADIANS, -168.75 * DEGREES_TO_RADIANS);
+    //System.out.println("Shape = " + shape + " Rect = " + box);
+    relationship = box.getRelationship(shape);
+    assertEquals(GeoArea.CONTAINS, relationship);
+  }
+
+  @Test
+  public void testBBoxPointWithin() {
+    GeoBBox box;
+    GeoPoint gp;
+
+    // Standard normal Rect box, not crossing dateline
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0);
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 0.0);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, 0.0);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.1);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.1);
+    assertFalse(box.isWithin(gp));
+    assertEquals(0.1,box.computeOutsideDistance(DistanceStyle.ARC,gp),1e-2);
+    assertEquals(0.1,box.computeOutsideDistance(DistanceStyle.NORMAL,gp),1e-2);
+    assertEquals(0.1,box.computeOutsideDistance(DistanceStyle.NORMAL,gp),1e-2);
+
+    // Standard normal Rect box, crossing dateline
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 1.0, -Math.PI + 1.0);
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
+    assertFalse(box.isWithin(gp));
+
+    // Latitude zone rectangle
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI, Math.PI);
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
+    assertTrue(box.isWithin(gp));
+
+    // World
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, Math.PI);
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, -Math.PI);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -Math.PI + 1.1);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, (-Math.PI - 1.1) + Math.PI * 2.0);
+    assertTrue(box.isWithin(gp));
+
+  }
+
+  @Test
+  public void testBBoxExpand() {
+    GeoBBox box;
+    GeoPoint gp;
+    // Standard normal Rect box, not crossing dateline
+    box = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0);
+    box = box.expand(0.1);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.0);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, 0.0);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, 0.0);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.15, 0.0);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.05);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.15);
+    assertFalse(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.05);
+    assertTrue(box.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, 1.15);
+    assertFalse(box.isWithin(gp));
+  }
+
+  @Test
+  public void testBBoxBounds() {
+    GeoBBox c;
+    LatLonBounds b;
+    XYZBounds xyzb;
+    GeoArea solid;
+    GeoPoint point;
+    int relationship;
+    
+    c= GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.7570958596622309, -0.7458670829264561, -0.9566079379002148, 1.4802570961901191);
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE,0.10922258701604912,0.1248184603754517,-0.8172414690802067,0.9959041483215542,-0.6136586624726926,0.6821740363641521);
+    point = new GeoPoint(PlanetModel.SPHERE, 0.3719987557178081, 1.4529582778845198);
+    assertTrue(c.isWithin(point));
+    assertTrue(solid.isWithin(point));
+    relationship = solid.getRelationship(c);
+    assertTrue(relationship == GeoArea.OVERLAPS || relationship == GeoArea.CONTAINS || relationship == GeoArea.WITHIN);
+
+    c= GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.006607096847842122, -0.002828135860810422, -0.0012934461873348349, 0.006727418645092394);
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE,0.9999995988328008,1.0000000002328306,-0.0012934708508166816,0.006727393021214471,-0.002828157275369464,0.006607074060760007);
+    point = new GeoPoint(PlanetModel.SPHERE, -5.236470872437899E-4, 3.992578692654256E-4);
+    assertTrue(c.isWithin(point));
+    assertTrue(solid.isWithin(point));
+    relationship = solid.getRelationship(c);
+    assertTrue(relationship == GeoArea.OVERLAPS || relationship == GeoArea.CONTAINS || relationship == GeoArea.WITHIN);
+    
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.25, -Math.PI * 0.25, -1.0, 1.0);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-1.0, b.getLeftLongitude(), 0.000001);
+    assertEquals(1.0, b.getRightLongitude(), 0.000001);
+    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
+    assertEquals(Math.PI * 0.25, b.getMaxLatitude(), 0.000001);
+    assertEquals(0.382051, xyzb.getMinimumX(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumX(), 0.000001);
+    assertEquals(-0.841471, xyzb.getMinimumY(), 0.000001);
+    assertEquals(0.841471, xyzb.getMaximumY(), 0.000001);
+    assertEquals(-0.707107, xyzb.getMinimumZ(), 0.000001);
+    assertEquals(0.707107, xyzb.getMaximumZ(), 0.000001);
+    
+    GeoArea area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE,
+      xyzb.getMinimumX() - 2.0 * Vector.MINIMUM_RESOLUTION,
+      xyzb.getMaximumX() + 2.0 * Vector.MINIMUM_RESOLUTION,
+      xyzb.getMinimumY() - 2.0 * Vector.MINIMUM_RESOLUTION,
+      xyzb.getMaximumY() + 2.0 * Vector.MINIMUM_RESOLUTION,
+      xyzb.getMinimumZ() - 2.0 * Vector.MINIMUM_RESOLUTION,
+      xyzb.getMaximumZ() + 2.0 * Vector.MINIMUM_RESOLUTION);
+    assertEquals(GeoArea.WITHIN, area.getRelationship(c));
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -1.0, 1.0);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-1.0, b.getLeftLongitude(), 0.000001);
+    assertEquals(1.0, b.getRightLongitude(), 0.000001);
+    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
+    assertEquals(0.0, b.getMaxLatitude(), 0.000001);
+    assertEquals(0.382051, xyzb.getMinimumX(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumX(), 0.000001);
+    assertEquals(-0.841471, xyzb.getMinimumY(), 0.000001);
+    assertEquals(0.841471, xyzb.getMaximumY(), 0.000001);
+    assertEquals(-0.707107, xyzb.getMinimumZ(), 0.000001);
+    assertEquals(0.0, xyzb.getMaximumZ(), 0.000001);
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, 1.0, -1.0);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    assertTrue(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    //assertEquals(1.0,b.getLeftLongitude(),0.000001);
+    //assertEquals(-1.0,b.getRightLongitude(),0.000001);
+    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
+    assertEquals(0.0, b.getMaxLatitude(), 0.000001);
+    assertEquals(-1.0, xyzb.getMinimumX(), 0.000001);
+    assertEquals(0.540303, xyzb.getMaximumX(), 0.000001);
+    assertEquals(-1.0, xyzb.getMinimumY(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumY(), 0.000001);
+    assertEquals(-0.707107, xyzb.getMinimumZ(), 0.000001);
+    assertEquals(0.0, xyzb.getMaximumZ(), 0.000001);
+
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -1.0, 1.0);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    //assertEquals(-1.0, b.getLeftLongitude(), 0.000001);
+    //assertEquals(1.0, b.getRightLongitude(), 0.000001);
+    assertEquals(0.0, xyzb.getMinimumX(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumX(), 0.000001);
+    assertEquals(-0.841471, xyzb.getMinimumY(), 0.000001);
+    assertEquals(0.841471, xyzb.getMaximumY(), 0.000001);
+    assertEquals(-1.0, xyzb.getMinimumZ(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumZ(), 0.000001);
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 1.0, -1.0);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    //assertEquals(1.0,b.getLeftLongitude(),0.000001);
+    //assertEquals(-1.0,b.getRightLongitude(),0.000001);
+    assertEquals(-1.0, xyzb.getMinimumX(), 0.000001);
+    assertEquals(0.540303, xyzb.getMaximumX(), 0.000001);
+    assertEquals(-1.0, xyzb.getMinimumY(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumY(), 0.000001);
+    assertEquals(-1.0, xyzb.getMinimumZ(), 0.000001);
+    assertEquals(1.0, xyzb.getMaximumZ(), 0.000001);
+
+    // Check wide variants of rectangle and longitude slice
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, -Math.PI + 0.1, Math.PI - 0.1);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001);
+    //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001);
+    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
+    assertEquals(0.0, b.getMaxLatitude(), 0.000001);
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 0.0, -Math.PI * 0.25, Math.PI - 0.1, -Math.PI + 0.1);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001);
+    assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001);
+    assertEquals(-Math.PI * 0.25, b.getMinLatitude(), 0.000001);
+    assertEquals(0.0, b.getMaxLatitude(), 0.000001);
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI + 0.1, Math.PI - 0.1);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    //assertEquals(-Math.PI+0.1,b.getLeftLongitude(),0.000001);
+    //assertEquals(Math.PI-0.1,b.getRightLongitude(),0.000001);
+
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, Math.PI - 0.1, -Math.PI + 0.1);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    //assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001);
+    //assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001);
+
+    // Check latitude zone
+    c = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, 1.0, -1.0, -Math.PI, Math.PI);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-1.0, b.getMinLatitude(), 0.000001);
+    assertEquals(1.0, b.getMaxLatitude(), 0.000001);
+
+    // Now, combine a few things to test the bounds object
+    GeoBBox c1;
+    GeoBBox c2;
+
+    c1 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0);
+    c2 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI);
+
+    b = new LatLonBounds();
+    c1.getBounds(b);
+    c2.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+
+    c1 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI, 0.0);
+    c2 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI * 0.5);
+
+    b = new LatLonBounds();
+    c1.getBounds(b);
+    c2.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    //assertEquals(-Math.PI,b.getLeftLongitude(),0.000001);
+    //assertEquals(Math.PI*0.5,b.getRightLongitude(),0.000001);
+
+    c1 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, -Math.PI * 0.5, 0.0);
+    c2 = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.PI * 0.5, -Math.PI * 0.5, 0.0, Math.PI);
+
+    b = new LatLonBounds();
+    c1.getBounds(b);
+    c2.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    //assertEquals(-Math.PI * 0.5,b.getLeftLongitude(),0.000001);
+    //assertEquals(Math.PI,b.getRightLongitude(),0.000001);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoCircleTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoCircleTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoCircleTest.java
new file mode 100755
index 0000000..186bf4c
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoCircleTest.java
@@ -0,0 +1,410 @@
+/*
+ * 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;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+public class GeoCircleTest extends LuceneTestCase {
+
+  @Test
+  public void testCircleDistance() {
+    GeoCircle c;
+    GeoPoint gp;
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertEquals(Double.MAX_VALUE, c.computeDistance(DistanceStyle.ARC,gp), 0.0);
+    assertEquals(Double.MAX_VALUE, c.computeDistance(DistanceStyle.NORMAL,gp), 0.0);
+    assertEquals(Double.MAX_VALUE, c.computeDistance(DistanceStyle.NORMAL,gp), 0.0);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertEquals(0.0, c.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    assertEquals(0.0, c.computeDistance(DistanceStyle.NORMAL,gp), 0.000001);
+    assertEquals(0.0, c.computeDistance(DistanceStyle.NORMAL,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
+    assertEquals(0.05, c.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    assertEquals(0.049995, c.computeDistance(DistanceStyle.LINEAR,gp), 0.000001);
+    assertEquals(0.049979, c.computeDistance(DistanceStyle.NORMAL,gp), 0.000001);
+  }
+
+  @Test
+  public void testCircleFullWorld() {
+    GeoCircle c;
+    GeoPoint gp;
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, -0.5, Math.PI);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertTrue(c.isWithin(gp));
+    LatLonBounds b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+  }
+
+  @Test
+  public void testCirclePointWithin() {
+    GeoCircle c;
+    GeoPoint gp;
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(c.isWithin(gp));
+    assertEquals(0.4,c.computeOutsideDistance(DistanceStyle.ARC,gp),1e-12);
+    assertEquals(0.12,c.computeOutsideDistance(DistanceStyle.NORMAL,gp),0.01);
+    assertEquals(0.4,c.computeOutsideDistance(DistanceStyle.LINEAR,gp),0.01);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(c.isWithin(gp));
+  }
+
+  @Test
+  public void testCircleBounds() {
+    GeoCircle c;
+    LatLonBounds b;
+    XYZBounds xyzb;
+    GeoArea area;
+    GeoPoint p1;
+    GeoPoint p2;
+    int relationship;
+
+    // ...
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84, -0.005931145568901605, -0.001942031539653079, 1.2991918568260272E-4);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84, 1.001098377143621, 1.001100011578687, -0.00207467080358696, -0.0018136665346280983, -0.006067808248760161, -0.005807683665759485);
+    p1 = new GeoPoint(PlanetModel.WGS84, -0.00591253844632244, -0.0020069187259065093);
+    p2 = new GeoPoint(1.001099185736782, -0.0020091272069679327, -0.005919118245803968);
+    assertTrue(c.isWithin(p1));
+    assertTrue(area.isWithin(p1));
+    relationship = area.getRelationship(c);
+    assertTrue(relationship != GeoArea.DISJOINT);
+
+    // Twelfth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84,-0.00824379317765984,-0.0011677469001838581,0.0011530035396910402);
+    p1 = new GeoPoint(PlanetModel.WGS84,-0.006505092992723671,0.007654282718327381);
+    p2 = new GeoPoint(1.0010681673665647,0.007662608264336381,-0.006512324005914593);
+    assertTrue(!c.isWithin(p1));
+    assertTrue(!c.isWithin(p2));
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84, 
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    relationship = area.getRelationship(c);
+    assertTrue(relationship == GeoArea.OVERLAPS || relationship == GeoArea.WITHIN);
+    // Point is actually outside the bounds, and outside the shape
+    assertTrue(!area.isWithin(p1));
+    // Approximate point the same
+    assertTrue(!area.isWithin(p2));
+    
+    // Eleventh BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE,-0.004431288600558495,-0.003687846671278374,1.704543429364245E-8);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println(area);
+    relationship = area.getRelationship(c);
+    assertTrue(GeoArea.WITHIN == relationship || GeoArea.OVERLAPS == relationship);
+
+    // Tenth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84,-0.0018829770647349636,-0.001969499061382591,1.3045439293158305E-5);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84, 
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println(area);
+    relationship = area.getRelationship(c);
+    assertTrue(GeoArea.WITHIN == relationship || GeoArea.OVERLAPS == relationship);
+
+    // Ninth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE,-4.211990380885122E-5,-0.0022958453508173044,1.4318475623498535E-5);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println(area);
+    relationship = area.getRelationship(c);
+    assertTrue(GeoArea.WITHIN == relationship || GeoArea.OVERLAPS == relationship);
+    
+    // Eighth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE,0.005321278689117842,-0.00216937368755372,1.5306034422500785E-4);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println(area);
+    relationship = area.getRelationship(c);
+    assertTrue(GeoArea.WITHIN == relationship || GeoArea.OVERLAPS == relationship);
+
+    // Seventh BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE,-0.0021627146783861745, -0.0017298167021592304,2.0818312293195752E-4);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println(area);
+    relationship = area.getRelationship(c);
+    assertTrue(GeoArea.WITHIN == relationship || GeoArea.OVERLAPS == relationship);
+
+    // Sixth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84,-0.006450320645814321,0.004660694205115142,0.00489710732634323);
+    //xyzb = new XYZBounds();
+    //c.getBounds(xyzb);
+    //System.err.println("xmin="+xyzb.getMinimumX()+", xmax="+xyzb.getMaximumX()+",ymin="+xyzb.getMinimumY()+", ymax="+xyzb.getMaximumY()+",zmin="+xyzb.getMinimumZ()+", zmax="+xyzb.getMaximumZ());
+    //xmin=1.0010356621420726, xmax=1.0011141249179447,ymin=-2.5326643901354566E-4, ymax=0.009584741915757169,zmin=-0.011359874956269283, zmax=-0.0015549504447452225
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,1.0010822580620098,1.0010945779732867,0.007079167343247293,0.007541006774427837,-0.0021855011220022575,-0.001896122718181518);
+    assertTrue(GeoArea.CONTAINS != area.getRelationship(c));
+    /*
+    p1 = new GeoPoint(1.0010893045436076,0.007380935180644008,-0.002140671370616495);
+    // This has a different bounding box, so we can't use it.
+    //p2 = new GeoPoint(PlanetModel.WGS84,-0.002164069780096702, 0.007505617500830066);
+    p2 = new GeoPoint(PlanetModel.WGS84,p1.getLatitude(),p1.getLongitude());
+    assertTrue(PlanetModel.WGS84.pointOnSurface(p2));
+    assertTrue(!c.isWithin(p2));
+    assertTrue(!area.isWithin(p2));
+    assertTrue(!c.isWithin(p1));
+    assertTrue(PlanetModel.WGS84.pointOnSurface(p1)); // This fails
+    assertTrue(!area.isWithin(p1)); // This fails
+    */
+    
+    // Fifth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -0.004282454525970269, -1.6739831367422277E-4, 1.959639723134033E-6);
+    assertTrue(c.isWithin(c.getEdgePoints()[0]));
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    assertTrue(GeoArea.WITHIN == area.getRelationship(c) || GeoArea.OVERLAPS == area.getRelationship(c));
+    
+    // Fourth BKD discovered failure
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -0.0048795517261255, 0.004053904306995974, 5.93699764258874E-6);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    assertTrue(GeoArea.WITHIN == area.getRelationship(c) || GeoArea.OVERLAPS == area.getRelationship(c));
+    
+    // Yet another test case from BKD
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.WGS84, 0.006229478708446979, 0.005570196723795424, 3.840276763694387E-5);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    p1 = new GeoPoint(PlanetModel.WGS84, 0.006224927111830945, 0.005597367237251763);
+    p2 = new GeoPoint(1.0010836083810235, 0.005603490759433942, 0.006231850560862502);
+    assertTrue(PlanetModel.WGS84.pointOnSurface(p1));
+    //assertTrue(PlanetModel.WGS84.pointOnSurface(p2));
+    assertTrue(c.isWithin(p1));
+    assertTrue(c.isWithin(p2));
+    assertTrue(area.isWithin(p1));
+    assertTrue(area.isWithin(p2));
+    
+    // Another test case from BKD
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -0.005955031040627789, -0.0029274772647399153, 1.601488279374338E-5);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    
+    relationship = area.getRelationship(c);
+    assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
+    
+    // Test case from BKD
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -0.765816119338, 0.991848766844, 0.8153163226330487);
+    p1 = new GeoPoint(0.7692262265236023, -0.055089298115534646, -0.6365973465711254);
+    assertTrue(c.isWithin(p1));
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    assertTrue(p1.x >= xyzb.getMinimumX() && p1.x <= xyzb.getMaximumX());
+    assertTrue(p1.y >= xyzb.getMinimumY() && p1.y <= xyzb.getMaximumY());
+    assertTrue(p1.z >= xyzb.getMinimumZ() && p1.z <= xyzb.getMaximumZ());
+    
+    // Vertical circle cases
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, -0.5, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-0.6, b.getLeftLongitude(), 0.000001);
+    assertEquals(-0.4, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.1, b.getMinLatitude(), 0.000001);
+    assertEquals(0.1, b.getMaxLatitude(), 0.000001);
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, 0.5, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.4, b.getLeftLongitude(), 0.000001);
+    assertEquals(0.6, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.1, b.getMinLatitude(), 0.000001);
+    assertEquals(0.1, b.getMaxLatitude(), 0.000001);
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-0.1, b.getLeftLongitude(), 0.000001);
+    assertEquals(0.1, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.1, b.getMinLatitude(), 0.000001);
+    assertEquals(0.1, b.getMaxLatitude(), 0.000001);
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.0, Math.PI, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.000001);
+    assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.1, b.getMinLatitude(), 0.000001);
+    assertEquals(0.1, b.getMaxLatitude(), 0.000001);
+    // Horizontal circle cases
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Math.PI * 0.5, 0.0, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertTrue(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(Math.PI * 0.5 - 0.1, b.getMinLatitude(), 0.000001);
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertTrue(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertTrue(b.checkNoBottomLatitudeBound());
+    assertEquals(-Math.PI * 0.5 + 0.1, b.getMaxLatitude(), 0.000001);
+
+    // Now do a somewhat tilted plane, facing different directions.
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, 0.0, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(-0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(0.1, b.getRightLongitude(), 0.00001);
+
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, Math.PI, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(Math.PI - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(-Math.PI + 0.1, b.getRightLongitude(), 0.00001);
+
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, Math.PI * 0.5, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001);
+
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, -Math.PI * 0.5, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(-Math.PI * 0.5 - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(-Math.PI * 0.5 + 0.1, b.getRightLongitude(), 0.00001);
+
+    // Slightly tilted, PI/4 direction.
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, Math.PI * 0.25, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001);
+
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, -Math.PI * 0.25, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(-Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001);
+
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -0.01, Math.PI * 0.25, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.09, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.11, b.getMinLatitude(), 0.000001);
+    assertEquals(Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001);
+
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, -0.01, -Math.PI * 0.25, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.09, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.11, b.getMinLatitude(), 0.000001);
+    assertEquals(-Math.PI * 0.25 - 0.1, b.getLeftLongitude(), 0.00001);
+    assertEquals(-Math.PI * 0.25 + 0.1, b.getRightLongitude(), 0.00001);
+
+    // Now do a somewhat tilted plane.
+    c = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, 0.01, -0.5, 0.1);
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(0.11, b.getMaxLatitude(), 0.000001);
+    assertEquals(-0.09, b.getMinLatitude(), 0.000001);
+    assertEquals(-0.6, b.getLeftLongitude(), 0.00001);
+    assertEquals(-0.4, b.getRightLongitude(), 0.00001);
+
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoConvexPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoConvexPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoConvexPolygonTest.java
new file mode 100755
index 0000000..a6ca404
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoConvexPolygonTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GeoConvexPolygonTest {
+
+
+  @Test
+  public void testPolygonPointWithin() {
+    GeoConvexPolygon c;
+    GeoPoint gp;
+    c = new GeoConvexPolygon(PlanetModel.SPHERE, -0.1, -0.5);
+    c.addPoint(0.0, -0.6, false);
+    c.addPoint(0.1, -0.5, false);
+    c.addPoint(0.0, -0.4, false);
+    c.done(false);
+    // Sample some points within
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    // Sample some nearby points outside, and compute distance-to-shape for them as well
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
+    assertFalse(c.isWithin(gp));
+    assertEquals(0.05,c.computeOutsideDistance(DistanceStyle.ARC,gp),1e-12);
+    assertEquals(0.05,c.computeOutsideDistance(DistanceStyle.NORMAL,gp),1e-3);
+    assertEquals(0.05,c.computeOutsideDistance(DistanceStyle.LINEAR,gp),1e-3);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    // Random points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(c.isWithin(gp));
+  }
+
+  @Test
+  public void testPolygonBounds() {
+    GeoConvexPolygon c;
+    LatLonBounds b;
+
+    c = new GeoConvexPolygon(PlanetModel.SPHERE, -0.1, -0.5);
+    c.addPoint(0.0, -0.6, false);
+    c.addPoint(0.1, -0.5, false);
+    c.addPoint(0.0, -0.4, false);
+    c.done(false);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-0.6, b.getLeftLongitude(), 0.000001);
+    assertEquals(-0.4, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.1, b.getMinLatitude(), 0.000001);
+    assertEquals(0.1, b.getMaxLatitude(), 0.000001);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoModelTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoModelTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoModelTest.java
new file mode 100644
index 0000000..d5fcbdd
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoModelTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test basic plane functionality.
+ */
+public class GeoModelTest {
+
+  protected final static PlanetModel scaledModel = new PlanetModel(1.2,1.5);
+  
+  @Test
+  public void testBasicCircle() {
+    // The point of this test is just to make sure nothing blows up doing normal things with a quite non-spherical model
+    // Make sure that the north pole is in the circle, and south pole isn't
+    final GeoPoint northPole = new GeoPoint(scaledModel, Math.PI * 0.5, 0.0);
+    final GeoPoint southPole = new GeoPoint(scaledModel, -Math.PI * 0.5, 0.0);
+    final GeoPoint point1 = new GeoPoint(scaledModel, Math.PI * 0.25, 0.0);
+    final GeoPoint point2 = new GeoPoint(scaledModel, Math.PI * 0.125, 0.0);
+    
+    GeoCircle circle = new GeoStandardCircle(scaledModel, Math.PI * 0.5, 0.0, 0.01);
+    assertTrue(circle.isWithin(northPole));
+    assertFalse(circle.isWithin(southPole));
+    assertFalse(circle.isWithin(point1));
+    LatLonBounds bounds;
+    bounds = new LatLonBounds();
+    circle.getBounds(bounds);
+    assertTrue(bounds.checkNoLongitudeBound());
+    assertTrue(bounds.checkNoTopLatitudeBound());
+    assertFalse(bounds.checkNoBottomLatitudeBound());
+    assertEquals(Math.PI * 0.5 - 0.01, bounds.getMinLatitude(), 0.01);
+
+    circle = new GeoStandardCircle(scaledModel, Math.PI * 0.25, 0.0, 0.01);
+    assertTrue(circle.isWithin(point1));
+    assertFalse(circle.isWithin(northPole));
+    assertFalse(circle.isWithin(southPole));
+    bounds = new LatLonBounds();
+    circle.getBounds(bounds);
+    assertFalse(bounds.checkNoTopLatitudeBound());
+    assertFalse(bounds.checkNoLongitudeBound());
+    assertFalse(bounds.checkNoBottomLatitudeBound());
+    assertEquals(Math.PI * 0.25 + 0.01, bounds.getMaxLatitude(), 0.00001);
+    assertEquals(Math.PI * 0.25 - 0.01, bounds.getMinLatitude(), 0.00001);
+    assertEquals(-0.0125, bounds.getLeftLongitude(), 0.0001);
+    assertEquals(0.0125, bounds.getRightLongitude(), 0.0001);
+
+    circle = new GeoStandardCircle(scaledModel, Math.PI * 0.125, 0.0, 0.01);
+    assertTrue(circle.isWithin(point2));
+    assertFalse(circle.isWithin(northPole));
+    assertFalse(circle.isWithin(southPole));
+    bounds = new LatLonBounds();
+    circle.getBounds(bounds);
+    assertFalse(bounds.checkNoLongitudeBound());
+    assertFalse(bounds.checkNoTopLatitudeBound());
+    assertFalse(bounds.checkNoBottomLatitudeBound());
+    // Symmetric, as expected
+    assertEquals(Math.PI * 0.125 - 0.01, bounds.getMinLatitude(), 0.00001);
+    assertEquals(Math.PI * 0.125 + 0.01, bounds.getMaxLatitude(), 0.00001);
+    assertEquals(-0.0089, bounds.getLeftLongitude(), 0.0001);
+    assertEquals(0.0089, bounds.getRightLongitude(), 0.0001);
+
+  }
+
+  @Test
+  public void testBasicRectangle() {
+    final GeoBBox bbox = GeoBBoxFactory.makeGeoBBox(scaledModel, 1.0, 0.0, 0.0, 1.0);
+    final GeoPoint insidePoint = new GeoPoint(scaledModel, 0.5, 0.5);
+    assertTrue(bbox.isWithin(insidePoint));
+    final GeoPoint topOutsidePoint = new GeoPoint(scaledModel, 1.01, 0.5);
+    assertFalse(bbox.isWithin(topOutsidePoint));
+    final GeoPoint bottomOutsidePoint = new GeoPoint(scaledModel, -0.01, 0.5);
+    assertFalse(bbox.isWithin(bottomOutsidePoint));
+    final GeoPoint leftOutsidePoint = new GeoPoint(scaledModel, 0.5, -0.01);
+    assertFalse(bbox.isWithin(leftOutsidePoint));
+    final GeoPoint rightOutsidePoint = new GeoPoint(scaledModel, 0.5, 1.01);
+    assertFalse(bbox.isWithin(rightOutsidePoint));
+    final LatLonBounds bounds = new LatLonBounds();
+    bbox.getBounds(bounds);
+    assertFalse(bounds.checkNoLongitudeBound());
+    assertFalse(bounds.checkNoTopLatitudeBound());
+    assertFalse(bounds.checkNoBottomLatitudeBound());
+    assertEquals(1.0, bounds.getMaxLatitude(), 0.00001);
+    assertEquals(0.0, bounds.getMinLatitude(), 0.00001);
+    assertEquals(1.0, bounds.getRightLongitude(), 0.00001);
+    assertEquals(0.0, bounds.getLeftLongitude(), 0.00001);
+  }
+  
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
new file mode 100755
index 0000000..3746069
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPathTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+import static java.lang.Math.toRadians;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GeoPathTest {
+
+  @Test
+  public void testPathDistance() {
+    // Start with a really simple case
+    GeoPath p;
+    GeoPoint gp;
+    p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    p.addPoint(0.0, 0.0);
+    p.addPoint(0.0, 0.1);
+    p.addPoint(0.0, 0.2);
+    p.done();
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.15);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15);
+    assertEquals(0.15 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12);
+    assertEquals(0.12 + 0.0, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.15, 0.05);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.25);
+    assertEquals(0.20 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.05);
+    assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+
+    // Compute path distances now
+    p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    p.addPoint(0.0, 0.0);
+    p.addPoint(0.0, 0.1);
+    p.addPoint(0.0, 0.2);
+    p.done();
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, 0.15);
+    assertEquals(0.15 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.12);
+    assertEquals(0.12, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+
+    // Now try a vertical path, and make sure distances are as expected
+    p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    p.addPoint(-Math.PI * 0.25, -0.5);
+    p.addPoint(Math.PI * 0.25, -0.5);
+    p.done();
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.1, -1.0);
+    assertEquals(Double.MAX_VALUE, p.computeDistance(DistanceStyle.ARC,gp), 0.0);
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.25 + 0.05, -0.5);
+    assertEquals(Math.PI * 0.5 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.25 - 0.05, -0.5);
+    assertEquals(0.0 + 0.05, p.computeDistance(DistanceStyle.ARC,gp), 0.000001);
+  }
+
+  @Test
+  public void testPathPointWithin() {
+    // Tests whether we can properly detect whether a point is within a path or not
+    GeoPath p;
+    GeoPoint gp;
+    p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    // Build a diagonal path crossing the equator
+    p.addPoint(-0.2, -0.2);
+    p.addPoint(0.2, 0.2);
+    p.done();
+    // Test points on the path
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.2, -0.2);
+    assertTrue(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertTrue(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, 0.1);
+    assertTrue(p.isWithin(gp));
+    // Test points off the path
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.2, 0.2);
+    assertFalse(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0);
+    assertFalse(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.2, -0.2);
+    assertFalse(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(p.isWithin(gp));
+    // Repeat the test, but across the terminator
+    p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    // Build a diagonal path crossing the equator
+    p.addPoint(-0.2, Math.PI - 0.2);
+    p.addPoint(0.2, -Math.PI + 0.2);
+    p.done();
+    // Test points on the path
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.2, Math.PI - 0.2);
+    assertTrue(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertTrue(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.1, -Math.PI + 0.1);
+    assertTrue(p.isWithin(gp));
+    // Test points off the path
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.2, -Math.PI + 0.2);
+    assertFalse(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0);
+    assertFalse(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.2, Math.PI - 0.2);
+    assertFalse(p.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(p.isWithin(gp));
+
+  }
+
+  @Test
+  public void testGetRelationship() {
+    GeoArea rect;
+    GeoPath p;
+    GeoPath c;
+    GeoPoint point;
+    GeoPoint pointApprox;
+    int relationship;
+    GeoArea area;
+    PlanetModel planetModel;
+    
+    planetModel = new PlanetModel(1.151145876105594, 0.8488541238944061);
+    c = new GeoPath(planetModel, 0.008726646259971648);
+    c.addPoint(-0.6925658899376476, 0.6316613927914589);
+    c.addPoint(0.27828548161836364, 0.6785795524104564);
+    c.done();
+    point = new GeoPoint(planetModel,-0.49298555067758226, 0.9892440995026406);
+    pointApprox = new GeoPoint(0.5110940362119821, 0.7774603209946239, -0.49984312299556544);
+    area = GeoAreaFactory.makeGeoArea(planetModel, 0.49937141144985997, 0.5161765426256085, 0.3337218719537796,0.8544419570901649, -0.6347692823688085, 0.3069696588119369);
+    assertTrue(!c.isWithin(point));
+    
+    // Start by testing the basic kinds of relationship, increasing in order of difficulty.
+
+    p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    p.addPoint(-0.3, -0.3);
+    p.addPoint(0.3, 0.3);
+    p.done();
+    // Easiest: The path is wholly contains the georect
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.05, -0.05, -0.05, 0.05);
+    assertEquals(GeoArea.CONTAINS, rect.getRelationship(p));
+    // Next easiest: Some endpoints of the rectangle are inside, and some are outside.
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.05, -0.05, -0.05, 0.5);
+    assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
+    // Now, all points are outside, but the figures intersect
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.05, -0.05, -0.5, 0.5);
+    assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
+    // Finally, all points are outside, and the figures *do not* intersect
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, 0.5);
+    assertEquals(GeoArea.WITHIN, rect.getRelationship(p));
+    // Check that segment edge overlap detection works
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.1, 0.0, -0.1, 0.0);
+    assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.2, 0.1, -0.2, -0.1);
+    assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
+    // Check if overlap at endpoints behaves as expected next
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, -0.35);
+    assertEquals(GeoArea.OVERLAPS, rect.getRelationship(p));
+    rect = new GeoRectangle(PlanetModel.SPHERE, 0.5, -0.5, -0.5, -0.45);
+    assertEquals(GeoArea.DISJOINT, rect.getRelationship(p));
+
+  }
+
+  @Test
+  public void testPathBounds() {
+    GeoPath c;
+    LatLonBounds b;
+    XYZBounds xyzb;
+    GeoPoint point;
+    int relationship;
+    GeoArea area;
+    PlanetModel planetModel;
+    
+    planetModel = new PlanetModel(0.751521665790406,1.248478334209594);
+    c = new GeoPath(planetModel, 0.7504915783575618);
+    c.addPoint(0.10869761172400265, 0.08895880215465272);
+    c.addPoint(0.22467878641991612, 0.10972973084229565);
+    c.addPoint(-0.7398772468744732, -0.4465812941383364);
+    c.addPoint(-0.18462055300079366, -0.6713857796763727);
+    c.done();
+    point = new GeoPoint(planetModel,-0.626645355125733,-1.409304625439381);
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(planetModel,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    relationship = area.getRelationship(c);
+    assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
+    assertTrue(area.isWithin(point));
+    // No longer true due to fixed GeoPath waypoints.
+    //assertTrue(c.isWithin(point));
+    
+    c = new GeoPath(PlanetModel.WGS84, 0.6894050545377601);
+    c.addPoint(-0.0788176065762948, 0.9431251741731624);
+    c.addPoint(0.510387871458147, 0.5327078872484678);
+    c.addPoint(-0.5624521609859962, 1.5398841746888388);
+    c.addPoint(-0.5025171434638661, -0.5895998642788894);
+    c.done();
+    point = new GeoPoint(PlanetModel.WGS84, 0.023652082107211682, 0.023131910152748437);
+    //System.err.println("Point.x = "+point.x+"; point.y="+point.y+"; point.z="+point.z);
+    assertTrue(c.isWithin(point));
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println("minx="+xyzb.getMinimumX()+" maxx="+xyzb.getMaximumX()+" miny="+xyzb.getMinimumY()+" maxy="+xyzb.getMaximumY()+" minz="+xyzb.getMinimumZ()+" maxz="+xyzb.getMaximumZ());
+    //System.err.println("point.x="+point.x+" point.y="+point.y+" point.z="+point.z);
+    relationship = area.getRelationship(c);
+    assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
+    assertTrue(area.isWithin(point));
+    
+    c = new GeoPath(PlanetModel.WGS84, 0.7766715171374766);
+    c.addPoint(-0.2751718361148076, -0.7786721269011477);
+    c.addPoint(0.5728375851539309, -1.2700115736820465);
+    c.done();
+    point = new GeoPoint(PlanetModel.WGS84, -0.01580760332365284, -0.03956004622490505);
+    assertTrue(c.isWithin(point));
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    //System.err.println("minx="+xyzb.getMinimumX()+" maxx="+xyzb.getMaximumX()+" miny="+xyzb.getMinimumY()+" maxy="+xyzb.getMaximumY()+" minz="+xyzb.getMinimumZ()+" maxz="+xyzb.getMaximumZ());
+    //System.err.println("point.x="+point.x+" point.y="+point.y+" point.z="+point.z);
+    relationship = area.getRelationship(c);
+    assertTrue(relationship == GeoArea.WITHIN || relationship == GeoArea.OVERLAPS);
+    assertTrue(area.isWithin(point));
+
+    c = new GeoPath(PlanetModel.SPHERE, 0.1);
+    c.addPoint(-0.3, -0.3);
+    c.addPoint(0.3, 0.3);
+    c.done();
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-0.4046919, b.getLeftLongitude(), 0.000001);
+    assertEquals(0.4046919, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.3999999, b.getMinLatitude(), 0.000001);
+    assertEquals(0.3999999, b.getMaxLatitude(), 0.000001);
+
+  }
+
+  @Test
+  public void testCoLinear() {
+    // p1: (12,-90), p2: (11, -55), (129, -90)
+    GeoPath p = new GeoPath(PlanetModel.SPHERE, 0.1);
+    p.addPoint(toRadians(-90), toRadians(12));//south pole
+    p.addPoint(toRadians(-55), toRadians(11));
+    p.addPoint(toRadians(-90), toRadians(129));//south pole again
+    p.done();//at least test this doesn't bomb like it used too -- LUCENE-6520
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPointTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPointTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPointTest.java
new file mode 100644
index 0000000..ed17928
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPointTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomFloat;
+
+/**
+ * Test basic GeoPoint functionality.
+ */
+public class GeoPointTest extends LuceneTestCase {
+  static final double DEGREES_TO_RADIANS =  Math.PI / 180;
+
+  @Test
+  public void testConversion() {
+    testPointRoundTrip(PlanetModel.SPHERE, 90 * DEGREES_TO_RADIANS, 0, 1e-6);
+    testPointRoundTrip(PlanetModel.SPHERE, -90 * DEGREES_TO_RADIANS, 0, 1e-6);
+    testPointRoundTrip(PlanetModel.WGS84, 90 * DEGREES_TO_RADIANS, 0, 1e-6);
+    testPointRoundTrip(PlanetModel.WGS84, -90 * DEGREES_TO_RADIANS, 0, 1e-6);
+
+    final int times = atLeast(100);
+    for (int i = 0; i < times; i++) {
+      final double pLat = (randomFloat() * 180.0 - 90.0) * DEGREES_TO_RADIANS;
+      final double pLon = (randomFloat() * 360.0 - 180.0) * DEGREES_TO_RADIANS;
+      testPointRoundTrip(PlanetModel.SPHERE, pLat, pLon, 1e-6);//1e-6 since there's a square root in there (Karl says)
+      testPointRoundTrip(PlanetModel.WGS84, pLat, pLon, 1e-6);
+    }
+  }
+
+  protected void testPointRoundTrip(PlanetModel planetModel, double pLat, double pLon, double epsilon) {
+    final GeoPoint p1 = new GeoPoint(planetModel, pLat, pLon);
+    // In order to force the reverse conversion, we have to construct a geopoint from just x,y,z
+    final GeoPoint p2 = new GeoPoint(p1.x, p1.y, p1.z);
+    // Now, construct the final point based on getLatitude() and getLongitude()
+    final GeoPoint p3 = new GeoPoint(planetModel, p2.getLatitude(), p2.getLongitude());
+    double dist = p1.arcDistance(p3);
+    assertEquals(0, dist, epsilon);
+  }
+
+  @Test
+  public void testSurfaceDistance() {
+    final int times = atLeast(100);
+    for (int i = 0; i < times; i++) {
+      final double p1Lat = (randomFloat() * 180.0 - 90.0) * DEGREES_TO_RADIANS;
+      final double p1Lon = (randomFloat() * 360.0 - 180.0) * DEGREES_TO_RADIANS;
+      final double p2Lat = (randomFloat() * 180.0 - 90.0) * DEGREES_TO_RADIANS;
+      final double p2Lon = (randomFloat() * 360.0 - 180.0) * DEGREES_TO_RADIANS;
+      final GeoPoint p1 = new GeoPoint(PlanetModel.SPHERE, p1Lat, p1Lon);
+      final GeoPoint p2 = new GeoPoint(PlanetModel.SPHERE, p2Lat, p2Lon);
+      final double arcDistance = p1.arcDistance(p2);
+      // Compute ellipsoid distance; it should agree for a sphere
+      final double surfaceDistance = PlanetModel.SPHERE.surfaceDistance(p1,p2);
+      assertEquals(arcDistance, surfaceDistance, 1e-6);
+    }
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testBadLatLon() {
+    new GeoPoint(PlanetModel.SPHERE, 50.0, 32.2);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
new file mode 100755
index 0000000..d9b220d
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/GeoPolygonTest.java
@@ -0,0 +1,165 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class GeoPolygonTest {
+
+
+  @Test
+  public void testPolygonPointWithin() {
+    GeoMembershipShape c;
+    GeoPoint gp;
+    List<GeoPoint> points;
+
+    points = new ArrayList<GeoPoint>();
+    points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
+
+    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0);
+    // Sample some points within
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    // Sample some nearby points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.65);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    // Random points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(c.isWithin(gp));
+
+    points = new ArrayList<GeoPoint>();
+    points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5));
+    points.add(new GeoPoint(PlanetModel.SPHERE, -0.01, -0.6));
+    points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.7));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.8));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.7));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.01, -0.6));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
+        
+        /*
+        System.out.println("Points: ");
+        for (GeoPoint p : points) {
+            System.out.println(" "+p);
+        }
+        */
+
+    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0);
+    // Sample some points within
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.55);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.45);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.05, -0.5);
+    assertTrue(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.7);
+    assertTrue(c.isWithin(gp));
+    // Sample some nearby points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, -0.35);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, -0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.15, -0.5);
+    assertFalse(c.isWithin(gp));
+    // Random points outside
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, Math.PI * 0.5, 0.0);
+    assertFalse(c.isWithin(gp));
+    gp = new GeoPoint(PlanetModel.SPHERE, 0.0, Math.PI);
+    assertFalse(c.isWithin(gp));
+
+  }
+
+  @Test
+  public void testPolygonBounds() {
+    GeoMembershipShape c;
+    LatLonBounds b;
+    List<GeoPoint> points;
+    XYZBounds xyzb;
+    GeoPoint point;
+    GeoArea area;
+    
+    // BKD failure
+    points = new ArrayList<GeoPoint>();
+    points.add(new GeoPoint(PlanetModel.WGS84, -0.36716183577912814, 1.4836349969188696));
+    points.add(new GeoPoint(PlanetModel.WGS84, 0.7846038240742979, -0.02743348424931823));
+    points.add(new GeoPoint(PlanetModel.WGS84, -0.7376479402362607, -0.5072961758807019));
+    points.add(new GeoPoint(PlanetModel.WGS84, -0.3760415907667887, 1.4970455334565513));
+    
+    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.WGS84, points, 1);
+    
+    point = new GeoPoint(PlanetModel.WGS84, -0.01580760332365284, -0.03956004622490505);
+    assertTrue(c.isWithin(point));
+    xyzb = new XYZBounds();
+    c.getBounds(xyzb);
+    area = GeoAreaFactory.makeGeoArea(PlanetModel.WGS84,
+      xyzb.getMinimumX(), xyzb.getMaximumX(), xyzb.getMinimumY(), xyzb.getMaximumY(), xyzb.getMinimumZ(), xyzb.getMaximumZ());
+    assertTrue(area.isWithin(point));
+    
+    points = new ArrayList<GeoPoint>();
+    points.add(new GeoPoint(PlanetModel.SPHERE, -0.1, -0.5));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.6));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.1, -0.5));
+    points.add(new GeoPoint(PlanetModel.SPHERE, 0.0, -0.4));
+
+    c = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, 0);
+
+    b = new LatLonBounds();
+    c.getBounds(b);
+    assertFalse(b.checkNoLongitudeBound());
+    assertFalse(b.checkNoTopLatitudeBound());
+    assertFalse(b.checkNoBottomLatitudeBound());
+    assertEquals(-0.6, b.getLeftLongitude(), 0.000001);
+    assertEquals(-0.4, b.getRightLongitude(), 0.000001);
+    assertEquals(-0.1, b.getMinLatitude(), 0.000001);
+    assertEquals(0.1, b.getMaxLatitude(), 0.000001);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/PlaneTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/PlaneTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/PlaneTest.java
new file mode 100644
index 0000000..91bd0c3
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/PlaneTest.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test basic plane functionality.
+ */
+public class PlaneTest {
+
+
+  @Test
+  public void testIdenticalPlanes() {
+    final GeoPoint p = new GeoPoint(PlanetModel.SPHERE, 0.123, -0.456);
+    final Plane plane1 = new Plane(p, 0.0);
+    final Plane plane2 = new Plane(p, 0.0);
+    assertTrue(plane1.isNumericallyIdentical(plane2));
+    final Plane plane3 = new Plane(p, 0.1);
+    assertFalse(plane1.isNumericallyIdentical(plane3));
+    final Vector v1 = new Vector(0.1, -0.732, 0.9);
+    final double constant = 0.432;
+    final Vector v2 = new Vector(v1.x * constant, v1.y * constant, v1.z * constant);
+    final Plane p1 = new Plane(v1, 0.2);
+    final Plane p2 = new Plane(v2, 0.2 * constant);
+    assertTrue(p1.isNumericallyIdentical(p2));
+  }
+
+  @Test
+  public void testInterpolation() {
+    // [X=0.35168818443386646, Y=-0.19637966197066342, Z=0.9152870857244183],
+    // [X=0.5003343189532654, Y=0.522128543226148, Z=0.6906861469771293],
+
+    final GeoPoint start = new GeoPoint(0.35168818443386646, -0.19637966197066342, 0.9152870857244183);
+    final GeoPoint end = new GeoPoint(0.5003343189532654, 0.522128543226148, 0.6906861469771293);
+
+    // [A=-0.6135342247741855, B=0.21504338363863665, C=0.28188192383666794, D=0.0, side=-1.0] internal? false;
+    final Plane p = new Plane(-0.6135342247741855, 0.21504338363863665, 0.28188192383666794, 0.0);
+
+    final GeoPoint[] points = p.interpolate(start, end, new double[]{0.25, 0.50, 0.75});
+
+    for (GeoPoint point : points) {
+      assertTrue(p.evaluateIsZero(point));
+    }
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/0a1951be/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/XYZSolidTest.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/XYZSolidTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/XYZSolidTest.java
new file mode 100644
index 0000000..98c616e
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/XYZSolidTest.java
@@ -0,0 +1,220 @@
+/*
+ * 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;
+
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+public class XYZSolidTest extends LuceneTestCase {
+
+  @Test
+  public void testNonDegenerateRelationships() {
+    XYZSolid s;
+    GeoShape shape;
+    // Something bigger than the world
+    s = new StandardXYZSolid(PlanetModel.SPHERE, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0);
+    // Any shape, except whole world, should be within.
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.WITHIN, s.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    // An XYZSolid represents a surface shape, which when larger than the world is in fact
+    // the entire world, so it should overlap the world.
+    assertEquals(GeoArea.OVERLAPS, s.getRelationship(shape));
+
+    // Something overlapping the world on only one side
+    s = new StandardXYZSolid(PlanetModel.SPHERE, -2.0, 0.0, -2.0, 2.0, -2.0, 2.0);
+    // Some things should be disjoint...
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, s.getRelationship(shape));
+    // And, some things should be within... 
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI, 0.1);
+    assertEquals(GeoArea.WITHIN, s.getRelationship(shape));
+    // And, some things should overlap.
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.OVERLAPS, s.getRelationship(shape));
+
+    // Partial world should be contained by GeoWorld object...
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, s.getRelationship(shape));
+    
+    // Something inside the world
+    s = new StandardXYZSolid(PlanetModel.SPHERE, -0.1, 0.1, -0.1, 0.1, -0.1, 0.1);
+    // All shapes should be disjoint
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, s.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.DISJOINT, s.getRelationship(shape));
+    
+  }
+
+  @Test
+  public void testDegenerateRelationships() {
+    GeoArea solid;
+    GeoShape shape;
+    
+    // Basic test of the factory method - non-degenerate
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, -2.0, 2.0, -2.0, 2.0, -2.0, 2.0);
+    // Any shape, except whole world, should be within.
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.WITHIN, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    // An XYZSolid represents a surface shape, which when larger than the world is in fact
+    // the entire world, so it should overlap the world.
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+
+    // Build a degenerate point, not on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
+    // disjoint with everything?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a degenerate point that IS on the sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0);
+    // inside everything that it touches?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape degenerate in (x,y), which has no points on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, 0.0, 0.0, -0.1, 0.1);
+    // disjoint with everything?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape degenerate in (x,y) which has one point on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, 0.0, 0.0, -0.1, 1.1);
+    // inside everything that it touches?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape degenerate in (x,y) which has two points on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, 0.0, 0.0, -1.1, 1.1);
+    // inside everything that it touches?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    
+    // Build a shape degenerate in (x,z), which has no points on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, -0.1, 0.1, 0.0, 0.0);
+    // disjoint with everything?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape degenerate in (x,z) which has one point on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, -0.1, 1.1, 0.0, 0.0);
+    // inside everything that it touches?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, -Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape degenerate in (x,y) which has two points on sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, -1.1, 1.1, 0.0, 0.0);
+    // inside everything that it touches?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, -Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+
+    // MHL for y-z check
+    
+    // Build a shape that is degenerate in x, which has zero points intersecting sphere
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, -0.1, 0.1, -0.1, 0.1);
+    // disjoint with everything?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape that is degenerate in x, which has zero points intersecting sphere, second variation
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, -0.1, 0.1, 1.1, 1.2);
+    // disjoint with everything?
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+
+    // Build a shape that is disjoint in X but intersects sphere in a complete circle
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, -1.1, 1.1, -1.1, 1.1);
+    // inside everything that it touches?
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, -Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+
+    // Build a shape that is disjoint in X but intersects sphere in a half circle in Y
+    solid = GeoAreaFactory.makeGeoArea(PlanetModel.SPHERE, 0.0, 0.0, 0.0, 1.1, -1.1, 1.1);
+    // inside everything that it touches?
+    shape = new GeoWorld(PlanetModel.SPHERE);
+    assertEquals(GeoArea.CONTAINS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, 0.0, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, 0.0, -Math.PI * 0.5, 0.1);
+    assertEquals(GeoArea.DISJOINT, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+    shape = new GeoStandardCircle(PlanetModel.SPHERE, -Math.PI * 0.5, 0.0, 0.1);
+    assertEquals(GeoArea.OVERLAPS, solid.getRelationship(shape));
+
+    // MHL for degenerate Y
+    // MHL for degenerate Z
+    
+  }
+  
+}