diff --git a/lucene/core/src/java/org/apache/lucene/geo/EdgeTree.java b/lucene/core/src/java/org/apache/lucene/geo/EdgeTree.java
index 55a3379a65..40766b1bfe 100644
--- a/lucene/core/src/java/org/apache/lucene/geo/EdgeTree.java
+++ b/lucene/core/src/java/org/apache/lucene/geo/EdgeTree.java
@@ -73,6 +73,16 @@ public abstract class EdgeTree {
     this.tree = createTree(lats, lons);
   }
 
+  private boolean sharesDateline(double minLat, double maxLat, double minLon, double maxLon) {
+    if ((this.minLon == GeoUtils.MIN_LON_INCL && maxLon == GeoEncodingUtils.MAX_LON_QUANTIZED) ||
+        (this.maxLon == GeoUtils.MAX_LON_INCL && minLon == GeoUtils.MIN_LON_INCL)) {
+      if ((minLat > this.maxLat || maxLat < minLat) == false) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   /** Returns relation to the provided triangle */
   public Relation relateTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
     // compute bounding box of triangle
@@ -81,7 +91,11 @@ public abstract class EdgeTree {
     double maxLat = StrictMath.max(StrictMath.max(ay, by), cy);
     double maxLon = StrictMath.max(StrictMath.max(ax, bx), cx);
     if (minLat <= maxY && minLon <= maxX) {
-      Relation relation = internalComponentRelateTriangle(ax, ay, bx, by, cx, cy);
+      if (maxLon < this.minLon || minLon > this.maxLon || maxLat < this.minLat || minLat > this.maxLat) {
+        return sharesDateline(minLat, maxLat, minLon, maxLon) ? Relation.CELL_CROSSES_QUERY : Relation.CELL_OUTSIDE_QUERY;
+      }
+
+      Relation relation = componentRelateTriangle(ax, ay, bx, by, cx, cy);
       if (relation != Relation.CELL_OUTSIDE_QUERY) {
         return relation;
       }
@@ -98,13 +112,22 @@ public abstract class EdgeTree {
         }
       }
     }
-    return Relation.CELL_OUTSIDE_QUERY;
+    return sharesDateline(minLat, maxLat, minLon, maxLon) ? Relation.CELL_CROSSES_QUERY : Relation.CELL_OUTSIDE_QUERY;
   }
 
   /** Returns relation to the provided rectangle */
   public Relation relate(double minLat, double maxLat, double minLon, double maxLon) {
     if (minLat <= maxY && minLon <= maxX) {
-      Relation relation = internalComponentRelate(minLat, maxLat, minLon, maxLon);
+      // if the bounding boxes are disjoint then the shape does not cross
+      if (maxLon < this.minLon || minLon > this.maxLon || maxLat < this.minLat || minLat > this.maxLat) {
+        return sharesDateline(minLat, maxLat, minLon, maxLon) ? Relation.CELL_CROSSES_QUERY : Relation.CELL_OUTSIDE_QUERY;
+      }
+      // if the rectangle fully encloses us, we cross.
+      if (minLat <= this.minLat && maxLat >= this.maxLat && minLon <= this.minLon && maxLon >= this.maxLon) {
+        return Relation.CELL_CROSSES_QUERY;
+      }
+
+      Relation relation = componentRelate(minLat, maxLat, minLon, maxLon);
       if (relation != Relation.CELL_OUTSIDE_QUERY) {
         return relation;
       }
@@ -121,7 +144,7 @@ public abstract class EdgeTree {
         }
       }
     }
-    return Relation.CELL_OUTSIDE_QUERY;
+    return sharesDateline(minLat, maxLat, minLon, maxLon) ? Relation.CELL_CROSSES_QUERY : Relation.CELL_OUTSIDE_QUERY;
   }
 
   /** Returns relation to the provided rectangle for this component */
