You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2018/11/05 18:56:38 UTC

[20/26] lucene-solr:jira/solr-12730: LUCENE-8555: Add dateline crossing support to LatLonShapeBoundingBoxQuery

LUCENE-8555: Add dateline crossing support to LatLonShapeBoundingBoxQuery


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/31d7dfe6
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/31d7dfe6
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/31d7dfe6

Branch: refs/heads/jira/solr-12730
Commit: 31d7dfe6b1b283e4678d1abd82af9eac680afe45
Parents: 0cbefe8
Author: Nicholas Knize <nk...@gmail.com>
Authored: Fri Nov 2 12:15:22 2018 -0500
Committer: Nicholas Knize <nk...@gmail.com>
Committed: Fri Nov 2 12:15:22 2018 -0500

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  2 +
 .../org/apache/lucene/geo/GeoEncodingUtils.java |  4 +
 .../org/apache/lucene/document/LatLonShape.java |  2 -
 .../document/LatLonShapeBoundingBoxQuery.java   | 95 +++++++++++++++++---
 .../document/BaseLatLonShapeTestCase.java       | 24 +++--
 .../document/TestLatLonLineShapeQueries.java    | 31 ++++++-
 .../document/TestLatLonPointShapeQueries.java   | 10 ++-
 .../document/TestLatLonPolygonShapeQueries.java | 29 +++++-
 8 files changed, 159 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 3461e57..59d81d0 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -244,6 +244,8 @@ New Features
 * LUCENE-8554: Add new LatLonShapeLineQuery that queries indexed LatLonShape fields
   by arbitrary lines. (Nick Knize)
 
