diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoAreaShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoAreaShape.java
new file mode 100644
index 0000000000..b00ffca232
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoAreaShape.java
@@ -0,0 +1,39 @@
+/*
+ * 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;
+
+/**
+ * Shape that implements GeoArea. This type of shapes are able to resolve the
+ * spatial relationship of other shapes with itself.
+ *
+ * @lucene.experimental
+ */
+
+public interface GeoAreaShape extends GeoMembershipShape, GeoArea{
+
+  /**
+   * Assess whether a shape intersects with any of the edges this shape.
+   * Note well that this method return false if the shape contains, is within
+   * or is disjoint with the given shape.
+   *
+   * @param geoShape is the shape to assess for intersection with this shape's edges.
+   *
+   * @return true if there's such an intersection, false if not.
+   */
+  boolean intersects(GeoShape geoShape);
+}
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBBox.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBBox.java
index 0ae242592d..683333cbe9 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBBox.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBBox.java
@@ -23,7 +23,7 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.experimental
  */
-public interface GeoBBox extends GeoMembershipShape, GeoSizeable, GeoArea {
+public interface GeoBBox extends GeoAreaShape, GeoSizeable {
 
   /**
    * Expand box by specified angle.
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseAreaShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseAreaShape.java
new file mode 100644
index 0000000000..5bd8ab3e24
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseAreaShape.java
@@ -0,0 +1,125 @@
+/*
+ * 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;
+
+/**
+ * Base extended areaShape object.
+ *
+ * @lucene.internal
+ */
+abstract class GeoBaseAreaShape extends GeoBaseMembershipShape implements GeoAreaShape {
+
+  /** Constructor.
+   *@param planetModel is the planet model to use.
+   */
+  public GeoBaseAreaShape(final PlanetModel planetModel) {
+    super(planetModel);
+  }
+
+  /** All edgepoints inside shape */
+  protected final static int ALL_INSIDE = 0;
+  /** Some edgepoints inside shape */
+  protected final static int SOME_INSIDE = 1;
+  /** No edgepoints inside shape */
+  protected final static int NONE_INSIDE = 2;
+
+  /** Determine the relationship between the GeoAreShape and the
+   * shape's edgepoints.
+   *@param geoShape is the shape.
+   *@return the relationship.
+   */
+  protected  int isShapeInsideGeoAreaShape(final GeoShape geoShape) {
+    boolean foundOutside = false;
+    boolean foundInside = false;
+    for (GeoPoint p : geoShape.getEdgePoints()) {
+      if (isWithin(p)) {
+        foundInside = true;
+      } else {
+        foundOutside = true;
+      }
+      if (foundInside && foundOutside) {
+        return SOME_INSIDE;
+      }
+    }
+    if (!foundInside && !foundOutside)
+      return NONE_INSIDE;
+    if (foundInside && !foundOutside)
+      return ALL_INSIDE;
+    if (foundOutside && !foundInside)
+      return NONE_INSIDE;
+    return SOME_INSIDE;
+  }
+
+  /** Determine the relationship between the GeoAreaShape's edgepoints and the
+   * provided shape.
+   *@param geoshape is the shape.
+   *@return the relationship.
+   */
+  protected int isGeoAreaShapeInsideShape(final GeoShape geoshape)  {
+    boolean foundOutside = false;
+    boolean foundInside = false;
+    for (GeoPoint p : getEdgePoints()) {
+      if (geoshape.isWithin(p)) {
+        foundInside = true;
+      } else {
+        foundOutside = true;
+      }
+      if (foundInside && foundOutside) {
+        return SOME_INSIDE;
+      }
+    }
+    if (!foundInside && !foundOutside)
+      return NONE_INSIDE;
+    if (foundInside && !foundOutside)
+      return ALL_INSIDE;
+    if (foundOutside && !foundInside)
+      return NONE_INSIDE;
+    return SOME_INSIDE;
+  }
+
+  @Override
+  public int getRelationship(GeoShape geoShape) {
+    final int insideGeoAreaShape = isShapeInsideGeoAreaShape(geoShape);
+    if (insideGeoAreaShape == SOME_INSIDE) {
+      return GeoArea.OVERLAPS;
+    }
+
+    final int insideShape = isGeoAreaShapeInsideShape(geoShape);
+    if (insideShape == SOME_INSIDE) {
+      return GeoArea.OVERLAPS;
+    }
+
+    if (insideGeoAreaShape == ALL_INSIDE && insideShape==ALL_INSIDE) {
+      return GeoArea.OVERLAPS;
+    }
+
+    if (intersects(geoShape)){
+      return  GeoArea.OVERLAPS;
+    }
+
+    if (insideGeoAreaShape == ALL_INSIDE) {
+      return GeoArea.WITHIN;
+    }
+
+    if (insideShape==ALL_INSIDE) {
+      return GeoArea.CONTAINS;
+    }
+
+    return GeoArea.DISJOINT;
+  }
+}
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseBBox.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseBBox.java
index ba92cc2242..59562beca8 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseBBox.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseBBox.java
@@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.internal
  */
-abstract class GeoBaseBBox extends GeoBaseMembershipShape implements GeoBBox {
+abstract class GeoBaseBBox extends GeoBaseAreaShape implements GeoBBox {
 
   /** Construct, given planet model.
    *@param planetModel is the planet model.
@@ -31,42 +31,6 @@ abstract class GeoBaseBBox extends GeoBaseMembershipShape implements GeoBBox {
     super(planetModel);
   }
 
-  // Signals for relationship of edge points to shape
-  
-  /** All edgepoints inside shape */
-  protected final static int ALL_INSIDE = 0;
-  /** Some edgepoints inside shape */
-  protected final static int SOME_INSIDE = 1;
-  /** No edgepoints inside shape */
-  protected final static int NONE_INSIDE = 2;
-
-  /** Determine the relationship between this BBox and the provided
-   * shape's edgepoints.
-   *@param path is the shape.
-   *@return the relationship.
-   */
-  protected int isShapeInsideBBox(final GeoShape path) {
-    final GeoPoint[] pathPoints = path.getEdgePoints();
-    boolean foundOutside = false;
-    boolean foundInside = false;
-    for (GeoPoint p : pathPoints) {
-      if (isWithin(p)) {
-        foundInside = true;
-      } else {
-        foundOutside = true;
-      }
-      if (foundInside && foundOutside) {
-        return SOME_INSIDE;
-      }
-    }
-    if (!foundInside && !foundOutside)
-      return NONE_INSIDE;
-    if (foundInside && !foundOutside)
-      return ALL_INSIDE;
-    if (foundOutside && !foundInside)
-      return NONE_INSIDE;
-    return SOME_INSIDE;
-  }
 
 }
 
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseDistanceShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseDistanceShape.java
index 82e811ad6b..d7be2ab1a8 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseDistanceShape.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBaseDistanceShape.java
@@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.experimental
  */
-public abstract class GeoBaseDistanceShape extends GeoBaseMembershipShape implements GeoDistanceShape {
+public abstract class GeoBaseDistanceShape extends GeoBaseAreaShape implements GeoDistanceShape {
 
   /** Constructor.
    *@param planetModel is the planet model to use.
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePolygon.java
index 08c0634cef..7fe8c98d04 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoBasePolygon.java
@@ -21,7 +21,7 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.internal
  */
-abstract class GeoBasePolygon extends GeoBaseMembershipShape implements GeoPolygon {
+abstract class GeoBasePolygon extends GeoBaseAreaShape implements GeoPolygon {
 
   /** Constructor.
    *@param planetModel is the planet model to use.
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
index 7f8f649d1f..6bd66d4158 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoComplexPolygon.java
@@ -369,6 +369,32 @@ class GeoComplexPolygon extends GeoBasePolygon {
     return true;
   }
 
+  @Override
+  public boolean intersects(GeoShape geoShape) {
+    // Create the intersector
+    final EdgeIterator intersector = new IntersectorShapeIterator(geoShape);
+    // First, compute the bounds for the the plane
+    final XYZBounds xyzBounds = new XYZBounds();
+    geoShape.getBounds(xyzBounds);
+
+    // Figure out which tree likely works best
+    final double xDelta = xyzBounds.getMaximumX() - xyzBounds.getMinimumX();
+    final double yDelta = xyzBounds.getMaximumY() - xyzBounds.getMinimumY();
+    final double zDelta = xyzBounds.getMaximumZ() - xyzBounds.getMinimumZ();
+    // Select the smallest range
+    if (xDelta <= yDelta && xDelta <= zDelta) {
+      // Drill down in x
+      return !xTree.traverse(intersector, xyzBounds.getMinimumX(), xyzBounds.getMaximumX());
+    } else if (yDelta <= xDelta && yDelta <= zDelta) {
+      // Drill down in y
+      return !yTree.traverse(intersector, xyzBounds.getMinimumY(), xyzBounds.getMaximumY());
+    } else if (zDelta <= xDelta && zDelta <= yDelta) {
+      // Drill down in z
+      return !zTree.traverse(intersector, xyzBounds.getMinimumZ(), xyzBounds.getMaximumZ());
+    }
+    return true;
+  }
+
 
   @Override
   public void getBounds(Bounds bounds) {
@@ -691,7 +717,24 @@ class GeoComplexPolygon extends GeoBasePolygon {
     }
 
   }
-  
+
+
+  /** Assess whether edge intersects the provided shape.
+   */
+  private class IntersectorShapeIterator implements EdgeIterator {
+
+    private final GeoShape shape;
+
+    public IntersectorShapeIterator(final GeoShape shape) {
+      this.shape = shape;
+    }
+
+    @Override
+    public boolean matches(final Edge edge) {
+      return !shape.intersects(edge.plane, edge.notablePoints, edge.startPlane, edge.endPlane);
+    }
+  }
+
   /** Count the number of verifiable edge crossings.
    */
   private class LinearCrossingEdgeIterator implements EdgeIterator {
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java
new file mode 100644
index 0000000000..c7ad81d99d
--- /dev/null
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeAreaShape.java
@@ -0,0 +1,141 @@
+/*
+ * 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;
+
+/**
+ * GeoCompositeAreShape is a set of GeoAreaShape's, treated as a unit.
+ *
+ * @lucene.experimental
+ */
+public class GeoCompositeAreaShape extends GeoCompositeMembershipShape implements GeoAreaShape {
+
+  /**
+   * Add a shape to the composite. It throw an IllegalArgumentException
+   * if the shape is not a GeoAreaShape
+   *@param shape is the shape to add.
+   */
+  @Override
+  public void addShape(final GeoMembershipShape shape) {
+    if (!(shape instanceof GeoAreaShape)){
+      throw new IllegalArgumentException("GeoCompositeAreaShape must be composed of GeoAreaShapes");
+    }
+    shapes.add(shape);
+  }
+
+  public boolean intersects(GeoShape geoShape){
+    for(GeoShape inShape : shapes){
+      if (((GeoAreaShape)inShape).intersects(geoShape)){
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /** All edgepoints inside shape */
+  protected final static int ALL_INSIDE = 0;
+  /** Some edgepoints inside shape */
+  protected final static int SOME_INSIDE = 1;
+  /** No edgepoints inside shape */
+  protected final static int NONE_INSIDE = 2;
+
+  /** Determine the relationship between the GeoAreShape and the
+   * shape's edgepoints.
+   *@param geoShape is the shape.
+   *@return the relationship.
+   */
+  protected  int isShapeInsideGeoAreaShape(final GeoShape geoShape) {
+    boolean foundOutside = false;
+    boolean foundInside = false;
+    for (GeoPoint p : geoShape.getEdgePoints()) {
+      if (isWithin(p)) {
+        foundInside = true;
+      } else {
+        foundOutside = true;
+      }
+      if (foundInside && foundOutside) {
+        return SOME_INSIDE;
+      }
+    }
+    if (!foundInside && !foundOutside)
+      return NONE_INSIDE;
+    if (foundInside && !foundOutside)
+      return ALL_INSIDE;
+    if (foundOutside && !foundInside)
+      return NONE_INSIDE;
+    return SOME_INSIDE;
+  }
+
+  /** Determine the relationship between the GeoAreShape's edgepoints and the
+   * provided shape.
+   *@param geoshape is the shape.
+   *@return the relationship.
+   */
+  protected int isGeoAreaShapeInsideShape(final GeoShape geoshape)  {
+    boolean foundOutside = false;
+    boolean foundInside = false;
+    for (GeoPoint p : getEdgePoints()) {
+      if (geoshape.isWithin(p)) {
+        foundInside = true;
+      } else {
+        foundOutside = true;
+      }
+      if (foundInside && foundOutside) {
+        return SOME_INSIDE;
+      }
+    }
+    if (!foundInside && !foundOutside)
+      return NONE_INSIDE;
+    if (foundInside && !foundOutside)
+      return ALL_INSIDE;
+    if (foundOutside && !foundInside)
+      return NONE_INSIDE;
+    return SOME_INSIDE;
+  }
+
+  @Override
+  public int getRelationship(GeoShape geoShape) {
+    final int insideGeoAreaShape = isShapeInsideGeoAreaShape(geoShape);
+    if (insideGeoAreaShape == SOME_INSIDE) {
+      return GeoArea.OVERLAPS;
+    }
+
+    final int insideShape = isGeoAreaShapeInsideShape(geoShape);
+    if (insideShape == SOME_INSIDE) {
+      return GeoArea.OVERLAPS;
+    }
+
+    if (insideGeoAreaShape == ALL_INSIDE && insideShape==ALL_INSIDE) {
+      return GeoArea.OVERLAPS;
+    }
+
+    if (intersects(geoShape)){
+      return  GeoArea.OVERLAPS;
+    }
+
+    if (insideGeoAreaShape == ALL_INSIDE) {
+      return GeoArea.WITHIN;
+    }
+
+    if (insideShape==ALL_INSIDE) {
+      return GeoArea.CONTAINS;
+    }
+
+    return GeoArea.DISJOINT;
+  }
+
+}
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java
index 6600a343a1..24428f9097 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositeMembershipShape.java
@@ -17,6 +17,7 @@
 package org.apache.lucene.spatial3d.geom;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -57,7 +58,11 @@ public class GeoCompositeMembershipShape implements GeoMembershipShape {
 
   @Override
   public GeoPoint[] getEdgePoints() {
-    return shapes.get(0).getEdgePoints();
+    List<GeoPoint> edgePoints = new ArrayList<>();
+    for(int i=0;i<shapes.size();i++){
+      edgePoints.addAll(Arrays.asList(shapes.get(i).getEdgePoints()));
+    }
+    return edgePoints.toArray(new GeoPoint[edgePoints.size()]);
   }
 
   @Override
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositePolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositePolygon.java
index 920d3fb9db..be373edc39 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositePolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoCompositePolygon.java
@@ -21,7 +21,7 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.experimental
  */
-public class GeoCompositePolygon extends GeoCompositeMembershipShape implements GeoPolygon {
+public class GeoCompositePolygon extends GeoCompositeAreaShape implements GeoPolygon {
   /** Constructor.
    */
   public GeoCompositePolygon() {
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
index e2a4c1ea79..75fecc8498 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConcavePolygon.java
@@ -366,6 +366,27 @@ class GeoConcavePolygon extends GeoBasePolygon {
     return false;
   }
 
+  @Override
+  public boolean intersects(GeoShape geoShape) {
+    for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
+      final SidedPlane edge = edges[edgeIndex];
+      final GeoPoint[] points = this.notableEdgePoints[edgeIndex];
+      if (!isInternalEdges.get(edgeIndex)) {
+        if (geoShape.intersects(edge, points, eitherBounds.get(edge))) {
+          return true;
+        }
+      }
+    }
+    if (holes != null) {
+      for (final GeoPolygon hole : holes) {
+        if (hole.intersects(geoShape)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   /** A membership implementation representing polygon edges that must apply.
    */
   protected static class EitherBound implements Membership {
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
index 6bd0aad9de..bf75e3b497 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoConvexPolygon.java
@@ -356,6 +356,27 @@ class GeoConvexPolygon extends GeoBasePolygon {
     return false;
   }
 
+  @Override
+  public boolean intersects(GeoShape shape) {
+    for (int edgeIndex = 0; edgeIndex < edges.length; edgeIndex++) {
+      final SidedPlane edge = edges[edgeIndex];
+      final GeoPoint[] points = this.notableEdgePoints[edgeIndex];
+      if (!isInternalEdges.get(edgeIndex)) {
+        if (shape.intersects(edge, points, eitherBounds.get(edge))) {
+          return true;
+        }
+      }
+    }
+    if (holes != null) {
+      for (final GeoPolygon hole : holes) {
+        if (hole.intersects(shape)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
   /** A membership implementation representing polygon edges that must apply.
    */
   protected static class EitherBound implements Membership {
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateHorizontalLine.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateHorizontalLine.java
index 927fbdc0f2..9518180221 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateHorizontalLine.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateHorizontalLine.java
@@ -155,6 +155,11 @@ class GeoDegenerateHorizontalLine extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(plane, planePoints, leftPlane, rightPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.addHorizontalPlane(planetModel, latitude, plane, leftPlane, rightPlane)
@@ -164,7 +169,7 @@ class GeoDegenerateHorizontalLine extends GeoBaseBBox {
   @Override
   public int getRelationship(final GeoShape path) {
     //System.err.println("getting relationship between "+this+" and "+path);
-    if (path.intersects(plane, planePoints, leftPlane, rightPlane)) {
+    if (intersects(path)) {
       //System.err.println(" overlaps");
       return OVERLAPS;
     }
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLatitudeZone.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLatitudeZone.java
index ea6bb57345..fc6b234a29 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLatitudeZone.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLatitudeZone.java
@@ -86,6 +86,11 @@ class GeoDegenerateLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(plane, planePoints);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.noLongitudeBound()
@@ -98,7 +103,7 @@ class GeoDegenerateLatitudeZone extends GeoBaseBBox {
     // work with no area endpoints.  So we rely entirely on intersections.
     //System.out.println("Got here! latitude="+latitude+" path="+path);
 
-    if (path.intersects(plane, planePoints)) {
+    if (intersects(path)) {
       return OVERLAPS;
     }
 
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLongitudeSlice.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLongitudeSlice.java
index 09f468cc27..e610ff485e 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLongitudeSlice.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateLongitudeSlice.java
@@ -97,6 +97,11 @@ class GeoDegenerateLongitudeSlice extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(plane, planePoints, boundingPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds
@@ -107,7 +112,7 @@ class GeoDegenerateLongitudeSlice extends GeoBaseBBox {
   @Override
   public int getRelationship(final GeoShape path) {
     // Look for intersections.
-    if (path.intersects(plane, planePoints, boundingPlane))
+    if (intersects(path))
       return OVERLAPS;
 
     if (path.isWithin(interiorPoint))
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePoint.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePoint.java
index ab08579174..2860757200 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePoint.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegeneratePoint.java
@@ -67,6 +67,11 @@ class GeoDegeneratePoint extends GeoPoint implements GeoBBox, GeoCircle {
   }
 
   @Override
+  public boolean intersects(GeoShape geoShape) {
+    return false;
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     bounds.addPoint(this);
   }
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateVerticalLine.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateVerticalLine.java
index dff53b4aa1..c8acf78555 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateVerticalLine.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDegenerateVerticalLine.java
@@ -145,6 +145,11 @@ public class GeoDegenerateVerticalLine extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(plane, planePoints, boundingPlane, topPlane, bottomPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.addVerticalPlane(planetModel, longitude, plane, boundingPlane, topPlane, bottomPlane)
@@ -154,7 +159,7 @@ public class GeoDegenerateVerticalLine extends GeoBaseBBox {
   @Override
   public int getRelationship(final GeoShape path) {
     //System.err.println(this+" relationship to "+path);
-    if (path.intersects(plane, planePoints, boundingPlane, topPlane, bottomPlane)) {
+    if (intersects(path)) {
       //System.err.println(" overlaps");
       return OVERLAPS;
     }
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDistanceShape.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDistanceShape.java
index ebd433f219..c49ed30b95 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDistanceShape.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoDistanceShape.java
@@ -22,7 +22,7 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.experimental
  */
-public interface GeoDistanceShape extends GeoMembershipShape, GeoDistance {
+public interface GeoDistanceShape extends GeoAreaShape, GeoDistance {
 
   /**
    * Compute a bound based on a provided distance measure.
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLatitudeZone.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLatitudeZone.java
index 14618f53f9..716efb509f 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLatitudeZone.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLatitudeZone.java
@@ -120,6 +120,12 @@ class GeoLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(topPlane, planePoints, bottomPlane) ||
+        geoShape.intersects(bottomPlane, planePoints, topPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.noLongitudeBound()
@@ -128,46 +134,6 @@ class GeoLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE)
-      return OVERLAPS;
-
-    final boolean topBoundaryInsideShape = path.isWithin(topBoundaryPoint);
-    final boolean bottomBoundaryInsideShape = path.isWithin(bottomBoundaryPoint);
-
-    if (topBoundaryInsideShape && !bottomBoundaryInsideShape ||
-        !topBoundaryInsideShape && bottomBoundaryInsideShape)
-      return OVERLAPS;
-
-    final boolean insideShape = topBoundaryInsideShape && bottomBoundaryInsideShape;
-
-    if (insideRectangle == ALL_INSIDE && insideShape)
-      return OVERLAPS;
-
-    // Second, the shortcut of seeing whether endpoints are in/out is not going to
-    // work with no area endpoints.  So we rely entirely on intersections.
-
-    if (path.intersects(topPlane, planePoints, bottomPlane) ||
-        path.intersects(bottomPlane, planePoints, topPlane))
-      return OVERLAPS;
-
-    // There is another case for latitude zones only.  This is when the boundaries of the shape all fit
-    // within the zone, but the shape includes areas outside the zone crossing a pole.
-    // In this case, the above "overlaps" check is insufficient.  We also need to check a point on either boundary
-    // whether it is within the shape.  If both such points are within, then CONTAINS is the right answer.  If
-    // one such point is within, then OVERLAPS is the right answer.
-
-    if (insideShape)
-      return CONTAINS;
-
-    if (insideRectangle == ALL_INSIDE)
-      return WITHIN;
-
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane);
     final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane);
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLongitudeSlice.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLongitudeSlice.java
index 294d4e831f..fc21a2186f 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLongitudeSlice.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoLongitudeSlice.java
@@ -128,6 +128,12 @@ class GeoLongitudeSlice extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(leftPlane, planePoints, rightPlane) ||
+        geoShape.intersects(rightPlane, planePoints, leftPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds
@@ -139,33 +145,6 @@ class GeoLongitudeSlice extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE)
-      return OVERLAPS;
-
-    final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
-
-    if (insideRectangle == ALL_INSIDE && insideShape)
-      return OVERLAPS;
-
-    if (path.intersects(leftPlane, planePoints, rightPlane) ||
-        path.intersects(rightPlane, planePoints, leftPlane)) {
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      return CONTAINS;
-    }
-
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane);
     final double rightDistance = distanceStyle.computeDistance(planetModel, rightPlane, x,y,z, leftPlane);
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthLatitudeZone.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthLatitudeZone.java
index 2acfc4ed8e..7c3ea96b75 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthLatitudeZone.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthLatitudeZone.java
@@ -99,6 +99,12 @@ class GeoNorthLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return
+        geoShape.intersects(bottomPlane, planePoints);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds
@@ -106,38 +112,6 @@ class GeoNorthLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE)
-      return OVERLAPS;
-
-    final boolean insideShape = path.isWithin(bottomBoundaryPoint);
-
-    if (insideRectangle == ALL_INSIDE && insideShape)
-      return OVERLAPS;
-
-    // Second, the shortcut of seeing whether endpoints are in/out is not going to
-    // work with no area endpoints.  So we rely entirely on intersections.
-
-    if (path.intersects(bottomPlane, planePoints))
-      return OVERLAPS;
-
-    // There is another case for latitude zones only.  This is when the boundaries of the shape all fit
-    // within the zone, but the shape includes areas outside the zone crossing a pole.
-    // In this case, the above "overlaps" check is insufficient.  We also need to check a point on either boundary
-    // whether it is within the shape.  If both such points are within, then CONTAINS is the right answer.  If
-    // one such point is within, then OVERLAPS is the right answer.
-
-    if (insideShape)
-      return CONTAINS;
-
-    if (insideRectangle == ALL_INSIDE)
-      return WITHIN;
-
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     return distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z);
   }
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthRectangle.java
index 27422d279e..2a1b7c5857 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthRectangle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoNorthRectangle.java
@@ -175,6 +175,14 @@ class GeoNorthRectangle extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return
+        geoShape.intersects(bottomPlane, bottomPlanePoints, leftPlane, rightPlane) ||
+            geoShape.intersects(leftPlane, leftPlanePoints, rightPlane, bottomPlane) ||
+            geoShape.intersects(rightPlane, rightPlanePoints, leftPlane, bottomPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds
@@ -186,43 +194,6 @@ class GeoNorthRectangle extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    //System.err.println(this+" getrelationship with "+path);
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE) {
-      //System.err.println(" some inside");
-      return OVERLAPS;
-    }
-
-    final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
-
-    if (insideRectangle == ALL_INSIDE && insideShape) {
-      //System.err.println(" inside of each other");
-      return OVERLAPS;
-    }
-
-    if (
-        path.intersects(bottomPlane, bottomPlanePoints, leftPlane, rightPlane) ||
-            path.intersects(leftPlane, leftPlanePoints, bottomPlane, rightPlane) ||
-            path.intersects(rightPlane, rightPlanePoints, leftPlane, bottomPlane)) {
-      //System.err.println(" edges intersect");
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      //System.err.println(" shape inside rectangle");
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      //System.err.println(" shape contains rectangle");
-      return CONTAINS;
-    }
-    //System.err.println(" disjoint");
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, leftPlane, rightPlane);
     final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane, bottomPlane);
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygon.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygon.java
index 742bdf808d..9125d14fc9 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygon.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoPolygon.java
@@ -21,6 +21,6 @@ package org.apache.lucene.spatial3d.geom;
  *
  * @lucene.experimental
  */
-public interface GeoPolygon extends GeoMembershipShape {
+public interface GeoPolygon extends GeoAreaShape {
 
 }
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java
index 3d0a97d8ae..faf29528a2 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoRectangle.java
@@ -197,6 +197,14 @@ class GeoRectangle extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(GeoShape geoShape) {
+    return geoShape.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) ||
+        geoShape.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) ||
+        geoShape.intersects(leftPlane, leftPlanePoints, rightPlane, topPlane, bottomPlane) ||
+        geoShape.intersects(rightPlane,  rightPlanePoints, leftPlane, topPlane, bottomPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.addHorizontalPlane(planetModel, topLat, topPlane, bottomPlane, leftPlane, rightPlane)
@@ -208,43 +216,6 @@ class GeoRectangle extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    //System.err.println(this+" getrelationship with "+path);
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE) {
-      //System.err.println(" some inside");
-      return OVERLAPS;
-    }
-
-    final boolean insideShape = path.isWithin(ULHC);
-
-    if (insideRectangle == ALL_INSIDE && insideShape) {
-      //System.err.println(" inside of each other");
-      return OVERLAPS;
-    }
-
-    if (path.intersects(topPlane, topPlanePoints, bottomPlane, leftPlane, rightPlane) ||
-        path.intersects(bottomPlane, bottomPlanePoints, topPlane, leftPlane, rightPlane) ||
-        path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane, rightPlane) ||
-        path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane, bottomPlane)) {
-      //System.err.println(" edges intersect");
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      //System.err.println(" shape inside rectangle");
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      //System.err.println(" shape contains rectangle");
-      return CONTAINS;
-    }
-    //System.err.println(" disjoint");
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, leftPlane, rightPlane);
     final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane, leftPlane, rightPlane);
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java
index c915c24f75..acb7cb0fba 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthLatitudeZone.java
@@ -102,6 +102,11 @@ class GeoSouthLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(topPlane, planePoints);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds
@@ -109,38 +114,6 @@ class GeoSouthLatitudeZone extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE)
-      return OVERLAPS;
-
-    final boolean insideShape = path.isWithin(topBoundaryPoint);
-
-    if (insideRectangle == ALL_INSIDE && insideShape)
-      return OVERLAPS;
-
-    // Second, the shortcut of seeing whether endpoints are in/out is not going to
-    // work with no area endpoints.  So we rely entirely on intersections.
-
-    if (path.intersects(topPlane, planePoints))
-      return OVERLAPS;
-
-    // There is another case for latitude zones only.  This is when the boundaries of the shape all fit
-    // within the zone, but the shape includes areas outside the zone crossing a pole.
-    // In this case, the above "overlaps" check is insufficient.  We also need to check a point on either boundary
-    // whether it is within the shape.  If both such points are within, then CONTAINS is the right answer.  If
-    // one such point is within, then OVERLAPS is the right answer.
-
-    if (insideShape)
-      return CONTAINS;
-
-    if (insideRectangle == ALL_INSIDE)
-      return WITHIN;
-
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     return distanceStyle.computeDistance(planetModel, topPlane, x,y,z);
   }
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java
index ed380e13d9..f75fc8c778 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoSouthRectangle.java
@@ -173,6 +173,13 @@ class GeoSouthRectangle extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) ||
+        geoShape.intersects(leftPlane, leftPlanePoints, rightPlane, topPlane) ||
+        geoShape.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds
@@ -184,42 +191,6 @@ class GeoSouthRectangle extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    //System.err.println(this+" getrelationship with "+path);
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE) {
-      //System.err.println(" some inside");
-      return OVERLAPS;
-    }
-
-    final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE);
-
-    if (insideRectangle == ALL_INSIDE && insideShape) {
-      //System.err.println(" inside of each other");
-      return OVERLAPS;
-    }
-
-    if (path.intersects(topPlane, topPlanePoints, leftPlane, rightPlane) ||
-        path.intersects(leftPlane, leftPlanePoints, topPlane, rightPlane) ||
-        path.intersects(rightPlane, rightPlanePoints, leftPlane, topPlane)) {
-      //System.err.println(" edges intersect");
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      //System.err.println(" shape inside rectangle");
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      //System.err.println(" shape contains rectangle");
-      return CONTAINS;
-    }
-    //System.err.println(" disjoint");
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, leftPlane, rightPlane);
     final double leftDistance = distanceStyle.computeDistance(planetModel, leftPlane, x,y,z, rightPlane, topPlane);
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java
index 06bef0057e..ec83edf10b 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardCircle.java
@@ -140,6 +140,14 @@ class GeoStandardCircle extends GeoBaseCircle {
   }
 
   @Override
+  public boolean intersects(GeoShape geoShape) {
+    if (circlePlane == null) {
+      return false;
+    }
+    return geoShape.intersects(circlePlane, circlePoints);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     if (circlePlane == null) {
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
index 51c507829b..b3b6594dbf 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoStandardPath.java
@@ -289,6 +289,23 @@ class GeoStandardPath extends GeoBasePath {
   }
 
   @Override
+  public boolean intersects(GeoShape geoShape) {
+    for (final SegmentEndpoint pathPoint : endPoints) {
+      if (pathPoint.intersects(geoShape)) {
+        return true;
+      }
+    }
+
+    for (final PathSegment pathSegment : segments) {
+      if (pathSegment.intersects(geoShape)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     // For building bounds, order matters.  We want to traverse
@@ -549,6 +566,17 @@ class GeoStandardPath extends GeoBasePath {
       return circlePlane.intersects(planetModel, p, notablePoints, this.notablePoints, bounds, this.cutoffPlanes);
     }
 
+    /** Determine if this endpoint intersects a GeoShape.
+     *@param geoShape is the GeoShape.
+     *@return true if there is shape intersect this endpoint.
+     */
+    public boolean intersects(final GeoShape geoShape) {
+      //System.err.println("  looking for intersection between plane "+p+" and circle "+circlePlane+" on proper side of "+cutoffPlanes+" within "+bounds);
+      if (circlePlane == null)
+        return false;
+      return geoShape.intersects(circlePlane, this.notablePoints, this.cutoffPlanes);
+    }
+
     /** Get the bounds for a segment endpoint.
      *@param planetModel is the planet model.
      *@param bounds are the bounds to be modified.
@@ -799,6 +827,15 @@ class GeoStandardPath extends GeoBasePath {
           lowerConnectingPlane.intersects(planetModel, p, notablePoints, lowerConnectingPlanePoints, bounds, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
     }
 
+    /** Determine if this endpoint intersects a specified GeoShape.
+     *@param geoShape is the GeoShape.
+     *@return true if there GeoShape intersects this endpoint.
+     */
+    public boolean intersects(final GeoShape geoShape) {
+      return geoShape.intersects(upperConnectingPlane, upperConnectingPlanePoints, lowerConnectingPlane, startCutoffPlane, endCutoffPlane) ||
+          geoShape.intersects(lowerConnectingPlane, lowerConnectingPlanePoints, upperConnectingPlane, startCutoffPlane, endCutoffPlane);
+    }
+
     /** Get the bounds for a segment endpoint.
      *@param planetModel is the planet model.
      *@param bounds are the bounds to be modified.
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java
index fe934731c2..90d35eef97 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideDegenerateHorizontalLine.java
@@ -166,6 +166,13 @@ class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+    // requires crossing into the right part of the other.  So intersection can ignore the left/right bounds.
+    return geoShape.intersects(plane, planePoints, eitherBound);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.isWide()
@@ -176,7 +183,7 @@ class GeoWideDegenerateHorizontalLine extends GeoBaseBBox {
 
   @Override
   public int getRelationship(final GeoShape path) {
-    if (path.intersects(plane, planePoints, eitherBound)) {
+    if (intersects(path)) {
       return OVERLAPS;
     }
 
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java
index 705d961eae..e15c27518c 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideLongitudeSlice.java
@@ -140,6 +140,14 @@ class GeoWideLongitudeSlice extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+    // requires crossing into the right part of the other.  So intersection can ignore the left/right bounds.
+    return geoShape.intersects(leftPlane, planePoints) ||
+        geoShape.intersects(rightPlane, planePoints);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.isWide()
@@ -151,30 +159,6 @@ class GeoWideLongitudeSlice extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE)
-      return OVERLAPS;
-
-    final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
-
-    if (insideRectangle == ALL_INSIDE && insideShape)
-      return OVERLAPS;
-
-    if (path.intersects(leftPlane, planePoints) ||
-        path.intersects(rightPlane, planePoints))
-      return OVERLAPS;
-
-    if (insideRectangle == ALL_INSIDE)
-      return WITHIN;
-
-    if (insideShape)
-      return CONTAINS;
-
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
     // unbounded distance to both the left and the right and only take the minimum of the two.
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java
index 849d4e9bfd..5672003b9a 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideNorthRectangle.java
@@ -177,6 +177,16 @@ class GeoWideNorthRectangle extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    // Right and left bounds are essentially independent hemispheres; crossing into the wrong part of one
+    // requires crossing into the right part of the other.  So intersection can ignore the left/right bounds.
+    return
+        geoShape.intersects(bottomPlane, bottomPlanePoints, eitherBound) ||
+            geoShape.intersects(leftPlane, leftPlanePoints, bottomPlane) ||
+            geoShape.intersects(rightPlane, rightPlanePoints, bottomPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.isWide()
@@ -188,44 +198,6 @@ class GeoWideNorthRectangle extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    //System.err.println(this+" comparing to "+path);
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE) {
-      //System.err.println(" some inside");
-      return OVERLAPS;
-    }
-
-    final boolean insideShape = path.isWithin(planetModel.NORTH_POLE);
-
-    if (insideRectangle == ALL_INSIDE && insideShape) {
-      //System.err.println(" both inside each other");
-      return OVERLAPS;
-    }
-
-    if (
-        path.intersects(bottomPlane, bottomPlanePoints, eitherBound) ||
-            path.intersects(leftPlane, leftPlanePoints, bottomPlane) ||
-            path.intersects(rightPlane, rightPlanePoints, bottomPlane)) {
-      //System.err.println(" edges intersect");
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      //System.err.println(" shape inside rectangle");
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      //System.err.println(" rectangle inside shape");
-      return CONTAINS;
-    }
-
-    //System.err.println(" disjoint");
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, eitherBound);
     // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java
index 53c8c59fb6..43b9bc7462 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideRectangle.java
@@ -205,6 +205,14 @@ class GeoWideRectangle extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(topPlane, topPlanePoints, bottomPlane, eitherBound) ||
+        geoShape.intersects(bottomPlane, bottomPlanePoints, topPlane, eitherBound) ||
+        geoShape.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane) ||
+        geoShape.intersects(rightPlane, rightPlanePoints, topPlane, bottomPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.isWide()
@@ -217,44 +225,6 @@ class GeoWideRectangle extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    //System.err.println(this+" comparing to "+path);
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE) {
-      //System.err.println(" some inside");
-      return OVERLAPS;
-    }
-
-    final boolean insideShape = path.isWithin(ULHC);
-
-    if (insideRectangle == ALL_INSIDE && insideShape) {
-      //System.err.println(" both inside each other");
-      return OVERLAPS;
-    }
-
-    if (path.intersects(topPlane, topPlanePoints, bottomPlane, eitherBound) ||
-        path.intersects(bottomPlane, bottomPlanePoints, topPlane, eitherBound) ||
-        path.intersects(leftPlane, leftPlanePoints, topPlane, bottomPlane) ||
-        path.intersects(rightPlane, rightPlanePoints, topPlane, bottomPlane)) {
-      //System.err.println(" edges intersect");
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      //System.err.println(" shape inside rectangle");
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      //System.err.println(" rectangle inside shape");
-      return CONTAINS;
-    }
-
-    //System.err.println(" disjoint");
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, bottomPlane, eitherBound);
     final double bottomDistance = distanceStyle.computeDistance(planetModel, bottomPlane, x,y,z, topPlane, eitherBound);
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java
index 29d89c50e5..ea60322f78 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWideSouthRectangle.java
@@ -176,6 +176,13 @@ class GeoWideSouthRectangle extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return geoShape.intersects(topPlane, topPlanePoints, eitherBound) ||
+        geoShape.intersects(leftPlane, leftPlanePoints, topPlane) ||
+        geoShape.intersects(rightPlane, rightPlanePoints, topPlane);
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     bounds.isWide()
@@ -187,43 +194,6 @@ class GeoWideSouthRectangle extends GeoBaseBBox {
   }
 
   @Override
-  public int getRelationship(final GeoShape path) {
-    //System.err.println(this+" comparing to "+path);
-    final int insideRectangle = isShapeInsideBBox(path);
-    if (insideRectangle == SOME_INSIDE) {
-      //System.err.println(" some inside");
-      return OVERLAPS;
-    }
-
-    final boolean insideShape = path.isWithin(planetModel.SOUTH_POLE);
-
-    if (insideRectangle == ALL_INSIDE && insideShape) {
-      //System.err.println(" both inside each other");
-      return OVERLAPS;
-    }
-
-    if (path.intersects(topPlane, topPlanePoints, eitherBound) ||
-        path.intersects(leftPlane, leftPlanePoints, topPlane) ||
-        path.intersects(rightPlane, rightPlanePoints, topPlane)) {
-      //System.err.println(" edges intersect");
-      return OVERLAPS;
-    }
-
-    if (insideRectangle == ALL_INSIDE) {
-      //System.err.println(" shape inside rectangle");
-      return WITHIN;
-    }
-
-    if (insideShape) {
-      //System.err.println(" rectangle inside shape");
-      return CONTAINS;
-    }
-
-    //System.err.println(" disjoint");
-    return DISJOINT;
-  }
-
-  @Override
   protected double outsideDistance(final DistanceStyle distanceStyle, final double x, final double y, final double z) {
     final double topDistance = distanceStyle.computeDistance(planetModel, topPlane, x,y,z, eitherBound);
     // Because the rectangle exceeds 180 degrees, it is safe to compute the horizontally 
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java
index 17352b940a..ebe11dbb1e 100755
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/geom/GeoWorld.java
@@ -67,6 +67,11 @@ class GeoWorld extends GeoBaseBBox {
   }
 
   @Override
+  public boolean intersects(final GeoShape geoShape) {
+    return false;
+  }
+
+  @Override
   public void getBounds(Bounds bounds) {
     super.getBounds(bounds);
     // Unnecessary
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/CompositeGeoPolygonRelationshipsTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/CompositeGeoPolygonRelationshipsTest.java
new file mode 100644
index 0000000000..f7d660660c
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/CompositeGeoPolygonRelationshipsTest.java
@@ -0,0 +1,842 @@
+/*
+ * 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.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Check relationship between polygon and GeoShapes of composite polygons. Normally we construct
+ * the composite polygon (when possible) and the complex one.
+ */
+public class CompositeGeoPolygonRelationshipsTest {
+
+  @Test
+  public void testGeoCompositePolygon1() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    GeoPolygon polConvex = buildGeoPolygon(20.0, -60.4,
+        20.1, -60.4,
+        20.1, -60.3,
+        20.0, -60.3,
+        20.0, -60.3);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -60.4,
+        20.1, -60.4,
+        20.1, -60.3,
+        20.0, -60.3);
+
+    //convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.WITHIN, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testGeoCompositePolygon2() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    //POLYGON ((20.9 -60.8, 21.1 -60.8, 21.1 -60.6, 20.9  -60.6,20.9 -60.8))
+    GeoPolygon polConvex = buildGeoPolygon(20.9, -60.8,
+        21.1, -60.8,
+        21.1, -60.6,
+        20.9, -60.6,
+        20.9, -60.6);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -60.8,
+        21.1, -60.8,
+        21.1, -60.6,
+        20.9, -60.6);
+
+    //convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.WITHIN, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testGeoCompositePolygon3() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    //POLYGON ((20.9 -61.1, 21.1 -61.1, 21.1 -60.9, 20.9  -60.9,20.9 -61.1))
+    GeoPolygon polConvex = buildGeoPolygon(20.9, -61.1,
+        21.1, -61.1,
+        21.1, -60.9,
+        20.9, -60.9,
+        20.9, -60.9);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -61.1,
+        21.1, -61.1,
+        21.1, -60.9,
+        20.9, -60.9);
+
+    //convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testGeoCompositePolygon4() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    //POLYGON ((20.9 -61.4, 21.1 -61.4, 21.1 -61.2, 20.9  -61.2,20.9 -61.4))
+    GeoPolygon polConvex = buildGeoPolygon(20.9, -61.4,
+        21.1, -61.4,
+        21.1, -61.2,
+        20.9, -61.2,
+        20.9, -61.2);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -61.4,
+        21.1, -61.4,
+        21.1, -61.2,
+        20.9, -61.2);
+
+    //convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.WITHIN, rel);
+
+  }
+
+  @Test
+  public void testGeoCompositePolygon5() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originaConvexlPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    //POLYGON ((19 -62, 23 -62, 23 -60, 19 -60,19 -62))
+    GeoPolygon polConvex = buildGeoPolygon(19, -62,
+        23, -62,
+        23, -60,
+        19, -60,
+        19, -60);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(19, -62,
+        23, -62,
+        23, -60,
+        19, -60);
+
+    //convex
+    int rel = originaConvexlPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(originaConvexlPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = originaConvexlPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originaConvexlPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testGeoCompositePolygon6() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    //POLYGON ((19 -62, 24 -62, 24 -60, 19 -60,19 -62))
+    GeoPolygon polConvex = buildGeoPolygon(19, -62,
+        24, -62,
+        24, -60,
+        19, -60,
+        19, -60);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(19, -62,
+        24, -62,
+        24, -60,
+        19, -60);
+
+    //convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.WITHIN, rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.CONTAINS, rel);
+  }
+
+
+
+  @Test
+  public void testGeoCompositePolygon7() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 21 -61,19.845091 -60.452631))
+    GeoPolygon originalConvexPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+    //POLYGON ((19.845091 -60.452631, 21 -61,22.820804 -60.257713,23.207901 -61.453298, 20.119948 -61.655652, 19.845091 -60.452631))
+    GeoPolygon originalConcavePol = buildGeoPolygon(19.84509, -60.452631,
+        21, -61,
+        22.820804, -60.257713,
+        23.207901, -61.453298,
+        20.119948, -61.655652);
+
+    //POLYGON ((20.2 -61.4, 20.5 -61.4, 20.5 -60.8, 20.2 -60.8,20.2  -61.4))
+    GeoPolygon polConvex = buildGeoPolygon(20.2, -61.4,
+        20.5, -61.4,
+        20.5, -60.8,
+        20.2, -60.8,
+        20.2, -60.8);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.2, -61.4,
+        20.5, -61.4,
+        20.5, -60.8,
+        20.2, -60.8);
+
+    //convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testGeoCompositePolygon8() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713,21 -61, 19.845091 -60.452631))
+    GeoPolygon originalPol = buildGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713,
+        21, -61);
+
+
+    GeoShape shape  = getInsideCompositeShape();
+
+    int rel = originalPol.getRelationship(shape);
+    assertEquals(GeoArea.WITHIN, rel);
+
+  }
+
+
+  @Test
+  public void testGeoPolygonPole1() {
+    //POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
+    GeoPolygon compositePol=  getCompositePolygon();
+    GeoPolygon complexPol=  getComplexPolygon();
+
+    //POLYGON ((20.9 -61.4, 21.1 -61.4, 21.1 -61.2, 20.9  -61.2,20.9 -61.4))
+    GeoPolygon polConvex = buildGeoPolygon(20.9, -61.4,
+        21.1, -61.4,
+        21.1, -61.2,
+        20.9, -61.2,
+        20.9, -61.2);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.9, -61.4,
+        21.1, -61.4,
+        21.1, -61.2,
+        20.9, -61.2);
+
+    int rel = compositePol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(compositePol);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    rel = compositePol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConcave.getRelationship(compositePol);
+    assertEquals(GeoArea.WITHIN, rel);
+
+    rel = complexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(complexPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    rel = complexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConcave.getRelationship(complexPol);
+    assertEquals(GeoArea.WITHIN, rel);
+  }
+
+  @Test
+  public void testGeoPolygonPole2() {
+    //POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
+    GeoPolygon compositePol=  getCompositePolygon();
+    GeoPolygon complexPol=  getComplexPolygon();
+
+    //POLYGON((-1 81, -1 79,1 79,1 81, -1 81))
+    GeoPolygon polConvex = buildGeoPolygon(-1,81,
+        -1,79,
+        1,79,
+        1,81,
+        1,81);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-1,81,
+        -1,79,
+        1,79,
+        1,81);
+
+    int rel = compositePol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(compositePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = compositePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(compositePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = complexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(complexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = complexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(complexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testGeoPolygonPole3() {
+    //POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
+    GeoPolygon compositePol=  getCompositePolygon();
+    GeoPolygon complexPol=  getComplexPolygon();
+
+    //POLYGON((-1 86, -1 84,1 84,1 86, -1 86))
+    GeoPolygon polConvex = buildGeoPolygon(-1,86,
+        -1,84,
+        1,84,
+        1,86,
+        1,86);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-1,86,
+        -1,84,
+        1,84,
+        1,86);
+
+    int rel = compositePol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(compositePol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = compositePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(compositePol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    rel = complexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(complexPol);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+    rel = complexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(complexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+  }
+
+  @Test
+  public void testMultiPolygon1() {
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPolygon multiPol=  getMultiPolygon();
+
+    //POLYGON((-145.8555 -5.13, -145.8540 -5.13, -145.8540 -5.12, -145.8555 -5.12, -145.8555 -5.13))
+    GeoPolygon polConvex = buildGeoPolygon(-145.8555, -5.13,
+        -145.8540, -5.13,
+        -145.8540, -5.12,
+        -145.8555, -5.12,
+        -145.8555, -5.12);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
+        -145.8540, -5.13,
+        -145.8540, -5.12,
+        -145.8555, -5.12);
+
+    int rel = multiPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConvex.getRelationship(multiPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+    assertEquals(false,multiPol.intersects(polConvex));
+    assertEquals(false,polConvex.intersects(multiPol));
+
+    rel = multiPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConcave.getRelationship(multiPol);
+    assertEquals(GeoArea.WITHIN, rel);
+    assertEquals(false,multiPol.intersects(polConcave));
+    assertEquals(false,polConcave.intersects(multiPol));
+  }
+
+  @Test
+  public void testMultiPolygon2() {
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPolygon multiPol=  getMultiPolygon();
+
+    //POLYGON((-145.8555 -5.13, -145.85 -5.13, -145.85 -5.12, -145.8555 -5.12, -145.8555 -5.13))
+    GeoPolygon polConvex = buildGeoPolygon(-145.8555, -5.13,
+        -145.85, -5.13,
+        -145.85, -5.12,
+        -145.8555, -5.12,
+        -145.8555, -5.12);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
+        -145.85, -5.13,
+        -145.85, -5.12,
+        -145.8555, -5.12);
+
+    int rel = multiPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(multiPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    assertEquals(true,multiPol.intersects(polConvex));
+    assertEquals(true,polConvex.intersects(multiPol));
+
+    rel = multiPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(multiPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    assertEquals(true,multiPol.intersects(polConcave));
+    assertEquals(true,polConcave.intersects(multiPol));
+  }
+
+  @Test
+  public void testMultiPolygon3() {
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPolygon multiPol=  getMultiPolygon();
+
+    //POLYGON((-146 -5.18, -145.854 -5.18, -145.854 -5.11, -146 -5.11, -146 -5.18))
+    //Case overlapping one of the polygons so intersection is false!
+    GeoPolygon polConvex = buildGeoPolygon(-146, -5.18,
+        -145.854, -5.18,
+        -145.854, -5.11,
+        -146, -5.11,
+        -146, -5.11);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-146, -5.18,
+        -145.854, -5.18,
+        -145.854, -5.11,
+        -146, -5.11);
+
+    int rel = multiPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConvex.getRelationship(multiPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    assertEquals(false,multiPol.intersects(polConvex));
+    assertEquals(false,polConvex.intersects(multiPol));
+
+    rel = multiPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(multiPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    assertEquals(false,multiPol.intersects(polConcave));
+    assertEquals(false,polConcave.intersects(multiPol));
+
+  }
+
+  @Test
+  public void testMultiPolygon4() {
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPolygon multiPol=  getMultiPolygon();
+
+    //POLYGON((-145.88 -5.13, -145.87 -5.13, -145.87 -5.12, -145.88 -5.12, -145.88 -5.13))
+    GeoPolygon polConvex = buildGeoPolygon(-145.88, -5.13,
+        -145.87, -5.13,
+        -145.87, -5.12,
+        -145.88, -5.12,
+        -145.88, -5.12);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-145.88, -5.13,
+        -145.87, -5.13,
+        -145.87, -5.12,
+        -145.88, -5.12);
+
+    int rel = multiPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = polConvex.getRelationship(multiPol);
+    assertEquals(GeoArea.CONTAINS, rel);
+    assertEquals(false,multiPol.intersects(polConvex));
+    assertEquals(false,polConvex.intersects(multiPol));
+
+    rel = multiPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = polConcave.getRelationship(multiPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    assertEquals(false,multiPol.intersects(polConcave));
+    assertEquals(false,polConcave.intersects(multiPol));
+  }
+
+  @Test
+  public void testMultiPolygon5() {
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPolygon multiPol=  getMultiPolygon();
+
+    //POLYGON((-146 -5.18, -145 -5.18, -145 -5.11, -146 -5.11, -146 -5.18))
+    GeoPolygon polConvex = buildGeoPolygon(-146, -5.18,
+        -145, -5.18,
+        -145, -5.11,
+        -146, -5.11,
+        -146, -5.11);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-146, -5.18,
+        -145, -5.18,
+        -145, -5.11,
+        -146, -5.11);
+
+    int rel = multiPol.getRelationship(polConvex);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = polConvex.getRelationship(multiPol);
+    assertEquals(GeoArea.WITHIN, rel);
+    assertEquals(false,multiPol.intersects(polConvex));
+
+    rel = multiPol.getRelationship(polConcave);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = polConcave.getRelationship(multiPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+    assertEquals(false,multiPol.intersects(polConcave));
+  }
+
+  private GeoPolygon buildGeoPolygon(double lon1,double lat1,
+                                     double lon2,double lat2,
+                                     double lon3,double lat3,
+                                     double lon4,double lat4,
+                                     double lon5,double lat5)
+  {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat5), Math.toRadians(lon5));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    points.add(point5);
+    return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
+  }
+
+  private GeoPolygon buildConcaveGeoPolygon(double lon1,double lat1,
+                                            double lon2,double lat2,
+                                            double lon3,double lat3,
+                                            double lon4,double lat4)
+  {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    return GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE,points);
+  }
+
+  private GeoPolygon getCompositePolygon(){
+    //POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(0));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(45));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(90));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(135));
+    GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(180));
+    GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(-135));
+    GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(-90));
+    GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(-45));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    points.add(point5);
+    points.add(point6);
+    points.add(point7);
+    points.add(point8);
+    return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
+  }
+
+  private GeoPolygon getComplexPolygon(){
+    //POLYGON((0 80, 45 85 ,90 80,135 85,180 80, -135 85, -90 80, -45 85,0 80))
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(0));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(45));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(90));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(135));
+    GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(180));
+    GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(-135));
+    GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(80), Math.toRadians(-90));
+    GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(85), Math.toRadians(-45));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    points.add(point5);
+    points.add(point6);
+    points.add(point7);
+    points.add(point8);
+    GeoPolygonFactory.PolygonDescription pd = new GeoPolygonFactory.PolygonDescription(points);
+    return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, Collections.singletonList(pd));
+  }
+
+  private GeoPolygon getMultiPolygon(){
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17543698881), Math.toRadians(-145.790967486));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11348060995), Math.toRadians(-145.790854979));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11339421216), Math.toRadians(-145.853073512));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17535061936), Math.toRadians(-145.853192037));
+    final List<GeoPoint> points1 = new ArrayList<>();
+    points1.add(point1);
+    points1.add(point2);
+    points1.add(point3);
+    points1.add(point4);
+    GeoPolygonFactory.PolygonDescription pd1 = new GeoPolygonFactory.PolygonDescription(points1);
+    GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17527125408), Math.toRadians(-145.8563923));
+    GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11332154814), Math.toRadians(-145.856222168));
+    GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11317773171), Math.toRadians(-145.918433943));
+    GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17512738429), Math.toRadians(-145.918610092));
+    final List<GeoPoint> points2 = new ArrayList<>();
+    points2.add(point5);
+    points2.add(point6);
+    points2.add(point7);
+    points2.add(point8);
+    GeoPolygonFactory.PolygonDescription pd2 = new GeoPolygonFactory.PolygonDescription(points2);
+    final List<GeoPolygonFactory.PolygonDescription> pds = new ArrayList<>();
+    pds.add(pd1);
+    pds.add(pd2);
+    return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, pds);
+  }
+
+  public GeoShape getInsideCompositeShape(){
+    //MULTIPOLYGON(((19.945091 -60.552631, 20.319948 -61.555652, 20.9 -61.5, 20.9 -61, 19.945091 -60.552631)),
+    // ((21.1 -61.5,  23.107901 -61.253298, 22.720804 -60.457713,21.1 -61, 21.1 -61.5)))
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-60.552631), Math.toRadians(19.945091));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-61.555652), Math.toRadians(20.319948));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-61.5), Math.toRadians(20.9));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-61), Math.toRadians(20.9));
+    final List<GeoPoint> points1 = new ArrayList<>();
+    points1.add(point1);
+    points1.add(point2);
+    points1.add(point3);
+    points1.add(point4);
+    GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-61.5), Math.toRadians(21.1));
+    GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-61.253298), Math.toRadians(23.107901));
+    GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-60.457713), Math.toRadians(22.720804));
+    GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-61), Math.toRadians(21.1));
+    final List<GeoPoint> points2 = new ArrayList<>();
+    points2.add(point5);
+    points2.add(point6);
+    points2.add(point7);
+    points2.add(point8);
+    GeoPolygon p1 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points1);
+    GeoPolygon p2 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points2);
+    GeoCompositeMembershipShape compositeMembershipShape = new GeoCompositeMembershipShape();
+    compositeMembershipShape.addShape(p1);
+    compositeMembershipShape.addShape(p2);
+    return compositeMembershipShape;
+  }
+
+}
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/RandomGeoShapeGenerator.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/RandomGeoShapeGenerator.java
new file mode 100644
index 0000000000..f8b662964e
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/RandomGeoShapeGenerator.java
@@ -0,0 +1,944 @@
+/*
+* 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.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
+import org.apache.lucene.util.LuceneTestCase;
+
+import static com.carrotsearch.randomizedtesting.RandomizedTest.randomDouble;
+
+/**
+ * Class for generating random Geo3dShapes. They can be generated under
+ * given constraints which are expressed as a shape and a relationship.
+ *
+ * note that convexity for polygons is defined as polygons that contains
+ * antipodal points, otherwise they are convex. Internally they can be
+ * created using GeoConvexPolygons and GeoConcavePolygons.
+ *
+ */
+public class RandomGeoShapeGenerator extends LuceneTestCase {
+
+  /* Max num of iterations to find right shape under given constrains */
+  final private static int MAX_SHAPE_ITERATIONS = 50;
+  /* Max num of iterations to find right point under given constrains */
+  final private static int MAX_POINT_ITERATIONS = 1000;
+
+  /* Supported shapes */
+  final protected static int CONVEX_POLYGON = 0;
+  final protected static int CONVEX_POLYGON_WITH_HOLES = 1;
+  final protected static int CONCAVE_POLYGON = 2;
+  final protected static int CONCAVE_POLYGON_WITH_HOLES = 3;
+  final protected static int COMPLEX_POLYGON = 4;
+  final protected static int CIRCLE = 5;
+  final protected static int RECTANGLE = 6;
+  final protected static int PATH = 7;
+  final protected static int COLLECTION = 8;
+
+  /* Helper shapes for generating constraints whch are just three sided polygons */
+  final protected static int CONVEX_SIMPLE_POLYGON = 500;
+  final protected static int CONCAVE_SIMPLE_POLYGON = 501;
+
+
+  /**
+   * Method that returns empty Constraints object..
+   *
+   * @return an empty Constraints object
+   */
+  public Constraints getEmptyConstraint(){
+    return new Constraints();
+  }
+
+  /**
+   * Method that returns a random generated a random Shape code from all
+   * supported shapes.
+   *
+   * @return a random generated shape code
+   */
+  public int randomShapeType(){
+    return random().nextInt(9);
+  }
+
+  /**
+   * Method that returns a random generated a random Shape code from all
+   * convex supported shapes.
+   *
+   * @return a random generated convex shape code
+   */
+  public int randomConvexShapeType(){
+    int shapeType = randomShapeType();
+    while (isConcave(shapeType)){
+      shapeType = randomShapeType();
+    }
+    return shapeType;
+  }
+
+  /**
+   * Method that returns a random generated a random Shape code from all
+   * concave supported shapes.
+   *
+   * @return a random generated concave shape code
+   */
+  public int randomConcaveShapeType(){
+    int shapeType = randomShapeType();
+    while (!isConcave(shapeType)){
+      shapeType = randomShapeType();
+    }
+    return shapeType;
+  }
+
+  /**
+   * Method that returns a random generated GeoAreaShape code from all
+   * supported GeoAreaShapes.
+   *
+   * We are removing Collections because it is difficult to create shapes
+   * with properties in some cases.
+   *
+   * @return a random generated polygon code
+   */
+  public int randomGeoAreaShapeType(){
+    return random().nextInt(8);
+  }
+
+  /**
+   * Check if a shape code represents a concave shape
+   *
+   * @return true if the shape represented by the code is concave
+   */
+  public boolean isConcave(int shapeType){
+    return (shapeType == CONCAVE_POLYGON);
+  }
+
+  /**
+   * Method that returns a random generated Planet model from the supported
+   * Planet models. currently SPHERE and WGS84
+   *
+   * @return a random generated Planet model
+   */
+  public PlanetModel randomPlanetModel() {
+    final int shapeType = random().nextInt(2);
+    switch (shapeType) {
+      case 0: {
+        return PlanetModel.SPHERE;
+      }
+      case 1: {
+        return PlanetModel.WGS84;
+      }
+      default:
+        throw new IllegalStateException("Unexpected planet model");
+    }
+  }
+
+  /**
+   * Method that returns a random generated GeoPoint under given constraints. Returns
+   * NULL if it cannot find a point under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPoint.
+   */
+  public GeoPoint randomGeoPoint(PlanetModel planetModel, Constraints constraints) {
+    int iterations = 0;
+    while (iterations < MAX_POINT_ITERATIONS) {
+      double lat = randomDouble();
+      if (Math.PI/2 - Math.abs(lat)  <0){
+        continue;
+      }
+      double lon =  randomDouble();
+      if (Math.PI - Math.abs(lat)   <0){
+        continue;
+      }
+      iterations++;
+      GeoPoint point = new GeoPoint(planetModel, lat, lon);
+      if (constraints.isWithin(point)) {
+        return point;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated GeoAreaShape.
+   *
+   * @param shapeType The GeoAreaShape code.
+   * @param planetModel The planet model.
+   * @return The random generated GeoAreaShape.
+   */
+  public GeoAreaShape randomGeoAreaShape(int shapeType, PlanetModel planetModel){
+    GeoAreaShape geoAreaShape = null;
+    while (geoAreaShape == null){
+      geoAreaShape = randomGeoAreaShape(shapeType,planetModel,new Constraints());
+    }
+    return geoAreaShape;
+  }
+
+  /**
+   * Method that returns a random generated GeoAreaShape under given constraints. Returns
+   * NULL if it cannot build the GeoAreaShape under the given constraints.
+   *
+   * @param shapeType The GeoAreaShape code.
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoAreaShape.
+   */
+  public GeoAreaShape randomGeoAreaShape(int shapeType, PlanetModel planetModel, Constraints constraints){
+    return (GeoAreaShape)randomGeoShape(shapeType, planetModel, constraints);
+  }
+
+  /**
+   * Method that returns a random generated GeoShape.
+   *
+   * @param shapeType The shape code.
+   * @param planetModel The planet model.
+   * @return The random generated GeoShape.
+   */
+  public GeoShape randomGeoShape(int shapeType, PlanetModel planetModel){
+    GeoShape geoShape = null;
+    while (geoShape == null){
+      geoShape = randomGeoShape(shapeType,planetModel,new Constraints());
+    }
+    return geoShape;
+  }
+
+  /**
+   * Method that returns a random generated GeoShape under given constraints. Returns
+   * NULL if it cannot build the GeoShape under the given constraints.
+   *
+   * @param shapeType The polygon code.
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoShape.
+   */
+  public GeoShape randomGeoShape(int shapeType, PlanetModel planetModel, Constraints constraints){
+    switch (shapeType) {
+      case CONVEX_POLYGON: {
+        return convexPolygon(planetModel, constraints);
+      }
+      case CONVEX_POLYGON_WITH_HOLES: {
+        return convexPolygonWithHoles(planetModel, constraints);
+      }
+      case CONCAVE_POLYGON: {
+        return concavePolygon(planetModel, constraints);
+      }
+      case CONCAVE_POLYGON_WITH_HOLES: {
+        return concavePolygonWithHoles(planetModel, constraints);
+      }
+      case COMPLEX_POLYGON: {
+        return complexPolygon(planetModel, constraints);
+      }
+      case CIRCLE: {
+        return circle(planetModel, constraints);
+      }
+      case RECTANGLE: {
+        return rectangle(planetModel, constraints);
+      }
+      case PATH: {
+        return path(planetModel, constraints);
+      }
+      case COLLECTION: {
+        return collection(planetModel, constraints);
+      }
+      case CONVEX_SIMPLE_POLYGON: {
+        return simpleConvexPolygon(planetModel, constraints);
+      }
+      case CONCAVE_SIMPLE_POLYGON: {
+        return concaveSimplePolygon(planetModel, constraints);
+      }
+      default:
+        throw new IllegalStateException("Unexpected shape type");
+    }
+  }
+
+  /**
+   * Method that returns a random generated a GeoCircle under given constraints. Returns
+   * NULL if it cannot build the GeoCircle under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoCircle.
+   */
+  private GeoCircle circle(PlanetModel planetModel , Constraints constraints) {
+    int iterations=0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      final GeoPoint center = randomGeoPoint(planetModel, constraints);
+      if (center == null){
+        continue;
+      }
+      final double radius = randomCutoffAngle();
+      try {
+
+        GeoCircle circle = GeoCircleFactory.makeGeoCircle(planetModel, center.getLatitude(), center.getLongitude(), radius);
+        if (!constraints.valid(circle)) {
+          continue;
+        }
+        return circle;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a GeoBBox under given constraints. Returns
+   * NULL if it cannot build the GeoBBox under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoBBox.
+   */
+  private GeoBBox rectangle(PlanetModel planetModel, Constraints constraints) {
+
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      final GeoPoint point1 = randomGeoPoint(planetModel, constraints);
+      if (point1 == null){
+        continue;
+      }
+      final GeoPoint point2 = randomGeoPoint(planetModel, constraints);
+      if (point2 == null){
+        continue;
+      }
+
+      double minLat = Math.min(point1.getLatitude(), point2.getLatitude());
+      double maxLat = Math.max(point1.getLatitude(), point2.getLatitude());
+      double minLon = Math.min(point1.getLongitude(), point2.getLongitude());
+      double maxLon = Math.max(point1.getLongitude(), point2.getLongitude());
+
+      try {
+        GeoBBox bbox = GeoBBoxFactory.makeGeoBBox(planetModel, maxLat, minLat, minLon, maxLon);
+        if (!constraints.valid(bbox)) {
+          continue;
+        }
+        return bbox;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a GeoPath under given constraints. Returns
+   * NULL if it cannot build the GeoPath under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPath.
+   */
+  private GeoPath path(PlanetModel planetModel, Constraints constraints) {
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      int vertexCount =  random().nextInt(2) + 2;
+      List<GeoPoint> geoPoints = points(vertexCount, planetModel, constraints);
+      double width =randomCutoffAngle();
+      try {
+        GeoPath path = GeoPathFactory.makeGeoPath(planetModel, width, geoPoints.toArray(new GeoPoint[geoPoints.size()]));
+        if (!constraints.valid(path)) {
+          continue;
+        }
+        return path;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a GeoCompositeMembershipShape under given constraints. Returns
+   * NULL if it cannot build the GGeoCompositeMembershipShape under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoCompositeMembershipShape.
+   */
+  private GeoCompositeAreaShape collection(PlanetModel planetModel, Constraints constraints) {
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      int numberShapes =  random().nextInt(3) + 2;
+      GeoCompositeAreaShape collection = new GeoCompositeAreaShape();
+      for(int i=0; i<numberShapes;i++){
+        GeoPolygon member = convexPolygon(planetModel, constraints);
+        if (member != null){
+          collection.addShape(member);
+        }
+      }
+      if (collection.shapes.size() ==0){
+        continue;
+      }
+      return collection;
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a convex GeoPolygon under given constraints. Returns
+   * NULL if it cannot build the GePolygon under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon convexPolygon(PlanetModel planetModel, Constraints constraints) {
+    int vertexCount = random().nextInt(4) + 3;
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
+      List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
+      try {
+        GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints);
+        if (!constraints.valid(polygon) || isConcave(planetModel, polygon)) {
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a convex GeoPolygon with holes under given constraints. Returns
+   * NULL if it cannot build the GeoPolygon with holes under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon convexPolygonWithHoles(PlanetModel planetModel, Constraints constraints) {
+    int vertexCount = random().nextInt(4) + 3;
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
+      List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
+      try {
+        GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints);
+        //polygon should comply with all constraints except disjoint as we have holes
+        Constraints polygonConstraints = new Constraints();
+        polygonConstraints.putAll(constraints.getContains());
+        polygonConstraints.putAll(constraints.getWithin());
+        polygonConstraints.putAll(constraints.getDisjoint());
+        if (!polygonConstraints.valid(polygon) || isConcave(planetModel, polygon)){
+          continue;
+        }
+        //hole must overlap with polygon and comply with any CONTAINS constraint.
+        Constraints holeConstraints = new Constraints();
+        holeConstraints.putAll(constraints.getContains());
+        holeConstraints.put(polygon,GeoArea.OVERLAPS);
+        //Points must be with in the polygon and must comply
+        // CONTAINS and DISJOINT constraints
+        Constraints pointsConstraints = new Constraints();
+        pointsConstraints.put(polygon,GeoArea.WITHIN);
+        pointsConstraints.putAll(constraints.getContains());
+        pointsConstraints.putAll(constraints.getDisjoint());
+        List<GeoPolygon> holes = concavePolygonHoles(planetModel, holeConstraints, pointsConstraints);
+        //we should have at least one hole
+        if (holes.size() == 0){
+          continue;
+        }
+        polygon = GeoPolygonFactory.makeGeoPolygon(planetModel,orderedGeoPoints,holes);
+        if (!constraints.valid(polygon) || isConcave(planetModel, polygon)){
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random list if concave GeoPolygons under given constraints. Method
+   * use to generate convex holes. Note that constraints for points and holes are different,
+   *
+   * @param planetModel The planet model.
+   * @param holeConstraints The given constraints that a hole must comply.
+   * @param pointConstraints The given constraints that a point must comply.
+   * @return The random generated GeoPolygon.
+   */
+  private List<GeoPolygon> concavePolygonHoles(PlanetModel planetModel,
+                                               Constraints holeConstraints,
+                                               Constraints pointConstraints) {
+    int iterations =0;
+    int holesCount = random().nextInt(3) + 1;
+    List<GeoPolygon> holes = new ArrayList<>();
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      int vertexCount = random().nextInt(3) + 3;
+      List<GeoPoint> geoPoints = points(vertexCount, planetModel, pointConstraints);
+      geoPoints = orderPoints(geoPoints);
+      Collections.reverse(geoPoints);
+      try {
+        GeoPolygon hole = GeoPolygonFactory.makeGeoPolygon(planetModel, geoPoints);
+        if (!holeConstraints.valid(hole) || isConvex(planetModel, hole)) {
+          continue;
+        }
+        holes.add(hole);
+        if (holes.size() == holesCount){
+          return holes;
+        }
+        pointConstraints.put(hole, GeoArea.DISJOINT);
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return holes;
+  }
+
+  /**
+   * Method that returns a random generated a concave GeoPolygon under given constraints. Returns
+   * NULL if it cannot build the concave GeoPolygon under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon concavePolygon(PlanetModel planetModel, Constraints constraints) {
+
+    int vertexCount = random().nextInt(4) + 3;
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
+      List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
+      Collections.reverse(orderedGeoPoints);
+      try {
+        GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints);
+        if (!constraints.valid(polygon) || isConvex(planetModel, polygon)) {
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a concave GeoPolygon with holes under given constraints. Returns
+   * NULL if it cannot build the GeoPolygon under the given constraints. Note that the final GeoPolygon is
+   * convex as the hole wraps the convex GeoPolygon.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon concavePolygonWithHoles(PlanetModel planetModel, Constraints constraints) {
+    int vertexCount = random().nextInt(4) + 3;
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      //we first build the hole. We consider all constraints except
+      // disjoint as we have a hole
+      Constraints holeConstraints = new Constraints();
+      holeConstraints.putAll(constraints.getContains());
+      holeConstraints.putAll(constraints.getWithin());
+      holeConstraints.putAll(constraints.getOverlaps());
+      GeoPolygon hole = convexPolygon(planetModel, holeConstraints);
+      if (hole == null){
+        continue;
+      }
+      // Now we get points for polygon. Must we with in the hole
+      // and we add contain constraints
+      Constraints pointConstraints = new Constraints();
+      pointConstraints.put(hole, GeoArea.WITHIN);
+      pointConstraints.putAll(constraints.getContains());
+      List<GeoPoint> geoPoints = points(vertexCount,planetModel, pointConstraints);
+      List<GeoPoint> orderedGeoPoints = orderPoints(geoPoints);
+      Collections.reverse(orderedGeoPoints);
+      try {
+        GeoPolygon polygon = GeoPolygonFactory.makeGeoPolygon(planetModel, orderedGeoPoints, Collections.singletonList(hole));
+        //final polygon must be convex
+        if (!constraints.valid(polygon) || isConcave(planetModel,polygon)) {
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated complex GeoPolygon under given constraints. Returns
+   * NULL if it cannot build the complex GeoPolygon under the given constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon complexPolygon(PlanetModel planetModel, Constraints constraints) {
+    int polygonsCount =random().nextInt(2) + 1;
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      List<GeoPolygonFactory.PolygonDescription> polDescription = new ArrayList<>();
+      while(polDescription.size() < polygonsCount){
+        int vertexCount = random().nextInt(14) + 3;
+        List<GeoPoint> geoPoints = points(vertexCount,planetModel, constraints);
+        orderPoints(geoPoints);
+        polDescription.add(new GeoPolygonFactory.PolygonDescription(geoPoints));
+      }
+      try {
+        GeoPolygon polygon = GeoPolygonFactory.makeLargeGeoPolygon(planetModel,polDescription);
+        if (!constraints.valid(polygon)) {
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a concave square GeoPolygon under given constraints. Returns
+   * NULL if it cannot build the concave GeoPolygon under the given constraints. This shape is an utility
+   * to build constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon simpleConvexPolygon(PlanetModel planetModel, Constraints constraints) {
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      List<GeoPoint> points = points(3,planetModel,constraints);
+      points = orderPoints(points);
+      try {
+        GeoPolygon polygon =  GeoPolygonFactory.makeGeoConvexPolygon(planetModel, points);
+        if(!constraints.valid(polygon) || isConcave(planetModel,polygon)){
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random generated a convex square GeoPolygon under given constraints. Returns
+   * NULL if it cannot build the convex GeoPolygon under the given constraints. This shape is an utility
+   * to build constraints.
+   *
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated GeoPolygon.
+   */
+  private GeoPolygon concaveSimplePolygon(PlanetModel planetModel, Constraints constraints) {
+    int iterations = 0;
+    while (iterations < MAX_SHAPE_ITERATIONS) {
+      iterations++;
+      List<GeoPoint> points = points(3, planetModel, constraints);
+      points = orderPoints(points);
+      Collections.reverse(points);
+      try {
+        GeoPolygon polygon =  GeoPolygonFactory.makeGeoConcavePolygon(planetModel, points);
+        if(!constraints.valid(polygon) || isConvex(planetModel, polygon)){
+          continue;
+        }
+        return polygon;
+      } catch (IllegalArgumentException e) {
+        continue;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Method that returns a random list of generated GeoPoints under given constraints. If it cannot
+   * find a point it will add a point that might not comply with the constraints.
+   *
+   * @param count The number of points
+   * @param planetModel The planet model.
+   * @param constraints The given constraints.
+   * @return The random generated List of GeoPoints.
+   */
+  private List<GeoPoint> points(int count, PlanetModel planetModel, Constraints constraints){
+    List<GeoPoint> geoPoints = new ArrayList<>(count);
+    for(int i= 0; i< count; i++) {
+      GeoPoint point = randomGeoPoint(planetModel, constraints);
+      if (point == null){
+        point = randomGeoPoint(planetModel, new Constraints());
+      }
+      geoPoints.add(point);
+    }
+    return  geoPoints;
+  }
+
+  /**
+   * Check if a GeoPolygon is pure concave. Note that our definition for concavity is that the polygon
+   * contains antipodal points.
+   *
+   * @param planetModel The planet model.
+   * @param shape The polygon to check.
+   * @return True if the polygon contains antipodal points.
+   */
+  private boolean isConcave(PlanetModel planetModel, GeoPolygon shape){
+    return (shape.isWithin(planetModel.NORTH_POLE) && shape.isWithin(planetModel.SOUTH_POLE))||
+        (shape.isWithin(planetModel.MAX_X_POLE) && shape.isWithin(planetModel.MIN_X_POLE)) ||
+        (shape.isWithin(planetModel.MAX_Y_POLE) && shape.isWithin(planetModel.MIN_Y_POLE));
+  }
+
+  /**
+   * Check if a GeoPolygon is pure convex. Note that our definition for convexity is that the polygon
+   * does not contain antipodal points.
+   *
+   * @param planetModel The planet model.
+   * @param shape The polygon to check.
+   * @return True if the polygon dies not contains antipodal points.
+   */
+  private boolean isConvex(PlanetModel planetModel, GeoPolygon shape){
+    return !isConcave(planetModel,shape);
+  }
+
+  /**
+   * Generates a random number between 0 and PI.
+   *
+   * @return the cutoff angle.
+   */
+  private double randomCutoffAngle() {
+    while(true) {
+      double radius = randomDouble();
+      if (radius <0 || radius > Math.PI){
+        continue;
+      }
+      return radius;
+    }
+  }
+
+  /**
+   * Method that orders a lit of points anti-clock-wise to prevent crossing edges.
+   *
+   * @param originalPoints The points to order.
+   * @return The list of ordered points anti-clockwise.
+   */
+  private List<GeoPoint> orderPoints(List<GeoPoint> originalPoints){
+    List<GeoPoint> points = new ArrayList<>(originalPoints.size());
+    points.addAll(originalPoints); //make a copy
+    GeoPoint lPoint = getPointLefLon(points);
+    points.remove(lPoint);
+    GeoPoint rPoint = getPointRigthLon(points);
+    points.remove(rPoint);
+    List<GeoPoint> APoints = getPointsBelowAndSort(points, lPoint);
+    List<GeoPoint> BPoints = getPointsAboveAndsort(points, lPoint);
+    List<GeoPoint> result = new ArrayList<>();
+    result.add(lPoint);
+    result.addAll(APoints);
+    result.add(rPoint);
+    result.addAll(BPoints);
+    return result;
+  }
+
+  private List<GeoPoint> getPointsAboveAndsort(List<GeoPoint> points,GeoPoint lPoint) {
+    List<GeoPoint> BPoints = new ArrayList<>();
+    for (GeoPoint point : points){
+      if(point.getLatitude() > lPoint.getLatitude()){
+        BPoints.add(point);
+      }
+    }
+    Collections.sort(BPoints, new Comparator<GeoPoint>() {
+      public int compare(GeoPoint idx1, GeoPoint idx2) {
+        return Double.compare(idx1.getLongitude(), idx2.getLongitude());
+      }
+    });
+    return BPoints;
+  }
+
+  private List<GeoPoint> getPointsBelowAndSort(List<GeoPoint> points,GeoPoint lPoint) {
+    List<GeoPoint> APoints = new ArrayList<>();
+    for (GeoPoint point : points){
+      if(point.getLatitude() < lPoint.getLatitude()){
+        APoints.add(point);
+      }
+    }
+    Collections.sort(APoints, new Comparator<GeoPoint>() {
+      public int compare(GeoPoint idx1, GeoPoint idx2) {
+        return Double.compare(idx1.getLongitude(), idx2.getLongitude());
+      }
+    });
+    return APoints;
+  }
+
+  private GeoPoint getPointLefLon(List<GeoPoint> points)  {
+    GeoPoint lPoint = null;
+    for (GeoPoint point : points){
+      if(lPoint == null ){
+        lPoint = point;
+      }
+      else{
+        if (lPoint.getLongitude() > point.getLongitude()){
+          lPoint = point;
+        }
+      }
+    }
+    return lPoint;
+  }
+
+  private GeoPoint getPointRigthLon(List<GeoPoint> points) {
+    GeoPoint rPoint = null;
+    for (GeoPoint point : points){
+      if(rPoint == null ){
+        rPoint = point;
+      }
+      else{
+        if (rPoint.getLongitude() < point.getLongitude()){
+          rPoint = point;
+        }
+      }
+    }
+    return rPoint;
+  }
+
+  /**
+   * Class that holds the constraints that are given to
+   * build shapes. It consists in a list of GeoAreaShapes
+   * and relationships the new shape needs to satisfy.
+   */
+  class Constraints extends HashMap<GeoAreaShape, Integer>{
+
+    /**
+     * Check if the shape is valid under the constraints.
+     *
+     * @param shape The shape to check
+     * @return true if the shape satisfy the constraints, else false.
+     */
+    public boolean valid(GeoShape shape) {
+      if (shape == null){
+        return false;
+      }
+      for (GeoAreaShape constraint : keySet()) {
+        if (constraint.getRelationship(shape) != get(constraint)) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * Check if a point is Within the constraints.
+     *
+     * @param point The point to check
+     * @return true if the point satisfy the constraints, else false.
+     */
+    public boolean isWithin(GeoPoint point) {
+      for (GeoShape constraint : keySet()) {
+        if (!(validPoint(point, constraint, get(constraint)))) {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * Check if a point is Within one constraint given by a shape and a relationship.
+     *
+     * @param point The point to check
+     * @param shape The shape of the constraint
+     * @param relationship The relationship of the constraint.
+     * @return true if the point satisfy the constraint, else false.
+     */
+    private boolean validPoint(GeoPoint point, GeoShape shape, int relationship) {
+      //For GeoCompositeMembershipShape we only consider the first shape to help
+      // converging
+      if (relationship == GeoArea.WITHIN && shape instanceof GeoCompositeMembershipShape) {
+        shape = (((GeoCompositeMembershipShape) shape).shapes.get(0));
+      }
+      switch (relationship) {
+        case GeoArea.DISJOINT:
+          return !shape.isWithin(point);
+        case GeoArea.OVERLAPS:
+          return true;
+        case GeoArea.CONTAINS:
+          return !shape.isWithin(point);
+        case GeoArea.WITHIN:
+          return shape.isWithin(point);
+        default:
+          return true;
+      }
+    }
+
+    /**
+     * Collect the CONTAINS constraints in the object
+     *
+     * @return the CONTAINS constraints.
+     */
+    public Constraints getContains(){
+      return getConstraintsOfType(GeoArea.CONTAINS);
+    }
+
+    /**
+     * Collect the WITHIN constraints in the object
+     *
+     * @return the WITHIN constraints.
+     */
+    public Constraints getWithin(){
+      return getConstraintsOfType(GeoArea.WITHIN);
+    }
+
+    /**
+     * Collect the OVERLAPS constraints in the object
+     *
+     * @return the OVERLAPS constraints.
+     */
+    public Constraints getOverlaps(){
+      return getConstraintsOfType(GeoArea.OVERLAPS);
+    }
+
+    /**
+     * Collect the DISJOINT constraints in the object
+     *
+     * @return the DISJOINT constraints.
+     */
+    public Constraints getDisjoint(){
+      return getConstraintsOfType(GeoArea.DISJOINT);
+    }
+
+    private Constraints getConstraintsOfType(int type){
+      Constraints constraints = new Constraints();
+      for (GeoAreaShape constraint : keySet()) {
+        if (type == get(constraint)) {
+          constraints.put(constraint, type);
+        }
+      }
+      return constraints;
+    }
+  }
+}
+
+
+
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/RandomGeoShapeRelationshipTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/RandomGeoShapeRelationshipTest.java
new file mode 100644
index 0000000000..fe1b1d9868
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/RandomGeoShapeRelationshipTest.java
@@ -0,0 +1,240 @@
+package org.apache.lucene.spatial3d.geom;
+
+import com.carrotsearch.randomizedtesting.annotations.Repeat;
+import org.junit.Test;
+
+/**
+ * Random test to check relationship between GeoAreaShapes and GeoShapes.
+ */
+public class RandomGeoShapeRelationshipTest extends RandomGeoShapeGenerator {
+
+
+  /**
+   * Test for WITHIN points. We build a WITHIN shape with respect the geoAreaShape
+   * and create a point WITHIN the shape. The resulting shape should be WITHIN
+   * the original shape.
+   *
+   */
+  @Test
+  @Repeat(iterations = 5)
+  public void testRandomPointWithin() {
+    int referenceShapeType = CONVEX_POLYGON;
+    PlanetModel planetModel = randomPlanetModel();
+    int shapeType = randomShapeType();
+    GeoAreaShape shape = null;
+    GeoPoint point = null;
+    while (point == null) {
+      shape = randomGeoAreaShape(shapeType, planetModel);
+      Constraints constraints = getEmptyConstraint();
+      constraints.put(shape, GeoArea.WITHIN);
+      GeoAreaShape reference =  randomGeoAreaShape(referenceShapeType, planetModel, constraints);
+      if (reference != null) {
+        constraints = new Constraints();
+        constraints.put(reference, GeoArea.WITHIN);
+        point = randomGeoPoint(planetModel, constraints);
+      }
+    }
+    assertTrue(shape.isWithin(point));
+  }
+
+  /**
+   * Test for NOT WITHIN points. We build a DIJOINT shape with respect the geoAreaShape
+   * and create a point WITHIN that shape. The resulting shape should not be WITHIN
+   * the original shape.
+   *
+   */
+  @Repeat(iterations = 5)
+  public void testRandomPointNotWithin() {
+    int referenceShapeType = CONVEX_POLYGON;
+    PlanetModel planetModel = randomPlanetModel();
+    int shapeType = randomShapeType();
+    GeoAreaShape shape = null;
+    GeoPoint point = null;
+    while (point == null) {
+      shape = randomGeoAreaShape(shapeType, planetModel);
+      Constraints constraints = getEmptyConstraint();
+      constraints.put(shape, GeoArea.DISJOINT);
+      GeoAreaShape reference =  randomGeoAreaShape(referenceShapeType, planetModel, constraints);
+      if (reference != null) {
+        constraints = new Constraints();
+        constraints.put(reference, GeoArea.WITHIN);
+        point = randomGeoPoint(planetModel, constraints);
+      }
+    }
+    assertFalse(shape.isWithin(point));
+  }
+
+  /**
+   * Test for disjoint shapes. We build a DISJOINT shape with respect the geoAreaShape
+   * and create shapes WITHIN that shapes. The resulting shape should be DISJOINT
+   * to the geoAreaShape.
+   *
+   * Note that both shapes cannot be concave.
+   */
+  @Test
+  @Repeat(iterations = 5)
+  public void testRandomDisjoint() {
+    int referenceShapeType = CONVEX_SIMPLE_POLYGON;
+    PlanetModel planetModel = randomPlanetModel();
+    int geoAreaShapeType = randomGeoAreaShapeType();
+    int shapeType =randomConvexShapeType();
+
+    GeoShape shape = null;
+    GeoAreaShape geoAreaShape = null;
+    while (shape == null) {
+      geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
+      Constraints constraints = new Constraints();
+      constraints.put(geoAreaShape, GeoArea.DISJOINT);
+      GeoAreaShape reference = randomGeoAreaShape(referenceShapeType, planetModel, constraints);
+      if (reference != null) {
+        constraints = getEmptyConstraint();
+        constraints.put(reference, GeoArea.WITHIN);
+        shape = randomGeoShape(shapeType, planetModel, constraints);
+      }
+    }
+    int rel = geoAreaShape.getRelationship(shape);
+    assertEquals(GeoArea.DISJOINT, rel);
+    if (shape instanceof GeoArea) {
+      rel = ((GeoArea)shape).getRelationship(geoAreaShape);
+      assertEquals(GeoArea.DISJOINT, rel);
+    }
+  }
+
+  /**
+   * Test for within shapes. We build a shape WITHIN the geoAreaShape and create
+   * shapes WITHIN that shape. The resulting shape should be WITHIN
+   * to the geoAreaShape.
+   *
+   * Note that if the geoAreaShape is not concave the other shape must be not concave.
+   */
+  @Test
+  @Repeat(iterations = 5)
+  public void testRandomWithIn() {
+    PlanetModel planetModel = randomPlanetModel();
+    int geoAreaShapeType = randomGeoAreaShapeType();
+    int shapeType =randomShapeType();
+    int referenceShapeType = CONVEX_SIMPLE_POLYGON;
+    if (!isConcave(geoAreaShapeType)){
+      shapeType =randomConvexShapeType();
+    }
+    if(isConcave(shapeType)){//both concave
+      referenceShapeType = CONCAVE_SIMPLE_POLYGON;
+    }
+    GeoShape shape = null;
+    GeoAreaShape geoAreaShape = null;
+    while (shape == null) {
+      geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
+      Constraints constraints = new Constraints();
+      constraints.put(geoAreaShape, GeoArea.WITHIN);
+      GeoAreaShape reference = randomGeoAreaShape(referenceShapeType, planetModel, constraints);
+      if (reference != null) {
+        constraints = new Constraints();
+        constraints.put(reference, GeoArea.WITHIN);
+        shape = randomGeoShape(shapeType, planetModel, constraints);
+      }
+    }
+    int rel = geoAreaShape.getRelationship(shape);
+    assertEquals(GeoArea.WITHIN, rel);
+    if (shape instanceof GeoArea) {
+      rel = ((GeoArea)shape).getRelationship(geoAreaShape);
+      assertEquals(GeoArea.CONTAINS, rel);
+    }
+  }
+
+
+  /**
+   * Test for contains shapes. We build a shape containing the geoAreaShape and create
+   * shapes WITHIN that shape. The resulting shape should CONTAIN
+   * the geoAreaShape.
+   *
+   * Note that if the geoAreaShape is concave the other shape must be concave.
+   * If shape is concave, the shape for reference should be concave as well.
+   *
+   */
+  @Test
+  @Repeat(iterations = 1)
+  public void testRandomContains() {
+    int referenceShapeType = CONVEX_SIMPLE_POLYGON;
+    PlanetModel planetModel = randomPlanetModel();
+    int geoAreaShapeType = randomGeoAreaShapeType();
+    while (geoAreaShapeType == COLLECTION){
+      geoAreaShapeType = randomGeoAreaShapeType();
+    }
+    int shapeType = randomShapeType();
+    if (isConcave(geoAreaShapeType)){
+      shapeType = randomConcaveShapeType();
+    }
+    if (isConcave(shapeType)){
+      referenceShapeType = CONCAVE_SIMPLE_POLYGON;
+    }
+    GeoShape shape = null;
+    GeoAreaShape geoAreaShape = null;
+    while (shape == null) {
+      geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
+      Constraints constraints = getEmptyConstraint();
+      constraints.put(geoAreaShape, GeoArea.CONTAINS);
+      GeoPolygon reference =(GeoPolygon)randomGeoAreaShape(referenceShapeType, planetModel, constraints);
+      if (reference != null) {
+        constraints = getEmptyConstraint();
+        constraints.put(reference, GeoArea.CONTAINS);
+        shape = randomGeoShape(shapeType, planetModel, constraints);
+      }
+    }
+    int rel = geoAreaShape.getRelationship(shape);
+    assertEquals(GeoArea.CONTAINS, rel);
+    if (shape instanceof GeoArea) {
+      rel = ((GeoArea)shape).getRelationship(geoAreaShape);
+      assertEquals(GeoArea.WITHIN, rel);
+    }
+  }
+
+  /**
+   * Test for overlapping shapes. We build a shape that contains part of the
+   * geoAreaShape, is disjoint to other part and contains a disjoint shape. We create
+   * shapes  according the criteria. The resulting shape should OVERLAP
+   * the geoAreaShape.
+   */
+  @Test
+  @Repeat(iterations = 5)
+  public void testRandomOverlaps() {
+    PlanetModel planetModel = randomPlanetModel();
+    int geoAreaShapeType = randomGeoAreaShapeType();
+    int shapeType = randomShapeType();
+
+    GeoShape shape = null;
+    GeoAreaShape geoAreaShape = null;
+    while (shape == null) {
+      geoAreaShape = randomGeoAreaShape(geoAreaShapeType, planetModel);
+      Constraints constraints = getEmptyConstraint();
+      constraints.put(geoAreaShape,GeoArea.WITHIN);
+      GeoAreaShape reference1 = randomGeoAreaShape(CONVEX_SIMPLE_POLYGON, planetModel, constraints);
+      if (reference1 == null){
+        continue;
+      }
+      constraints = getEmptyConstraint();
+      constraints.put(geoAreaShape, GeoArea.WITHIN);
+      constraints.put(reference1, GeoArea.DISJOINT);
+      GeoAreaShape reference2 = randomGeoAreaShape(CONVEX_SIMPLE_POLYGON, planetModel, constraints);
+      if (reference2 == null){
+        continue;
+      }
+      constraints = getEmptyConstraint();
+      constraints.put(geoAreaShape, GeoArea.DISJOINT);
+      GeoAreaShape reference3 = randomGeoAreaShape(CONVEX_SIMPLE_POLYGON, planetModel, constraints);
+      if (reference3 != null) {
+        constraints = new Constraints();
+        constraints.put(reference1, GeoArea.DISJOINT);
+        constraints.put(reference2, GeoArea.CONTAINS);
+        constraints.put(reference3, GeoArea.CONTAINS);
+        shape = randomGeoShape(shapeType, planetModel, constraints);
+      }
+    }
+    int rel = geoAreaShape.getRelationship(shape);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    if (shape instanceof GeoArea) {
+      rel = ((GeoArea)shape).getRelationship(geoAreaShape);
+      assertEquals(GeoArea.OVERLAPS, rel);
+    }
+  }
+}
+
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/SimpleGeoPolygonRelationshipsTest.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/SimpleGeoPolygonRelationshipsTest.java
new file mode 100644
index 0000000000..0d336396ba
--- /dev/null
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/geom/SimpleGeoPolygonRelationshipsTest.java
@@ -0,0 +1,837 @@
+/*
+* 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.Collections;
+import java.util.List;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Check relationship between polygon and GeoShapes of basic polygons. Normally we construct
+ * the convex, concave counterpart and the convex polygon as a complex polygon.
+ */
+public class SimpleGeoPolygonRelationshipsTest {
+
+  /**
+   * Test with two shapes with no crossing edges and no points in common in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygon1() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
+    GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -60.4,
+        20.1, -60.4,
+        20.1, -60.3,
+        20.0, -60.3);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -60.4,
+        20.1, -60.4,
+        20.1, -60.3,
+        20.0, -60.3);
+
+
+    //Convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.DISJOINT,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.WITHIN,  rel);//Check
+
+    //Concave
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN,  rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.CONTAINS,  rel);
+
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //Complex
+    rel = originalComplexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT,  rel);
+    rel = polConvex.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.DISJOINT,  rel);
+
+    rel = originalComplexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS,  rel);
+    rel = polConcave.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.WITHIN,  rel);
+  }
+
+
+  /**
+   * Test with two shapes with crossing edges and some points inside in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygon2() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
+    GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    //POLYGON ((20.0 -60.4, 23.1 -60.4, 23.1 -60.3, 20.0  -60.3,20.0 -60.4))
+    GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -60.4,
+        23.1, -60.4,
+        23.1, -60.3,
+        20.0, -60.3);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -60.4,
+        23.1, -60.4,
+        23.1, -60.3,
+        20.0, -60.3);
+
+    //Convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //Concave
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //Complex
+    rel = originalComplexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+
+    rel = originalComplexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  /**
+   * Test with two shapes with no crossing edges and all points inside in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygon3() {
+
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
+    GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    //POLYGON ((20.0 -61.1, 20.1 -61.1, 20.1 -60.5, 20.0  -60.5,20.0 -61.1))
+    GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -61.1,
+        20.1, -61.1,
+        20.1, -60.5,
+        20.0, -60.5);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -61.1,
+        20.1, -61.1,
+        20.1, -60.5,
+        20.0, -60.5);
+
+    //Convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.CONTAINS,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //Concave
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS,  rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.WITHIN,  rel);//check
+
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT,  rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.DISJOINT,  rel);
+
+    //Complex
+    rel = originalComplexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN,  rel);
+    rel = polConvex.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.CONTAINS,  rel);
+
+    rel = originalComplexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  /**
+   * Test with two shapes with crossing edges and no points inside in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygon4() {
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
+    GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    //POLYGON ((20.0 -62.4, 20.1 -62.4, 20.1 -60.3, 20.0  -60.3,20.0 -62.4)) intersects no points inside
+    GeoPolygon polConvex = buildConvexGeoPolygon(20.0, -62.4,
+        20.1, -62.4,
+        20.1, -60.3,
+        20.0, -60.3);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(20.0, -62.4,
+        20.1, -62.4,
+        20.1, -60.3,
+        20.0, -60.3);
+
+    //Convex
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //concave
+    rel = originalConcavePol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConcavePol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConcavePol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //Complex
+    rel = originalComplexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalComplexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalComplexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  /**
+   * Test with two shapes with no crossing edges and polygon in hole  in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygonWithHole1() {
+    //POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
+    GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7);
+    GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31, hole);
+
+    GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31);
+
+    GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7, holeInv);
+
+    //POLYGON((-135.7 -30.6, -135.7 -30.45, -136 -30.45, -136 -30.6, -135.7 -30.6)) in the hole
+    GeoPolygon polConvex = buildConvexGeoPolygon(-135.7, -30.6,
+        -135.7, -30.45,
+        -136, -30.45,
+        -136, -30.6);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-135.7, -30.6,
+        -135.7, -30.45,
+        -136, -30.45,
+        -136, -30.6);
+
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.DISJOINT,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.WITHIN,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConvex);
+    assertEquals(GeoArea.DISJOINT,  rel);
+    rel = polConvex.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.DISJOINT,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConcave);
+    assertEquals(GeoArea.CONTAINS,  rel);
+    rel = polConcave.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.WITHIN,  rel);
+  }
+
+  /**
+   * Test with two shapes with crossing edges in hole and some points inside in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygonWithHole2() {
+    //POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
+    GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7);
+    GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31, hole);
+
+    GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31);
+
+    GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7, holeInv);
+
+    //POLYGON((-135.5 -31.2, -135.5 -30.8, -136 -30.8, -136 -31.2, -135.5 -31.2)) intersects the hole
+    GeoPolygon polConvex = buildConvexGeoPolygon(-135.5, -30.2,
+        -135.5, -30.8,
+        -136, -30.8,
+        -136, -30.2);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-135.5, -30.2,
+        -135.5, -30.8,
+        -136, -30.8,
+        -136, -30.2);
+
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  /**
+   * Test with two shapes with crossing edges and some points inside in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygonWithHole3() {
+    //POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
+    GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7);
+    GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31, hole);
+
+    GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31);
+
+    GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7, holeInv);
+
+    //POLYGON((-135.2 -30.8, -135.2 -30.2, -136.8 -30.2, -136.8 -30.8, -135.2 -30.8)) inside the polygon covering the hole
+    GeoPolygon polConvex = buildConvexGeoPolygon(-135.2, -30.8,
+        -135.2, -30.3,
+        -136.8, -30.2,
+        -136.8, -30.8);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-135.2, -30.8,
+        -135.2, -30.3,
+        -136.8, -30.2,
+        -136.8, -30.8);
+
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConvex);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConvex.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  /**
+   * Test with two shapes with no crossing edges and all points inside in convex case.
+   */
+  @Test
+  public void testGeoSimplePolygonWithHole4() {
+    //POLYGON((-135 -31, -135 -30, -137 -30, -137 -31, -135 -31),(-135.5 -30.7, -135.5 -30.4, -136.5 -30.4, -136.5 -30.7, -135.5 -30.7))
+    GeoPolygon hole = buildConcaveGeoPolygon(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7);
+    GeoPolygon originalConvexPol = buildConvexGeoPolygonWithHole(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31, hole);
+
+    GeoPolygon holeInv = buildConvexGeoPolygon(-135, -31,
+        -135, -30,
+        -137, -30,
+        -137, -31);
+
+    GeoPolygon originalConvexPolInv = buildConcaveGeoPolygonWithHole(-135.5, -30.7,
+        -135.5, -30.4,
+        -136.5, -30.4,
+        -136.5, -30.7, holeInv);
+
+    // POLYGON((-135.7 -30.3, -135.7 -30.2, -136 -30.2, -136 -30.3, -135.7 -30.3))inside the polygon
+    GeoPolygon polConvex = buildConvexGeoPolygon(-135.7, -30.3,
+        -135.7, -30.2,
+        -136, -30.2,
+        -136, -30.3);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-135.7, -30.3,
+        -135.7, -30.2,
+        -136, -30.2,
+        -136, -30.3);
+
+    int rel = originalConvexPol.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN,  rel);
+    rel = polConvex.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.CONTAINS,  rel);
+
+    rel = originalConvexPol.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConvex);
+    assertEquals(GeoArea.WITHIN,  rel);
+    rel = polConvex.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.CONTAINS,  rel);
+
+    rel = originalConvexPolInv.getRelationship(polConcave);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(originalConvexPolInv);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  @Test
+  public void testGeoSimplePolygonWithCircle() {
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
+    GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoCircle outCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Math.toRadians(-70), Math.toRadians(23), Math.toRadians(1));
+    int rel = originalConvexPol.getRelationship(outCircle);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = originalConcavePol.getRelationship(outCircle);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = originalComplexPol.getRelationship(outCircle);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    GeoCircle overlapCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Math.toRadians(-61.5), Math.toRadians(20), Math.toRadians(1));
+    rel = originalConvexPol.getRelationship(overlapCircle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = originalConcavePol.getRelationship(overlapCircle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = originalComplexPol.getRelationship(overlapCircle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    GeoCircle inCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Math.toRadians(-61), Math.toRadians(21), Math.toRadians(0.1));
+    rel = originalConvexPol.getRelationship(inCircle);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = originalConcavePol.getRelationship(inCircle);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = originalComplexPol.getRelationship(inCircle);
+    assertEquals(GeoArea.WITHIN, rel);
+
+    GeoCircle onCircle = GeoCircleFactory.makeGeoCircle(PlanetModel.SPHERE, Math.toRadians(-61), Math.toRadians(21), Math.toRadians(10.));
+    rel = originalConvexPol.getRelationship(onCircle);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = originalConcavePol.getRelationship(onCircle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = originalComplexPol.getRelationship(onCircle);
+    assertEquals(GeoArea.CONTAINS, rel);
+  }
+
+  @Test
+  public void testGeoSimplePolygonWithBBox() {
+    //POLYGON ((19.845091 -60.452631, 20.119948 -61.655652, 23.207901 -61.453298, 22.820804 -60.257713, 19.845091 -60.452631)) disjoint
+    GeoPolygon originalConvexPol = buildConvexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalConcavePol = buildConcaveGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoPolygon originalComplexPol = buildComplexGeoPolygon(19.84509, -60.452631,
+        20.119948, -61.655652,
+        23.207901, -61.453298,
+        22.820804, -60.257713);
+
+    GeoBBox outRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.toRadians(-69),
+        Math.toRadians(-70),
+        Math.toRadians(22),
+        Math.toRadians(23));
+    int rel = originalConvexPol.getRelationship(outRectangle);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = outRectangle.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = originalConcavePol.getRelationship(outRectangle);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = originalComplexPol.getRelationship(outRectangle);
+    assertEquals(GeoArea.DISJOINT, rel);
+
+    GeoBBox overlapRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.toRadians(-61),
+        Math.toRadians(-62),
+        Math.toRadians(22),
+        Math.toRadians(23));
+    rel = originalConvexPol.getRelationship(overlapRectangle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = overlapRectangle.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = originalConcavePol.getRelationship(overlapRectangle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = originalComplexPol.getRelationship(overlapRectangle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+
+    GeoBBox inRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.toRadians(-61),
+        Math.toRadians(-61.1),
+        Math.toRadians(22.5),
+        Math.toRadians(23));
+    rel = originalConvexPol.getRelationship(inRectangle);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = inRectangle.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = originalConcavePol.getRelationship(inRectangle);
+    assertEquals(GeoArea.DISJOINT, rel);
+    rel = originalComplexPol.getRelationship(inRectangle);
+    assertEquals(GeoArea.WITHIN, rel);
+
+    GeoBBox onRectangle = GeoBBoxFactory.makeGeoBBox(PlanetModel.SPHERE, Math.toRadians(-59),
+        Math.toRadians(-64.1),
+        Math.toRadians(18.5),
+        Math.toRadians(27));
+    rel = originalConvexPol.getRelationship(onRectangle);
+    assertEquals(GeoArea.CONTAINS, rel);
+    rel = onRectangle.getRelationship(originalConvexPol);
+    assertEquals(GeoArea.WITHIN, rel);
+    rel = originalConcavePol.getRelationship(onRectangle);
+    assertEquals(GeoArea.OVERLAPS, rel);
+    rel = originalComplexPol.getRelationship(onRectangle);
+    assertEquals(GeoArea.CONTAINS, rel);
+
+  }
+
+  @Test
+  public void testGeoSimplePolygonWithComposite() {
+    GeoShape shape = getCompositeShape();
+
+    //POLYGON((-145.8555 -5.13, -145.8540 -5.13, -145.8540 -5.12, -145.8555 -5.12, -145.8555 -5.13))
+    GeoPolygon polConvex = buildConvexGeoPolygon(-145.8555, -5.13,
+        -145.8540, -5.13,
+        -145.8540, -5.12,
+        -145.8555, -5.12);
+
+    GeoPolygon polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
+        -145.8540, -5.13,
+        -145.8540, -5.12,
+        -145.8555, -5.12);
+
+    int rel = polConvex.getRelationship(shape);
+    assertEquals(GeoArea.DISJOINT,  rel);
+    rel = polConcave.getRelationship(shape);
+    assertEquals(GeoArea.WITHIN,  rel);
+
+    //POLYGON((-145.8555 -5.13, -145.85 -5.13, -145.85 -5.12, -145.8555 -5.12, -145.8555 -5.13))
+    polConvex = buildConvexGeoPolygon(-145.8555, -5.13,
+        -145.85, -5.13,
+        -145.85, -5.12,
+        -145.8555, -5.12);
+
+    polConcave = buildConcaveGeoPolygon(-145.8555, -5.13,
+        -145.85, -5.13,
+        -145.85, -5.12,
+        -145.8555, -5.12);
+
+    rel = polConvex.getRelationship(shape);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(shape);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //POLYGON((-146 -5.18, -145.854 -5.18, -145.854 -5.11, -146 -5.11, -146 -5.18))
+    //Case overlaping on of the shapes
+    polConvex = buildConvexGeoPolygon(-146, -5.18,
+        -145.854, -5.18,
+        -145.854, -5.11,
+        -146, -5.11);
+
+    polConcave = buildConcaveGeoPolygon(-146, -5.18,
+        -145.854, -5.18,
+        -145.854, -5.11,
+        -146, -5.11);
+
+    rel = polConvex.getRelationship(shape);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+    rel = polConcave.getRelationship(shape);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+
+    //POLYGON((-145.88 -5.13, -145.87 -5.13, -145.87 -5.12, -145.88 -5.12, -145.88 -5.13))
+    polConvex = buildConvexGeoPolygon(-145.88, -5.13,
+        -145.87, -5.13,
+        -145.87, -5.12,
+        -145.88, -5.12);
+
+    polConcave = buildConcaveGeoPolygon(-145.88, -5.13,
+        -145.87, -5.13,
+        -145.87, -5.12,
+        -145.88, -5.12);
+
+    rel = polConvex.getRelationship(shape);
+    assertEquals(GeoArea.CONTAINS,  rel);
+    rel = polConcave.getRelationship(shape);
+    assertEquals(GeoArea.OVERLAPS,  rel);
+  }
+
+  private GeoPolygon buildConvexGeoPolygon(double lon1, double lat1,
+                                           double lon2, double lat2,
+                                           double lon3, double lat3,
+                                           double lon4, double lat4) {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points);
+  }
+
+  private GeoPolygon buildConcaveGeoPolygon(double lon1, double lat1,
+                                            double lon2, double lat2,
+                                            double lon3, double lat3,
+                                            double lon4, double lat4) {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    return GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE, points);
+  }
+
+  private GeoPolygon buildComplexGeoPolygon(double lon1, double lat1,
+                                            double lon2, double lat2,
+                                            double lon3, double lat3,
+                                            double lon4, double lat4) {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    GeoPolygonFactory.PolygonDescription pd = new GeoPolygonFactory.PolygonDescription(points);
+    return GeoPolygonFactory.makeLargeGeoPolygon(PlanetModel.SPHERE, Collections.singletonList(pd));
+  }
+
+  private GeoPolygon buildConvexGeoPolygonWithHole(double lon1, double lat1,
+                                                   double lon2, double lat2,
+                                                   double lon3, double lat3,
+                                                   double lon4, double lat4,
+                                                   GeoPolygon hole) {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    //return new GeoConvexPolygon(PlanetModel.SPHERE,points, Collections.singletonList(hole));
+    return GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points, Collections.singletonList(hole));
+  }
+
+  private GeoPolygon buildConcaveGeoPolygonWithHole(double lon1, double lat1,
+                                                    double lon2, double lat2,
+                                                    double lon3, double lat3,
+                                                    double lon4, double lat4,
+                                                    GeoPolygon hole) {
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat1), Math.toRadians(lon1));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat2), Math.toRadians(lon2));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat3), Math.toRadians(lon3));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(lat4), Math.toRadians(lon4));
+    final List<GeoPoint> points = new ArrayList<>();
+    points.add(point1);
+    points.add(point2);
+    points.add(point3);
+    points.add(point4);
+    return GeoPolygonFactory.makeGeoConcavePolygon(PlanetModel.SPHERE, points, Collections.singletonList(hole));
+  }
+
+  private GeoShape getCompositeShape(){
+    //MULTIPOLYGON(((-145.790967486 -5.17543698881, -145.790854979 -5.11348060995, -145.853073512 -5.11339421216, -145.853192037 -5.17535061936, -145.790967486 -5.17543698881)),
+    //((-145.8563923 -5.17527125408, -145.856222168 -5.11332154814, -145.918433943 -5.11317773171, -145.918610092 -5.17512738429, -145.8563923 -5.17527125408)))
+    GeoPoint point1 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17543698881), Math.toRadians(-145.790967486));
+    GeoPoint point2 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11348060995), Math.toRadians(-145.790854979));
+    GeoPoint point3 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11339421216), Math.toRadians(-145.853073512));
+    GeoPoint point4 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17535061936), Math.toRadians(-145.853192037));
+    final List<GeoPoint> points1 = new ArrayList<>();
+    points1.add(point1);
+    points1.add(point2);
+    points1.add(point3);
+    points1.add(point4);
+    GeoPolygon pol1 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE,points1);
+    GeoPoint point5 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17527125408), Math.toRadians(-145.8563923));
+    GeoPoint point6 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11332154814), Math.toRadians(-145.856222168));
+    GeoPoint point7 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.11317773171), Math.toRadians(-145.918433943));
+    GeoPoint point8 = new GeoPoint(PlanetModel.SPHERE, Math.toRadians(-5.17512738429), Math.toRadians(-145.918610092));
+    final List<GeoPoint> points2 = new ArrayList<>();
+    points2.add(point5);
+    points2.add(point6);
+    points2.add(point7);
+    points2.add(point8);
+    GeoPolygon pol2 = GeoPolygonFactory.makeGeoPolygon(PlanetModel.SPHERE, points2);
+    GeoCompositeMembershipShape composite = new GeoCompositeMembershipShape();
+    composite.addShape(pol1);
+    composite.addShape(pol2);
+    return composite;
+  }
+}