@@ -130,33 +153,6 @@ public abstract class EdgeTree {
   /** Returns relation to the provided triangle for this component */
   protected abstract Relation componentRelateTriangle(double ax, double ay, double bx, double by, double cx, double cy);
 
-
-  private Relation internalComponentRelateTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
-    // compute bounding box of triangle
-    double minLat = StrictMath.min(StrictMath.min(ay, by), cy);
-    double minLon = StrictMath.min(StrictMath.min(ax, bx), cx);
-    double maxLat = StrictMath.max(StrictMath.max(ay, by), cy);
-    double maxLon = StrictMath.max(StrictMath.max(ax, bx), cx);
-    if (maxLon < this.minLon || minLon > this.maxLon || maxLat < this.minLat || minLat > this.maxLat) {
-      return Relation.CELL_OUTSIDE_QUERY;
-    }
-    return componentRelateTriangle(ax, ay, bx, by, cx, cy);
-  }
-
-
-  /** Returns relation to the provided rectangle for this component */
-  protected Relation internalComponentRelate(double minLat, double maxLat, double minLon, double maxLon) {
-    // if the bounding boxes are disjoint then the shape does not cross
-    if (maxLon < this.minLon || minLon > this.maxLon || maxLat < this.minLat || minLat > this.maxLat) {
-      return Relation.CELL_OUTSIDE_QUERY;
-    }
-    // if the rectangle fully encloses us, we cross.
-    if (minLat <= this.minLat && maxLat >= this.maxLat && minLon <= this.minLon && maxLon >= this.maxLon) {
-      return Relation.CELL_CROSSES_QUERY;
-    }
-    return componentRelate(minLat, maxLat, minLon, maxLon);
-  }
-
   /** Creates tree from sorted components (with range low and high inclusive) */
   protected static EdgeTree createTree(EdgeTree components[], int low, int high, boolean splitX) {
     if (low > high) {
diff --git a/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java b/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
index 00b7252fe8..c9007f3673 100644
--- a/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
+++ b/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
@@ -45,7 +45,7 @@ public final class GeoEncodingUtils {
 
   public static final int MIN_LON_ENCODED = encodeLongitude(MIN_LON_INCL);
   public static final int MAX_LON_ENCODED = encodeLongitude(MAX_LON_INCL);
-
+  public static final double MAX_LON_QUANTIZED = decodeLongitude(MAX_LON_ENCODED);
 
   // No instance:
   private GeoEncodingUtils() {
diff --git a/lucene/sandbox/src/java/org/apache/lucene/geo/Line2D.java b/lucene/sandbox/src/java/org/apache/lucene/geo/Line2D.java
index 94d92224fa..f28b743c14 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/geo/Line2D.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/geo/Line2D.java
@@ -16,7 +16,7 @@
  */
 package org.apache.lucene.geo;
 
-import org.apache.lucene.index.PointValues;
+import org.apache.lucene.index.PointValues.Relation;
 
 /**
  * 2D line implementation represented as a balanced interval tree of edges.
@@ -41,15 +41,15 @@ public final class Line2D extends EdgeTree {
   }
 
   @Override
-  protected PointValues.Relation componentRelate(double minLat, double maxLat, double minLon, double maxLon) {
+  protected Relation componentRelate(double minLat, double maxLat, double minLon, double maxLon) {
     if (tree.crosses(minLat, maxLat, minLon, maxLon)) {
-      return PointValues.Relation.CELL_CROSSES_QUERY;
+      return Relation.CELL_CROSSES_QUERY;
     }
-    return PointValues.Relation.CELL_OUTSIDE_QUERY;
+    return Relation.CELL_OUTSIDE_QUERY;
   }
 
   @Override
-  protected PointValues.Relation componentRelateTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
+  protected Relation componentRelateTriangle(double ax, double ay, double bx, double by, double cx, double cy) {
     return tree.relateTriangle(ax, ay, bx, by, cx, cy);
   }
 }
\ No newline at end of file
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonShape.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonShape.java
index ce994873b0..facb0fa444 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonShape.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonShape.java
@@ -401,4 +401,91 @@ public class TestLatLonShape extends LuceneTestCase {
     assertEquals(PointValues.Relation.CELL_CROSSES_QUERY, rel);
   }
 
+  public void testLUCENE8721() throws Exception {
+    ///// index /////
+    Directory dir = newDirectory();
+    RandomIndexWriter w = new RandomIndexWriter(random(), dir);
+    Document doc = new Document();
+
+    // western hemisphere polygon sharing dateline
+    Polygon indexPoly = new Polygon(
+        new double[] {-7.5d, 15d, 15d, 0d, -7.5d},
+        new double[] {-180d, -180d, -176d, -176d, -180d}
+    );
+    Field[] fields = LatLonShape.createIndexableFields("test", indexPoly);
+    for (Field f : fields) {
+      doc.add(f);
+    }
+    w.addDocument(doc);
+
+    // western hemisphere line touching dateline at -180, 15
+    doc = new Document();
+    Line indexLine = new Line(
+        new double[] {15d, 16d, 22d, 23d},
+        new double[] {-180d, -179d, -178d, -176d}
+    );
+    fields = LatLonShape.createIndexableFields("test", indexLine);
+    for (Field f : fields) {
+      doc.add(f);
+    }
+    w.addDocument(doc);
+
+    // eastern hemisphere polygon sharing dateline
+    doc = new Document();
+    indexPoly = new Polygon(
+        new double[] {67.5d, 75d, 75d, 70d, 67.5d},
+        new double[] {176d, 176d, 180d, 180d, 176d}
+    );
+    fields = LatLonShape.createIndexableFields("test", indexPoly);
+    for (Field f : fields) {
+      doc.add(f);
+    }
+    w.addDocument(doc);
+
+    // western hemisphere line touching dateline at -180, 15
+    doc = new Document();
+    indexLine = new Line(
+        new double[] {75d, 76d, 82d, 75d},
+        new double[] {176, 177d, 178d, 180d}
+    );
+    fields = LatLonShape.createIndexableFields("test", indexLine);
+    for (Field f : fields) {
+      doc.add(f);
+    }
+    w.addDocument(doc);
+    w.forceMerge(1);
+
+    ///// search //////
+    IndexReader reader = w.getReader();
+    w.close();
+    IndexSearcher searcher = newSearcher(reader);
+
+    // eastern hemisphere search poly
+    Polygon searchPoly = new Polygon(new double[] {-7.5d, 15d, 15d, 0d, -7.5d},
+        new double[] {180d, 180d, 170d, 170d, 180d});
+
+    Query q = LatLonShape.newPolygonQuery("test", QueryRelation.INTERSECTS, searchPoly);
+    assertEquals(2, searcher.count(q));
+
+    // search using a eastern hemisphere line that intersects western geometry at (180, 15)
+    Line searchLine = new Line(new double[] {23d, 22d, 16d, 15d},
+        new double[] {177d, 178d, 179d, 180d});
+    q = LatLonShape.newLineQuery("test", QueryRelation.INTERSECTS, searchLine);
+    assertEquals(2, searcher.count(q));
+
+    // western hemisphere search poly
+    searchPoly = new Polygon(new double[] {67.5d, 75d, 75d, 60d, 67.5d},
+        new double[] {-170d, -170d, -180d, -180d, -170d});
+
+    q = LatLonShape.newPolygonQuery("test", QueryRelation.INTERSECTS, searchPoly);
+    assertEquals(2, searcher.count(q));
+
+    // search using a eastern hemisphere line that intersects western geometry at (180, 15)
+    searchLine = new Line(new double[] {80d, 81d, 82d, 83d},
+        new double[] {-177d, -178d, -179d, -180d});
+    q = LatLonShape.newLineQuery("test", QueryRelation.INTERSECTS, searchLine);
+    assertEquals(2, searcher.count(q));
+
+    IOUtils.close(w, reader, dir);
+  }
 }