+* LUCENE-8555: Add dateline crossing support to LatLonShapeBoundingBoxQuery. (Nick Knize)
+
 Improvements:
 
 * LUCENE-8521: Change LatLonShape encoding to 7 dimensions instead of 6; where the

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
----------------------------------------------------------------------
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 663cb2e..00b7252 100644
--- a/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
+++ b/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
@@ -43,6 +43,10 @@ public final class GeoEncodingUtils {
   private static final double LON_SCALE = (0x1L<<BITS)/360.0D;
   private static final double LON_DECODE = 1/LON_SCALE;
 
+  public static final int MIN_LON_ENCODED = encodeLongitude(MIN_LON_INCL);
+  public static final int MAX_LON_ENCODED = encodeLongitude(MAX_LON_INCL);
+
+
   // No instance:
   private GeoEncodingUtils() {
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShape.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShape.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShape.java
index 580d574..7c074cf 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShape.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShape.java
@@ -117,8 +117,6 @@ public class LatLonShape {
   }
 
   /** create a query to find all polygons that intersect a defined bounding box
-   *  note: does not currently support dateline crossing boxes
-   * todo split dateline crossing boxes into two queries like {@link LatLonPoint#newBoxQuery}
    **/
   public static Query newBoxQuery(String field, QueryRelation queryRelation, double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) {
     return new LatLonShapeBoundingBoxQuery(field, queryRelation, minLatitude, maxLatitude, minLongitude, maxLongitude);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
index d1c3e14..b4f7f4b 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
@@ -25,6 +25,8 @@ import org.apache.lucene.util.FutureArrays;
 import org.apache.lucene.util.NumericUtils;
 
 import static org.apache.lucene.document.LatLonShape.BYTES;
+import static org.apache.lucene.geo.GeoEncodingUtils.MAX_LON_ENCODED;
+import static org.apache.lucene.geo.GeoEncodingUtils.MIN_LON_ENCODED;
 import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
@@ -43,6 +45,7 @@ import static org.apache.lucene.geo.GeoUtils.orient;
  **/
 final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
   final byte[] bbox;
+  final byte[] west;
   final int minX;
   final int maxX;
   final int minY;
@@ -50,23 +53,59 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
 
   public LatLonShapeBoundingBoxQuery(String field, LatLonShape.QueryRelation queryRelation, double minLat, double maxLat, double minLon, double maxLon) {
     super(field, queryRelation);
-    if (minLon > maxLon) {
-      throw new IllegalArgumentException("dateline crossing bounding box queries are not supported for [" + field + "]");
-    }
+
     this.bbox = new byte[4 * LatLonShape.BYTES];
-    this.minX = encodeLongitudeCeil(minLon);
-    this.maxX = encodeLongitude(maxLon);
+    int minXenc = encodeLongitudeCeil(minLon);
+    int maxXenc = encodeLongitude(maxLon);
     this.minY = encodeLatitudeCeil(minLat);
     this.maxY = encodeLatitude(maxLat);
-    LatLonShape.encodeTriangleBoxVal(this.minY, bbox, 0);
-    LatLonShape.encodeTriangleBoxVal(this.minX, bbox, BYTES);
-    LatLonShape.encodeTriangleBoxVal(this.maxY, bbox, 2 * BYTES);
-    LatLonShape.encodeTriangleBoxVal(this.maxX, bbox, 3 * BYTES);
+
+    if (minLon > maxLon == true) {
+      // crossing dateline is split into east/west boxes
+      this.west = new byte[4 * LatLonShape.BYTES];
+      this.minX = minXenc;
+      this.maxX = maxXenc;
+      encode(MIN_LON_ENCODED, this.maxX, this.minY, this.maxY, this.west);
+      encode(this.minX, MAX_LON_ENCODED, this.minY, this.maxY, this.bbox);
+    } else {
+      // encodeLongitudeCeil may cause minX to be > maxX iff
+      // the delta between the longtude < the encoding resolution
+      if (minXenc > maxXenc) {
+        minXenc = maxXenc;
+      }
+      this.west = null;
+      this.minX = minXenc;
+      this.maxX = maxXenc;
+      encode(this.minX, this.maxX, this.minY, this.maxY, bbox);
+    }
+  }
+
+  /** encodes a bounding box into the provided byte array */
+  private static void encode(final int minX, final int maxX, final int minY, final int maxY, byte[] b) {
+    if (b == null) {
+      b = new byte[4 * LatLonShape.BYTES];
+    }
+    LatLonShape.encodeTriangleBoxVal(minY, b, 0);
+    LatLonShape.encodeTriangleBoxVal(minX, b, BYTES);
+    LatLonShape.encodeTriangleBoxVal(maxY, b, 2 * BYTES);
+    LatLonShape.encodeTriangleBoxVal(maxX, b, 3 * BYTES);
   }
 
   @Override
   protected Relation relateRangeBBoxToQuery(int minXOffset, int minYOffset, byte[] minTriangle,
                                             int maxXOffset, int maxYOffset, byte[] maxTriangle) {
+    Relation eastRelation = compareBBoxToRangeBBox(this.bbox, minXOffset, minYOffset, minTriangle, maxXOffset, maxYOffset, maxTriangle);
+    if (this.crossesDateline() && eastRelation == Relation.CELL_OUTSIDE_QUERY) {
+      return compareBBoxToRangeBBox(this.west, minXOffset, minYOffset, minTriangle, maxXOffset, maxYOffset, maxTriangle);
+    }
+
+    return eastRelation;
+  }
+
+  /** static utility method to compare a bbox with a range of triangles (just the bbox of the triangle collection) */
+  protected static Relation compareBBoxToRangeBBox(final byte[] bbox,
+                                                   int minXOffset, int minYOffset, byte[] minTriangle,
+                                                   int maxXOffset, int maxYOffset, byte[] maxTriangle) {
     // check bounding box (DISJOINT)
     if (FutureArrays.compareUnsigned(minTriangle, minXOffset, minXOffset + BYTES, bbox, 3 * BYTES, 4 * BYTES) > 0 ||
         FutureArrays.compareUnsigned(maxTriangle, maxXOffset, maxXOffset + BYTES, bbox, BYTES, 2 * BYTES) < 0 ||
@@ -87,6 +126,7 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
   /** returns true if the query matches the encoded triangle */
   @Override
   protected boolean queryMatches(byte[] t) {
+    // decode indexed triangle
     long a = NumericUtils.sortableBytesToLong(t, 4 * LatLonShape.BYTES);
     long b = NumericUtils.sortableBytesToLong(t, 5 * LatLonShape.BYTES);
     long c = NumericUtils.sortableBytesToLong(t, 6 * LatLonShape.BYTES);
@@ -99,11 +139,19 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
     int cY = (int)(c & 0x00000000FFFFFFFFL);
 
     if (queryRelation == LatLonShape.QueryRelation.WITHIN) {
-      return bboxContainsTriangle(aX, aY, bX, bY, cX, cY, minX, maxX, minY, maxY);
+      return queryContainsTriangle(aX, aY, bX, bY, cX, cY);
     }
     return queryMatches(aX, aY, bX, bY, cX, cY);
   }
 
+  private boolean queryContainsTriangle(int ax, int ay, int bx, int by, int cx, int cy) {
+    if (this.crossesDateline() == true) {
+      return bboxContainsTriangle(ax, ay, bx, by, cx, cy, MIN_LON_ENCODED, this.maxX, this.minY, this.maxY)
+          || bboxContainsTriangle(ax, ay, bx, by, cx, cy, this.minX, MAX_LON_ENCODED, this.minY, this.maxY);
+    }
+    return bboxContainsTriangle(ax, ay, bx, by, cx, cy, minX, maxX, minY, maxY);
+  }
+
   /** static utility method to check if a bounding box contains a point */
   private static boolean bboxContainsPoint(int x, int y, int minX, int maxX, int minY, int maxY) {
     return (x < minX || x > maxX || y < minY || y > maxY) == false;
@@ -119,6 +167,10 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
 
   /** instance method to check if query box contains point */
   private boolean queryContainsPoint(int x, int y) {
+    if (this.crossesDateline() == true) {
+      return bboxContainsPoint(x, y, MIN_LON_ENCODED, this.maxX, this.minY, this.maxY)
+          || bboxContainsPoint(x, y, this.minX, MAX_LON_ENCODED, this.minY, this.maxY);
+    }
     return bboxContainsPoint(x, y, this.minX, this.maxX, this.minY, this.maxY);
   }
 
@@ -135,7 +187,12 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
     int tMaxY = StrictMath.max(StrictMath.max(aY, bY), cY);
 
     // 2. check bounding boxes are disjoint
-    if (tMaxX < minX || tMinX > maxX || tMinY > maxY || tMaxY < minY) {
+    if (this.crossesDateline() == true) {
+      if (boxesAreDisjoint(tMinX, tMaxX, tMinY, tMaxY, MIN_LON_ENCODED, this.maxX, this.minY, this.maxY)
+          && boxesAreDisjoint(tMinX, tMaxX, tMinY, tMaxY, this.minX, MAX_LON_ENCODED, this.minY, this.maxY)) {
+        return false;
+      }
+    } else if (tMaxX < minX || tMinX > maxX || tMinY > maxY || tMaxY < minY) {
       return false;
     }
 
@@ -210,6 +267,10 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
 
   /** returns true if the edge (defined by (ax, ay) (bx, by)) intersects the query */
   private boolean edgeIntersectsQuery(int ax, int ay, int bx, int by) {
+    if (this.crossesDateline() == true) {
+      return edgeIntersectsBox(ax, ay, bx, by, MIN_LON_ENCODED, this.maxX, this.minY, this.maxY)
+          || edgeIntersectsBox(ax, ay, bx, by, this.minX, MAX_LON_ENCODED, this.minY, this.maxY);
+    }
     return edgeIntersectsBox(ax, ay, bx, by, this.minX, this.maxX, this.minY, this.maxY);
   }
 
@@ -230,6 +291,10 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
     return (aMaxX < bMinX || aMinX > bMaxX || aMaxY < bMinY || aMinY > bMaxY);
   }
 
+  public boolean crossesDateline() {
+    return minX > maxX;
+  }
+
   @Override
   public boolean equals(Object o) {
     return sameClassAs(o) && equalsTo(getClass().cast(o));
@@ -237,13 +302,16 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
 
   @Override
   protected boolean equalsTo(Object o) {
-    return super.equalsTo(o) && Arrays.equals(bbox, ((LatLonShapeBoundingBoxQuery)o).bbox);
+    return super.equalsTo(o)
+        && Arrays.equals(bbox, ((LatLonShapeBoundingBoxQuery)o).bbox)
+        && Arrays.equals(west, ((LatLonShapeBoundingBoxQuery)o).west);
   }
 
   @Override
   public int hashCode() {
     int hash = super.hashCode();
     hash = 31 * hash + Arrays.hashCode(bbox);
+    hash = 31 * hash + Arrays.hashCode(west);
     return hash;
   }
 
@@ -265,6 +333,9 @@ final class LatLonShapeBoundingBoxQuery extends LatLonShapeQuery {
     sb.append(decodeLongitude(minX));
     sb.append(" TO ");
     sb.append(decodeLongitude(maxX));
+    if (maxX < minX) {
+      sb.append(" [crosses dateline!]");
+    }
     sb.append(")");
     return sb.toString();
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/sandbox/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java b/lucene/sandbox/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
index 106446e..942979b 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
@@ -284,16 +284,7 @@ public abstract class BaseLatLonShapeTestCase extends LuceneTestCase {
       }
 
       // BBox
-      Rectangle rect;
-      // quantizing the bbox may end up w/ bounding boxes crossing dateline...
-      // todo add support for bounding boxes crossing dateline
-      while (true) {
-        rect = GeoTestUtil.nextBoxNotCrossingDateline();
-        if (decodeLongitude(encodeLongitudeCeil(rect.minLon)) <= decodeLongitude(encodeLongitude(rect.maxLon)) &&
-            decodeLatitude(encodeLatitudeCeil(rect.minLat)) <= decodeLatitude(encodeLatitude(rect.maxLat))) {
-          break;
-        }
-      }
+      Rectangle rect = GeoTestUtil.nextBox();
       QueryRelation queryRelation = RandomPicks.randomFrom(random(), QueryRelation.values());
       Query query = newRectQuery(FIELD_NAME, queryRelation, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
 
@@ -328,6 +319,8 @@ public abstract class BaseLatLonShapeTestCase extends LuceneTestCase {
         assertEquals(docID, docIDToID.nextDoc());
         int id = (int) docIDToID.longValue();
         boolean expected;
+        double qMinLon = quantizeLonCeil(rect.minLon);
+        double qMaxLon = quantizeLon(rect.maxLon);
         if (liveDocs != null && liveDocs.get(docID) == false) {
           // document is deleted
           expected = false;
@@ -335,8 +328,12 @@ public abstract class BaseLatLonShapeTestCase extends LuceneTestCase {
           expected = false;
         } else {
           // check quantized poly against quantized query
-          expected = getValidator(queryRelation).testBBoxQuery(quantizeLatCeil(rect.minLat), quantizeLat(rect.maxLat),
-              quantizeLonCeil(rect.minLon), quantizeLon(rect.maxLon), shapes[id]);
+          if (qMinLon > qMaxLon && rect.crossesDateline() == false) {
+            // if the quantization creates a false dateline crossing (because of encodeCeil):
+            // then do not use encodeCeil
+            qMinLon = quantizeLon(rect.minLon);
+          }
+          expected = getValidator(queryRelation).testBBoxQuery(quantizeLatCeil(rect.minLat), quantizeLat(rect.maxLat), qMinLon, qMaxLon, shapes[id]);
         }
 
         if (hits.get(docID) != expected) {
@@ -351,8 +348,7 @@ public abstract class BaseLatLonShapeTestCase extends LuceneTestCase {
           b.append("  query=" + query + " docID=" + docID + "\n");
           b.append("  shape=" + shapes[id] + "\n");
           b.append("  deleted?=" + (liveDocs != null && liveDocs.get(docID) == false));
-          b.append("  rect=Rectangle(" + quantizeLatCeil(rect.minLat) + " TO " + quantizeLat(rect.maxLat) + " lon=" + quantizeLonCeil(rect.minLon) + " TO " + quantizeLon(rect.maxLon) + ")\n");
-          if (true) {
+          b.append("  rect=Rectangle(lat=" + quantizeLatCeil(rect.minLat) + " TO " + quantizeLat(rect.maxLat) + " lon=" + qMinLon + " TO " + quantizeLon(rect.maxLon) + ")\n");          if (true) {
             fail("wrong hit (first of possibly more):\n\n" + b);
           } else {
             System.out.println(b.toString());

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonLineShapeQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonLineShapeQueries.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonLineShapeQueries.java
index 5c83448..3919e17 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonLineShapeQueries.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonLineShapeQueries.java
@@ -20,11 +20,15 @@ import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
 import org.apache.lucene.document.LatLonShape.QueryRelation;
 import org.apache.lucene.geo.EdgeTree;
 import org.apache.lucene.geo.GeoTestUtil;
+import org.apache.lucene.geo.GeoUtils;
 import org.apache.lucene.geo.Line;
 import org.apache.lucene.geo.Line2D;
 import org.apache.lucene.geo.Polygon2D;
 import org.apache.lucene.index.PointValues.Relation;
 
+import static org.apache.lucene.geo.GeoUtils.MAX_LON_INCL;
+import static org.apache.lucene.geo.GeoUtils.MIN_LON_INCL;
+
 /** random bounding box and polygon query tests for random generated {@link Line} types */
 public class TestLatLonLineShapeQueries extends BaseLatLonShapeTestCase {
 
@@ -78,13 +82,32 @@ public class TestLatLonLineShapeQueries extends BaseLatLonShapeTestCase {
       Line l = (Line)shape;
       if (queryRelation == QueryRelation.WITHIN) {
         // within: bounding box of shape should be within query box
-        return minLat <= quantizeLat(l.minLat) && maxLat >= quantizeLat(l.maxLat)
-            && minLon <= quantizeLon(l.minLon) && maxLon >= quantizeLon(l.maxLon);
+        double lMinLat = quantizeLat(l.minLat);
+        double lMinLon = quantizeLon(l.minLon);
+        double lMaxLat = quantizeLat(l.maxLat);
+        double lMaxLon = quantizeLon(l.maxLon);
+
+        if (minLon > maxLon) {
+          // crosses dateline:
+          return minLat <= lMinLat && maxLat >= lMaxLat
+              && ((GeoUtils.MIN_LON_INCL <= lMinLon && maxLon >= lMaxLon)
+              || (minLon <= lMinLon && GeoUtils.MAX_LON_INCL >= lMaxLon));
+        }
+        return minLat <= lMinLat && maxLat >= lMaxLat
+            && minLon <= lMinLon && maxLon >= lMaxLon;
       }
 
-      // to keep it simple we convert the bbox into a polygon and use poly2d
       Line2D line = Line2D.create(quantizeLine(l));
-      Relation r = line.relate(minLat, maxLat, minLon, maxLon);
+      Relation r;
+      if (minLon > maxLon) {
+        // crosses dateline:
+        r = line.relate(minLat, maxLat, MIN_LON_INCL, maxLon);
+        if (r == Relation.CELL_OUTSIDE_QUERY) {
+          r = line.relate(minLat, maxLat, minLon, MAX_LON_INCL);
+        }
+      } else {
+        r = line.relate(minLat, maxLat, minLon, maxLon);
+      }
 
       if (queryRelation == QueryRelation.DISJOINT) {
         return r == Relation.CELL_OUTSIDE_QUERY;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
index 3f3c30d..96b026c 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
@@ -81,9 +81,13 @@ public class TestLatLonPointShapeQueries extends BaseLatLonShapeTestCase {
     @Override
     public boolean testBBoxQuery(double minLat, double maxLat, double minLon, double maxLon, Object shape) {
       Point p = (Point)shape;
-      double lat = decodeLatitude(encodeLatitude(p.lat));
-      double lon = decodeLongitude(encodeLongitude(p.lon));
-      boolean isDisjoint = lat < minLat || lat > maxLat || lon < minLon || lon > maxLon;
+      double lat = quantizeLat(p.lat);
+      double lon = quantizeLon(p.lon);
+      boolean isDisjoint = lat < minLat || lat > maxLat;
+
+      isDisjoint = isDisjoint || ((minLon > maxLon)
+          ? lon < minLon && lon > maxLon
+          : lon < minLon || lon > maxLon);
       if (queryRelation == QueryRelation.DISJOINT) {
         return isDisjoint;
       }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/31d7dfe6/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPolygonShapeQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPolygonShapeQueries.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPolygonShapeQueries.java
index 3a82dd0..24cba64 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPolygonShapeQueries.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPolygonShapeQueries.java
@@ -26,6 +26,9 @@ import org.apache.lucene.geo.Polygon2D;
 import org.apache.lucene.geo.Tessellator;
 import org.apache.lucene.index.PointValues.Relation;
 
+import static org.apache.lucene.geo.GeoUtils.MAX_LON_INCL;
+import static org.apache.lucene.geo.GeoUtils.MIN_LON_INCL;
+
 /** random bounding box and polygon query tests for random indexed {@link Polygon} types */
 public class TestLatLonPolygonShapeQueries extends BaseLatLonShapeTestCase {
 
@@ -68,12 +71,32 @@ public class TestLatLonPolygonShapeQueries extends BaseLatLonShapeTestCase {
       Polygon p = (Polygon)shape;
       if (queryRelation == QueryRelation.WITHIN) {
         // within: bounding box of shape should be within query box
-        return minLat <= quantizeLat(p.minLat) && maxLat >= quantizeLat(p.maxLat)
-            && minLon <= quantizeLon(p.minLon) && maxLon >= quantizeLon(p.maxLon);
+        double pMinLat = quantizeLat(p.minLat);
+        double pMinLon = quantizeLon(p.minLon);
+        double pMaxLat = quantizeLat(p.maxLat);
+        double pMaxLon = quantizeLon(p.maxLon);
+
+        if (minLon > maxLon) {
+          // crosses dateline:
+          return minLat <= pMinLat && maxLat >= pMaxLat
+              && ((MIN_LON_INCL <= pMinLon && maxLon >= pMaxLon)
+              ||  (minLon <= pMinLon && MAX_LON_INCL >= pMaxLon));
+        }
+        return minLat <= pMinLat && maxLat >= pMaxLat
+            && minLon <= pMinLon && maxLon >= pMaxLon;
       }
 
       Polygon2D poly = Polygon2D.create(quantizePolygon(p));
-      Relation r = poly.relate(minLat, maxLat, minLon, maxLon);
+      Relation r;
+      if (minLon > maxLon) {
+        // crosses dateline:
+        r = poly.relate(minLat, maxLat, MIN_LON_INCL, maxLon);
+        if (r == Relation.CELL_OUTSIDE_QUERY) {
+          r = poly.relate(minLat, maxLat, minLon, MAX_LON_INCL);
+        }
+      } else {
+        r = poly.relate(minLat, maxLat, minLon, maxLon);
+      }
       if (queryRelation == QueryRelation.DISJOINT) {
         return r == Relation.CELL_OUTSIDE_QUERY;
       }