You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by iv...@apache.org on 2020/11/03 08:01:41 UTC

[lucene-solr] branch branch_8x updated (d2c88e3 -> 5d68325)

This is an automated email from the ASF dual-hosted git repository.

ivera pushed a change to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git.


    from d2c88e3  SOLR-14961 ZkMaintenanceUtils.clean doesn't remove zk nodes with same length
     new e30959f  LUCENE-9552: Adds a LatLonPoint query that accepts an array of LatLonGeometries (#1940)
     new 5d68325  fix changes

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 lucene/CHANGES.txt                                 |   3 +-
 .../document/LatLonDocValuesDistanceQuery.java     | 148 --------
 .../lucene/document/LatLonDocValuesField.java      |  28 +-
 ...va => LatLonDocValuesPointInGeometryQuery.java} |  49 +--
 .../org/apache/lucene/document/LatLonPoint.java    |  28 +-
 ...nQuery.java => LatLonPointInGeometryQuery.java} |  66 ++--
 .../document/LatLonShapeBoundingBoxQuery.java      |  32 +-
 .../src/java/org/apache/lucene/geo/Circle.java     |  10 +-
 .../org/apache/lucene/geo/GeoEncodingUtils.java    |  12 +-
 .../java/org/apache/lucene/geo/Rectangle2D.java    |  21 +-
 .../lucene/document/BaseLatLonShapeTestCase.java   |   4 +-
 .../apache/lucene/document/BaseShapeTestCase.java  |  31 +-
 .../document/TestLatLonPointShapeQueries.java      |  19 +-
 .../apache/lucene/document/TestLatLonShape.java    |   8 +-
 .../src/test/org/apache/lucene/geo/TestCircle.java |   6 +-
 .../lucene/search/TestLatLonDocValuesQueries.java  |  11 +-
 .../lucene/search/TestLatLonPointQueries.java      |   6 +
 .../apache/lucene/geo/BaseGeoPointTestCase.java    | 415 ++++++++++-----------
 .../java/org/apache/lucene/geo/GeoTestUtil.java    |   2 +-
 19 files changed, 376 insertions(+), 523 deletions(-)
 delete mode 100644 lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java
 rename lucene/core/src/java/org/apache/lucene/document/{LatLonDocValuesPointInPolygonQuery.java => LatLonDocValuesPointInGeometryQuery.java} (72%)
 rename lucene/core/src/java/org/apache/lucene/document/{LatLonPointInPolygonQuery.java => LatLonPointInGeometryQuery.java} (80%)


[lucene-solr] 01/02: LUCENE-9552: Adds a LatLonPoint query that accepts an array of LatLonGeometries (#1940)

Posted by iv...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ivera pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git

commit e30959f8cab9d56dfcbb8cd41efb64cba3714d3b
Author: Ignacio Vera <iv...@apache.org>
AuthorDate: Tue Nov 3 08:51:28 2020 +0100

    LUCENE-9552: Adds a LatLonPoint query that accepts an array of LatLonGeometries (#1940)
---
 lucene/CHANGES.txt                                 |   2 +
 .../document/LatLonDocValuesDistanceQuery.java     | 148 --------
 .../lucene/document/LatLonDocValuesField.java      |  28 +-
 ...va => LatLonDocValuesPointInGeometryQuery.java} |  49 +--
 .../org/apache/lucene/document/LatLonPoint.java    |  28 +-
 ...nQuery.java => LatLonPointInGeometryQuery.java} |  66 ++--
 .../document/LatLonShapeBoundingBoxQuery.java      |  32 +-
 .../src/java/org/apache/lucene/geo/Circle.java     |  10 +-
 .../org/apache/lucene/geo/GeoEncodingUtils.java    |  12 +-
 .../java/org/apache/lucene/geo/Rectangle2D.java    |  21 +-
 .../lucene/document/BaseLatLonShapeTestCase.java   |   4 +-
 .../apache/lucene/document/BaseShapeTestCase.java  |  31 +-
 .../document/TestLatLonPointShapeQueries.java      |  19 +-
 .../apache/lucene/document/TestLatLonShape.java    |   8 +-
 .../src/test/org/apache/lucene/geo/TestCircle.java |   6 +-
 .../lucene/search/TestLatLonDocValuesQueries.java  |  11 +-
 .../lucene/search/TestLatLonPointQueries.java      |   6 +
 .../apache/lucene/geo/BaseGeoPointTestCase.java    | 415 ++++++++++-----------
 .../java/org/apache/lucene/geo/GeoTestUtil.java    |   2 +-
 19 files changed, 376 insertions(+), 522 deletions(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 85e4176..cfa788a 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -13,6 +13,8 @@ New Features
 ---------------------
 (No changes)
 
+* LUCENE-9552: New LatLonPoint query that accepts an array of LatLonGeometries. (Ignacio Vera)  
+
 Improvements
 ---------------------
 
diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java
deleted file mode 100644
index 5ea137b..0000000
--- a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * 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.document;
-
-import java.io.IOException;
-
-import org.apache.lucene.geo.GeoEncodingUtils;
-import org.apache.lucene.geo.GeoUtils;
-import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.SortedNumericDocValues;
-import org.apache.lucene.search.ConstantScoreScorer;
-import org.apache.lucene.search.ConstantScoreWeight;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.QueryVisitor;
-import org.apache.lucene.search.ScoreMode;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.TwoPhaseIterator;
-import org.apache.lucene.search.Weight;
-
-/** Distance query for {@link LatLonDocValuesField}. */
-final class LatLonDocValuesDistanceQuery extends Query {
-
-  private final String field;
-  private final double latitude, longitude;
-  private final double radiusMeters;
-
-  LatLonDocValuesDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
-    if (Double.isFinite(radiusMeters) == false || radiusMeters < 0) {
-      throw new IllegalArgumentException("radiusMeters: '" + radiusMeters + "' is invalid");
-    }
-    GeoUtils.checkLatitude(latitude);
-    GeoUtils.checkLongitude(longitude);
-    if (field == null) {
-      throw new IllegalArgumentException("field must not be null");
-    }
-    this.field = field;
-    this.latitude = latitude;
-    this.longitude = longitude;
-    this.radiusMeters = radiusMeters;
-  }
-
-  @Override
-  public void visit(QueryVisitor visitor) {
-    if (visitor.acceptField(field)) {
-      visitor.visitLeaf(this);
-    }
-  }
-
-  @Override
-  public String toString(String field) {
-    StringBuilder sb = new StringBuilder();
-    if (!this.field.equals(field)) {
-      sb.append(this.field);
-      sb.append(':');
-    }
-    sb.append(latitude);
-    sb.append(",");
-    sb.append(longitude);
-    sb.append(" +/- ");
-    sb.append(radiusMeters);
-    sb.append(" meters");
-    return sb.toString();
-  }
-
-  @Override
-  public boolean equals(Object obj) {
-    if (sameClassAs(obj) == false) {
-      return false;
-    }
-    LatLonDocValuesDistanceQuery other = (LatLonDocValuesDistanceQuery) obj;
-    return field.equals(other.field) &&
-        Double.doubleToLongBits(latitude) == Double.doubleToLongBits(other.latitude) &&
-        Double.doubleToLongBits(longitude) == Double.doubleToLongBits(other.longitude) &&
-        Double.doubleToLongBits(radiusMeters) == Double.doubleToLongBits(other.radiusMeters);
-  }
-
-  @Override
-  public int hashCode() {
-    int h = classHash();
-    h = 31 * h + field.hashCode();
-    h = 31 * h + Double.hashCode(latitude);
-    h = 31 * h + Double.hashCode(longitude);
-    h = 31 * h + Double.hashCode(radiusMeters);
-    return h;
-  }
-
-  @Override
-  public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
-    return new ConstantScoreWeight(this, boost) {
-
-      private final GeoEncodingUtils.DistancePredicate distancePredicate = GeoEncodingUtils.createDistancePredicate(latitude, longitude, radiusMeters);
-
-      @Override
-      public Scorer scorer(LeafReaderContext context) throws IOException {
-        final SortedNumericDocValues values = context.reader().getSortedNumericDocValues(field);
-        if (values == null) {
-          return null;
-        }
-
-        final TwoPhaseIterator iterator = new TwoPhaseIterator(values) {
-
-          @Override
-          public boolean matches() throws IOException {
-            for (int i = 0, count = values.docValueCount(); i < count; ++i) {
-              final long value = values.nextValue();
-              final int lat = (int) (value >>> 32);
-              final int lon = (int) (value & 0xFFFFFFFF);
-              if (distancePredicate.test(lat, lon)) {
-                return true;
-              }
-            }
-            return false;
-          }
-
-          @Override
-          public float matchCost() {
-            return 100f; // TODO: what should it be?
-          }
-
-        };
-        return new ConstantScoreScorer(this, boost, scoreMode, iterator);
-      }
-
-      @Override
-      public boolean isCacheable(LeafReaderContext ctx) {
-        return DocValues.isCacheable(ctx, field);
-      }
-
-    };
-  }
-
-}
diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesField.java b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesField.java
index 07f1bb8..e263f78 100644
--- a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesField.java
+++ b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesField.java
@@ -21,7 +21,10 @@ import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
 
+import org.apache.lucene.geo.Circle;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Polygon;
+import org.apache.lucene.geo.Rectangle;
 import org.apache.lucene.index.DocValuesType;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.search.FieldDoc;
@@ -177,7 +180,8 @@ public class LatLonDocValuesField extends Field {
    * @throws IllegalArgumentException if {@code field} is null, location has invalid coordinates, or radius is invalid.
    */
   public static Query newSlowDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
-    return new LatLonDocValuesDistanceQuery(field, latitude, longitude, radiusMeters);
+    Circle circle = new Circle(latitude, longitude, radiusMeters);
+    return newSlowGeometryQuery(field, circle);
   }
 
   /**
@@ -192,6 +196,26 @@ public class LatLonDocValuesField extends Field {
    * @throws IllegalArgumentException if {@code field} is null or polygons is empty or contain a null polygon.
    */
   public static Query newSlowPolygonQuery(String field, Polygon... polygons) {
-    return new LatLonDocValuesPointInPolygonQuery(field, polygons);
+    return newSlowGeometryQuery(field, polygons);
+  }
+
+  /**
+   * Create a query for matching points within the supplied geometries. Line geometries are not supported.
+   * This query is usually slow as it does not use an index structure and needs
+   * to verify documents one-by-one in order to know whether they match. It is
+   * best used wrapped in an {@link IndexOrDocValuesQuery} alongside a
+   * {@link LatLonPoint#newGeometryQuery(String, LatLonGeometry...)}.
+   * @param field field name. must not be null.
+   * @param latLonGeometries array of LatLonGeometries. must not be null or empty.
+   * @return query matching points within the given polygons.
+   * @throws IllegalArgumentException if {@code field} is null, {@code latLonGeometries} is null, empty or contain a null or line geometry.
+   */
+  public static Query newSlowGeometryQuery(String field, LatLonGeometry... latLonGeometries) {
+    if (latLonGeometries.length == 1 && latLonGeometries[0] instanceof Rectangle) {
+      LatLonGeometry geometry = latLonGeometries[0];
+      Rectangle rect = (Rectangle) geometry;
+      return newSlowBoxQuery(field, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
+    }
+    return new LatLonDocValuesPointInGeometryQuery(field, latLonGeometries);
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesPointInPolygonQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesPointInGeometryQuery.java
similarity index 72%
rename from lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesPointInPolygonQuery.java
rename to lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesPointInGeometryQuery.java
index abf1c88..3d6f25b 100644
--- a/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesPointInPolygonQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/LatLonDocValuesPointInGeometryQuery.java
@@ -17,13 +17,10 @@
 
 package org.apache.lucene.document;
 
-import java.io.IOException;
-import java.util.Arrays;
-
 import org.apache.lucene.geo.Component2D;
 import org.apache.lucene.geo.GeoEncodingUtils;
 import org.apache.lucene.geo.LatLonGeometry;
-import org.apache.lucene.geo.Polygon;
+import org.apache.lucene.geo.Line;
 import org.apache.lucene.index.DocValues;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.SortedNumericDocValues;
@@ -37,30 +34,36 @@ import org.apache.lucene.search.Scorer;
 import org.apache.lucene.search.TwoPhaseIterator;
 import org.apache.lucene.search.Weight;
 
-/** Polygon query for {@link LatLonDocValuesField}. */
-public class LatLonDocValuesPointInPolygonQuery extends Query {
+import java.io.IOException;
+import java.util.Arrays;
+
+/** Geometry query for {@link LatLonDocValuesField}. */
+public class LatLonDocValuesPointInGeometryQuery extends Query {
 
   private final String field;
-  private final Polygon[] polygons;
+  private final LatLonGeometry[] geometries;
 
 
-  LatLonDocValuesPointInPolygonQuery(String field, Polygon... polygons) {
+  LatLonDocValuesPointInGeometryQuery(String field, LatLonGeometry... geometries) {
     if (field == null) {
       throw new IllegalArgumentException("field must not be null");
     }
-    if (polygons == null) {
-      throw new IllegalArgumentException("polygons must not be null");
+    if (geometries == null) {
+      throw new IllegalArgumentException("geometries must not be null");
     }
-    if (polygons.length == 0) {
-      throw new IllegalArgumentException("polygons must not be empty");
+    if (geometries.length == 0) {
+      throw new IllegalArgumentException("geometries must not be empty");
     }
-    for (int i = 0; i < polygons.length; i++) {
-      if (polygons[i] == null) {
-        throw new IllegalArgumentException("polygon[" + i + "] must not be null");
+    for (int i = 0; i < geometries.length; i++) {
+      if (geometries[i] == null) {
+        throw new IllegalArgumentException("geometries[" + i + "] must not be null");
+      }
+      if (geometries[i] instanceof Line) {
+        throw new IllegalArgumentException("LatLonDocValuesPointInGeometryQuery does not support queries with line geometries");
       }
     }
     this.field = field;
-    this.polygons = polygons;
+    this.geometries = geometries;
   }
 
   @Override
@@ -70,7 +73,7 @@ public class LatLonDocValuesPointInPolygonQuery extends Query {
       sb.append(this.field);
       sb.append(':');
     }
-    sb.append("polygons(").append(Arrays.toString(polygons));
+    sb.append("geometries(").append(Arrays.toString(geometries));
     return sb.append(")").toString();
   }
 
@@ -79,16 +82,16 @@ public class LatLonDocValuesPointInPolygonQuery extends Query {
     if (sameClassAs(obj) == false) {
       return false;
     }
-    LatLonDocValuesPointInPolygonQuery other = (LatLonDocValuesPointInPolygonQuery) obj;
+    LatLonDocValuesPointInGeometryQuery other = (LatLonDocValuesPointInGeometryQuery) obj;
     return field.equals(other.field) &&
-           Arrays.equals(polygons, other.polygons);
+           Arrays.equals(geometries, other.geometries);
   }
 
   @Override
   public int hashCode() {
     int h = classHash();
     h = 31 * h + field.hashCode();
-    h = 31 * h + Arrays.hashCode(polygons);
+    h = 31 * h + Arrays.hashCode(geometries);
     return h;
   }
 
@@ -104,8 +107,8 @@ public class LatLonDocValuesPointInPolygonQuery extends Query {
 
     return new ConstantScoreWeight(this, boost) {
 
-      final Component2D tree = LatLonGeometry.create(polygons);
-      final GeoEncodingUtils.PolygonPredicate polygonPredicate = GeoEncodingUtils.createComponentPredicate(tree);
+      final Component2D tree = LatLonGeometry.create(geometries);
+      final GeoEncodingUtils.Component2DPredicate component2DPredicate = GeoEncodingUtils.createComponentPredicate(tree);
 
       @Override
       public Scorer scorer(LeafReaderContext context) throws IOException {
@@ -122,7 +125,7 @@ public class LatLonDocValuesPointInPolygonQuery extends Query {
               final long value = values.nextValue();
               final int lat = (int) (value >>> 32);
               final int lon = (int) (value & 0xFFFFFFFF);
-              if (polygonPredicate.test(lat, lon)) {
+              if (component2DPredicate.test(lat, lon)) {
                 return true;
               }
             }
diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonPoint.java b/lucene/core/src/java/org/apache/lucene/document/LatLonPoint.java
index 22f20c9..7dfb5a0 100644
--- a/lucene/core/src/java/org/apache/lucene/document/LatLonPoint.java
+++ b/lucene/core/src/java/org/apache/lucene/document/LatLonPoint.java
@@ -16,7 +16,10 @@
  */
 package org.apache.lucene.document;
 
+import org.apache.lucene.geo.Circle;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Polygon;
+import org.apache.lucene.geo.Rectangle;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.PointValues;
 import org.apache.lucene.search.BooleanClause;
@@ -49,6 +52,7 @@ import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitudeCeil;
  *   <li>{@link #newBoxQuery newBoxQuery()} for matching points within a bounding box.
  *   <li>{@link #newDistanceQuery newDistanceQuery()} for matching points within a specified distance.
  *   <li>{@link #newPolygonQuery newPolygonQuery()} for matching points within an arbitrary polygon.
+ *   <li>{@link #newGeometryQuery newGeometryQuery()} for matching points within an arbitrary geometry collection.
  * </ul>
  * <p>
  * If you also need per-document operations such as sort by distance, add a separate {@link LatLonDocValuesField} instance.
@@ -251,7 +255,29 @@ public class LatLonPoint extends Field {
    * @see Polygon
    */
   public static Query newPolygonQuery(String field, Polygon... polygons) {
-    return new LatLonPointInPolygonQuery(field, polygons);
+    return newGeometryQuery(field, polygons);
+  }
+
+  /**
+   * Create a query for matching one or more geometries. Line geometries are not supported.
+   * @param field field name. must not be null.
+   * @param latLonGeometries array of LatLonGeometries. must not be null or empty.
+   * @return query matching points within at least one geometry.
+   * @throws IllegalArgumentException  if {@code field} is null, {@code latLonGeometries} is null, empty or contain a null or line geometry.
+   * @see LatLonGeometry
+   */
+  public static Query newGeometryQuery(String field, LatLonGeometry... latLonGeometries) {
+    if (latLonGeometries.length == 1) {
+      if (latLonGeometries[0] instanceof Rectangle) {
+        final Rectangle rect = (Rectangle) latLonGeometries[0];
+        return newBoxQuery(field, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
+      }
+      if (latLonGeometries[0] instanceof Circle) {
+        final Circle circle = (Circle) latLonGeometries[0];
+        return newDistanceQuery(field, circle.getLat(), circle.getLon(), circle.getRadius());
+      }
+    }
+    return new LatLonPointInGeometryQuery(field, latLonGeometries);
   }
 
   /**
diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonPointInGeometryQuery.java
similarity index 80%
rename from lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java
rename to lucene/core/src/java/org/apache/lucene/document/LatLonPointInGeometryQuery.java
index b823ca6..53d2db5 100644
--- a/lucene/core/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/LatLonPointInGeometryQuery.java
@@ -16,13 +16,10 @@
  */
 package org.apache.lucene.document;
 
-import java.io.IOException;
-import java.util.Arrays;
-
 import org.apache.lucene.geo.Component2D;
 import org.apache.lucene.geo.GeoEncodingUtils;
 import org.apache.lucene.geo.LatLonGeometry;
-import org.apache.lucene.geo.Polygon;
+import org.apache.lucene.geo.Line;
 import org.apache.lucene.index.FieldInfo;
 import org.apache.lucene.index.LeafReader;
 import org.apache.lucene.index.LeafReaderContext;
@@ -43,39 +40,44 @@ import org.apache.lucene.util.DocIdSetBuilder;
 import org.apache.lucene.util.FutureArrays;
 import org.apache.lucene.util.NumericUtils;
 
+import java.io.IOException;
+import java.util.Arrays;
+
 import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
 import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
 
-/** Finds all previously indexed points that fall within the specified polygons.
+/** Finds all previously indexed points that fall within the specified geometries.
  *
- *  <p>The field must be indexed with using {@link org.apache.lucene.document.LatLonPoint} added per document.
+ *  <p>The field must be indexed with using {@link LatLonPoint} added per document.
  *
  *  @lucene.experimental */
 
-final class LatLonPointInPolygonQuery extends Query {
+final class LatLonPointInGeometryQuery extends Query {
   final String field;
-  final Polygon[] polygons;
+  final LatLonGeometry[] geometries;
 
-  LatLonPointInPolygonQuery(String field, Polygon[] polygons) {
+  LatLonPointInGeometryQuery(String field, LatLonGeometry[] geometries) {
     if (field == null) {
       throw new IllegalArgumentException("field must not be null");
     }
-    if (polygons == null) {
-      throw new IllegalArgumentException("polygons must not be null");
+    if (geometries == null) {
+      throw new IllegalArgumentException("geometries must not be null");
     }
-    if (polygons.length == 0) {
-      throw new IllegalArgumentException("polygons must not be empty");
+    if (geometries.length == 0) {
+      throw new IllegalArgumentException("geometries must not be empty");
     }
-    for (int i = 0; i < polygons.length; i++) {
-      if (polygons[i] == null) {
-        throw new IllegalArgumentException("polygon[" + i + "] must not be null");
+    for (int i = 0; i < geometries.length; i++) {
+      if (geometries[i] == null) {
+        throw new IllegalArgumentException("geometries[" + i + "] must not be null");
+      }
+      if (geometries[i] instanceof Line) {
+        throw new IllegalArgumentException("LatLonPointInGeometryQuery does not support queries with line geometries");
       }
     }
     this.field = field;
-    this.polygons = polygons.clone();
-    // TODO: we could also compute the maximal inner bounding box, to make relations faster to compute?
+    this.geometries = geometries.clone();
   }
 
   @Override
@@ -85,7 +87,7 @@ final class LatLonPointInPolygonQuery extends Query {
     }
   }
 
-  private IntersectVisitor getIntersectVisitor(DocIdSetBuilder result, Component2D tree, GeoEncodingUtils.PolygonPredicate polygonPredicate,
+  private IntersectVisitor getIntersectVisitor(DocIdSetBuilder result, Component2D tree, GeoEncodingUtils.Component2DPredicate component2DPredicate,
                                                byte[] minLat, byte[] maxLat, byte[] minLon, byte[] maxLon) {
     return new IntersectVisitor() {
           DocIdSetBuilder.BulkAdder adder;
@@ -102,7 +104,7 @@ final class LatLonPointInPolygonQuery extends Query {
 
           @Override
           public void visit(int docID, byte[] packedValue) {
-            if (polygonPredicate.test(NumericUtils.sortableBytesToInt(packedValue, 0),
+            if (component2DPredicate.test(NumericUtils.sortableBytesToInt(packedValue, 0),
                 NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES))) {
               visit(docID);
             }
@@ -110,7 +112,7 @@ final class LatLonPointInPolygonQuery extends Query {
 
           @Override
           public void visit(DocIdSetIterator iterator, byte[] packedValue) throws IOException {
-            if (polygonPredicate.test(NumericUtils.sortableBytesToInt(packedValue, 0),
+            if (component2DPredicate.test(NumericUtils.sortableBytesToInt(packedValue, 0),
                 NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES))) {
               int docID;
               while ((docID = iterator.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
@@ -142,9 +144,9 @@ final class LatLonPointInPolygonQuery extends Query {
   @Override
   public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
 
-    final Component2D tree = LatLonGeometry.create(polygons);
-    final GeoEncodingUtils.PolygonPredicate polygonPredicate = GeoEncodingUtils.createComponentPredicate(tree);
-    // bounding box over all polygons, this can speed up tree intersection/cheaply improve approximation for complex multi-polygons
+    final Component2D tree = LatLonGeometry.create(geometries);
+    final GeoEncodingUtils.Component2DPredicate component2DPredicate = GeoEncodingUtils.createComponentPredicate(tree);
+    // bounding box over all geometries, this can speed up tree intersection/cheaply improve approximation for complex multi-geometries
     final byte minLat[] = new byte[Integer.BYTES];
     final byte maxLat[] = new byte[Integer.BYTES];
     final byte minLon[] = new byte[Integer.BYTES];
@@ -176,7 +178,7 @@ final class LatLonPointInPolygonQuery extends Query {
 
           long cost = -1;
           DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
-          final IntersectVisitor visitor = getIntersectVisitor(result, tree, polygonPredicate, minLat, maxLat, minLon, maxLon);
+          final IntersectVisitor visitor = getIntersectVisitor(result, tree, component2DPredicate, minLat, maxLat, minLon, maxLon);
 
           @Override
           public Scorer get(long leadCost) throws IOException {
@@ -218,9 +220,9 @@ final class LatLonPointInPolygonQuery extends Query {
     return field;
   }
 
-  /** Returns a copy of the internal polygon array */
-  public Polygon[] getPolygons() {
-    return polygons.clone();
+  /** Returns a copy of the internal geometry array */
+  public LatLonGeometry[] getGeometries() {
+    return geometries.clone();
   }
 
   @Override
@@ -228,7 +230,7 @@ final class LatLonPointInPolygonQuery extends Query {
     final int prime = 31;
     int result = classHash();
     result = prime * result + field.hashCode();
-    result = prime * result + Arrays.hashCode(polygons);
+    result = prime * result + Arrays.hashCode(geometries);
     return result;
   }
 
@@ -238,9 +240,9 @@ final class LatLonPointInPolygonQuery extends Query {
            equalsTo(getClass().cast(other));
   }
 
-  private boolean equalsTo(LatLonPointInPolygonQuery other) {
+  private boolean equalsTo(LatLonPointInGeometryQuery other) {
     return field.equals(other.field) &&
-           Arrays.equals(polygons, other.polygons);
+           Arrays.equals(geometries, other.geometries);
   }
 
   @Override
@@ -253,7 +255,7 @@ final class LatLonPointInPolygonQuery extends Query {
       sb.append(this.field);
       sb.append(':');
     }
-    sb.append(Arrays.toString(polygons));
+    sb.append(Arrays.toString(geometries));
     return sb.toString();
   }
 }
diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
index 5a67276..d7077e6 100644
--- a/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java
@@ -177,37 +177,23 @@ final class LatLonShapeBoundingBoxQuery extends ShapeQuery {
 
     EncodedRectangle(double minLat, double maxLat, double minLon, double maxLon) {
       this.bbox = new byte[4 * BYTES];
-      int minXenc = encodeLongitudeCeil(minLon);
-      int maxXenc = encodeLongitude(maxLon);
-      int minYenc = encodeLatitudeCeil(minLat);
-      int maxYenc = encodeLatitude(maxLat);
-      if (minYenc > maxYenc) {
-        minYenc = maxYenc;
-      }
-      this.minY = minYenc;
-      this.maxY = maxYenc;
-
-      if (minLon > maxLon == true) {
-        this.crossesDateline = true;
+      if (minLon == 180.0 && minLon > maxLon) {
+        minLon = -180;
+      }
+      this.minX = encodeLongitudeCeil(minLon);
+      this.maxX = encodeLongitude(maxLon);
+      this.minY  = encodeLatitudeCeil(minLat);
+      this.maxY = encodeLatitude(maxLat);
+      this.crossesDateline = minLon > maxLon;
+      if (this.crossesDateline) {
         // crossing dateline is split into east/west boxes
         this.west = new byte[4 * 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 {
-        this.crossesDateline = false;
-        // encodeLongitudeCeil may cause minX to be > maxX iff
-        // the delta between the longitude < 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);
       }
-
     }
 
     /**
diff --git a/lucene/core/src/java/org/apache/lucene/geo/Circle.java b/lucene/core/src/java/org/apache/lucene/geo/Circle.java
index 4bb63c8..30121c2 100644
--- a/lucene/core/src/java/org/apache/lucene/geo/Circle.java
+++ b/lucene/core/src/java/org/apache/lucene/geo/Circle.java
@@ -35,9 +35,6 @@ public final class Circle extends LatLonGeometry {
   private final double lon;
   /** radius in meters */
   private final double radiusMeters;
-  /** Max radius allowed, half of the earth mean radius.*/
-  public static double MAX_RADIUS = GeoUtils.EARTH_MEAN_RADIUS_METERS / 2.0;
-
 
   /**
    * Creates a new circle from the supplied latitude/longitude center and a radius in meters..
@@ -45,11 +42,8 @@ public final class Circle extends LatLonGeometry {
   public Circle(double lat, double lon, double radiusMeters) {
     GeoUtils.checkLatitude(lat);
     GeoUtils.checkLongitude(lon);
-    if (radiusMeters <= 0) {
-       throw new IllegalArgumentException("radius must be bigger than 0, got " + radiusMeters);
-    }
-    if (radiusMeters < MAX_RADIUS == false) {
-      throw new IllegalArgumentException("radius must be lower than " + MAX_RADIUS + ", got " + radiusMeters);
+    if (Double.isFinite(radiusMeters) == false || radiusMeters < 0) {
+      throw new IllegalArgumentException("radiusMeters: '" + radiusMeters + "' is invalid");
     }
     this.lat = lat;
     this.lon = lon;
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 5c7078d..da63880 100644
--- a/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
+++ b/lucene/core/src/java/org/apache/lucene/geo/GeoEncodingUtils.java
@@ -178,16 +178,16 @@ public final class GeoEncodingUtils {
         lat, lon, distanceSortKey);
   }
 
-  /** Create a predicate that checks whether points are within a polygon.
+  /** Create a predicate that checks whether points are within a geometry.
    *  It works the same way as {@link #createDistancePredicate}.
    *  @lucene.internal */
-  public static PolygonPredicate createComponentPredicate(Component2D tree) {
+  public static Component2DPredicate createComponentPredicate(Component2D tree) {
     final Rectangle boundingBox = new Rectangle(tree.getMinY(), tree.getMaxY(), tree.getMinX(), tree.getMaxX());
     final Function<Rectangle, Relation> boxToRelation = box -> tree.relate(
         box.minLon, box.maxLon, box.minLat, box.maxLat);
     final Grid subBoxes = createSubBoxes(boundingBox, boxToRelation);
 
-    return new PolygonPredicate(
+    return new Component2DPredicate(
         subBoxes.latShift, subBoxes.lonShift,
         subBoxes.latBase, subBoxes.lonBase,
         subBoxes.maxLatDelta, subBoxes.maxLonDelta,
@@ -342,12 +342,12 @@ public final class GeoEncodingUtils {
     }
   }
 
-  /** A predicate that checks whether a given point is within a polygon. */
-  public static class PolygonPredicate extends Grid {
+  /** A predicate that checks whether a given point is within a component2D geometry. */
+  public static class Component2DPredicate extends Grid {
 
     private final Component2D tree;
 
-    private PolygonPredicate(
+    private Component2DPredicate(
         int latShift, int lonShift,
         int latBase, int lonBase,
         int maxLatDelta, int maxLonDelta,
diff --git a/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java b/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java
index b1beb5d..37b5f03 100644
--- a/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java
+++ b/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java
@@ -232,28 +232,25 @@ final class Rectangle2D implements Component2D {
 
   /** create a component2D from the provided LatLon rectangle */
   static Component2D create(Rectangle rectangle) {
+    // behavior of LatLonPoint.newBoxQuery()
+    double minLongitude = rectangle.minLon;
+    boolean crossesDateline = rectangle.minLon > rectangle.maxLon;
+    if (minLongitude == 180.0 && crossesDateline) {
+      minLongitude = -180;
+      crossesDateline = false;
+    }
     // need to quantize!
     double qMinLat = decodeLatitude(encodeLatitudeCeil(rectangle.minLat));
     double qMaxLat = decodeLatitude(encodeLatitude(rectangle.maxLat));
-    double qMinLon = decodeLongitude(encodeLongitudeCeil(rectangle.minLon));
+    double qMinLon = decodeLongitude(encodeLongitudeCeil(minLongitude));
     double qMaxLon = decodeLongitude(encodeLongitude(rectangle.maxLon));
-    if (qMinLat > qMaxLat) {
-      // encodeLatitudeCeil may cause minY to be > maxY iff
-      // the delta between the longitude < the encoding resolution
-      qMinLat = qMaxLat;
-    }
-    if (rectangle.minLon > rectangle.maxLon) {
+    if (crossesDateline) {
       // for rectangles that cross the dateline we need to create two components
       Component2D[] components = new Component2D[2];
       components[0] = new Rectangle2D(MIN_LON_INCL_QUANTIZE, qMaxLon, qMinLat, qMaxLat);
       components[1] = new Rectangle2D(qMinLon, MAX_LON_INCL_QUANTIZE, qMinLat, qMaxLat);
       return ComponentTree.create(components);
     } else {
-      // encodeLongitudeCeil may cause minX to be > maxX iff
-      // the delta between the longitude < the encoding resolution
-      if (qMinLon > qMaxLon) {
-        qMinLon = qMaxLon;
-      }
       return new Rectangle2D(qMinLon, qMaxLon, qMinLat, qMaxLat);
     }
   }
diff --git a/lucene/core/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java b/lucene/core/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
index 785faec..831c016 100644
--- a/lucene/core/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
+++ b/lucene/core/src/test/org/apache/lucene/document/BaseLatLonShapeTestCase.java
@@ -22,6 +22,7 @@ import com.carrotsearch.randomizedtesting.generators.RandomPicks;
 import org.apache.lucene.document.ShapeField.QueryRelation;
 import org.apache.lucene.geo.Component2D;
 import org.apache.lucene.geo.GeoTestUtil;
+import org.apache.lucene.geo.GeoUtils;
 import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Line;
 import org.apache.lucene.geo.Polygon;
@@ -109,7 +110,8 @@ public abstract class BaseLatLonShapeTestCase extends BaseShapeTestCase {
 
   @Override
   protected Circle nextCircle() {
-    return new Circle(nextLatitude(), nextLongitude(), random().nextDouble() * Circle.MAX_RADIUS);
+    final double radiusMeters = random().nextDouble() * GeoUtils.EARTH_MEAN_RADIUS_METERS * Math.PI / 2.0 + 1.0;
+    return new Circle(nextLatitude(), nextLongitude(), radiusMeters);
   }
 
   @Override
diff --git a/lucene/core/src/test/org/apache/lucene/document/BaseShapeTestCase.java b/lucene/core/src/test/org/apache/lucene/document/BaseShapeTestCase.java
index ada90f7..3374ea2 100644
--- a/lucene/core/src/test/org/apache/lucene/document/BaseShapeTestCase.java
+++ b/lucene/core/src/test/org/apache/lucene/document/BaseShapeTestCase.java
@@ -326,34 +326,23 @@ public abstract class BaseShapeTestCase extends LuceneTestCase {
         assertEquals(docID, docIDToID.nextDoc());
         int id = (int) docIDToID.longValue();
         boolean expected;
-        double qMinLon = ENCODER.quantizeXCeil(rectMinX(rect));
-        double qMaxLon = ENCODER.quantizeX(rectMaxX(rect));
-        double qMinLat = ENCODER.quantizeYCeil(rectMinY(rect));
-        double qMaxLat = ENCODER.quantizeY(rectMaxY(rect));
+        double minLon = rectMinX(rect);
+        double maxLon = rectMaxX(rect);
+        double minLat = rectMinY(rect);
+        double maxLat = rectMaxY(rect);
         if (liveDocs != null && liveDocs.get(docID) == false) {
           // document is deleted
           expected = false;
         } else if (shapes[id] == null) {
           expected = false;
         } else {
-          if (qMinLat > qMaxLat) {
-            qMinLat = ENCODER.quantizeY(rectMaxY(rect));
-          }
           if (queryRelation == QueryRelation.CONTAINS && rectCrossesDateline(rect)) {
-            //For contains we need to call the validator for each section. It is only expected
-            //if both sides are contained.
-            expected = VALIDATOR.setRelation(queryRelation).testBBoxQuery(qMinLat, qMaxLat, qMinLon, GeoUtils.MAX_LON_INCL, shapes[id]);
-            if (expected) {
-              expected = VALIDATOR.setRelation(queryRelation).testBBoxQuery(qMinLat, qMaxLat, GeoUtils.MIN_LON_INCL, qMaxLon, shapes[id]);
-            }
+            // For contains we need to call the validator for each section. 
+            // It is only expected if both sides are contained.
+            expected = VALIDATOR.setRelation(queryRelation).testBBoxQuery(minLat, maxLat, minLon, GeoUtils.MAX_LON_INCL, shapes[id]) &&
+                    VALIDATOR.setRelation(queryRelation).testBBoxQuery(minLat, maxLat, GeoUtils.MIN_LON_INCL, maxLon, shapes[id]);
           } else {
-            // check quantized poly against quantized query
-            if (qMinLon > qMaxLon && rectCrossesDateline(rect) == false) {
-              // if the quantization creates a false dateline crossing (because of encodeCeil):
-              // then do not use encodeCeil
-              qMinLon = ENCODER.quantizeX(rectMinX(rect));
-            }
-            expected = VALIDATOR.setRelation(queryRelation).testBBoxQuery(qMinLat, qMaxLat, qMinLon, qMaxLon, shapes[id]);
+            expected = VALIDATOR.setRelation(queryRelation).testBBoxQuery(minLat, maxLat, minLon, maxLon, shapes[id]);
           }
         }
 
@@ -373,7 +362,7 @@ public abstract class BaseShapeTestCase extends LuceneTestCase {
             b.append("  shape=" + shapes[id] + "\n");
           }
           b.append("  deleted?=" + (liveDocs != null && liveDocs.get(docID) == false));
-          b.append("  rect=Rectangle(lat=" + ENCODER.quantizeYCeil(rectMinY(rect)) + " TO " + ENCODER.quantizeY(rectMaxY(rect)) + " lon=" + qMinLon + " TO " + ENCODER.quantizeX(rectMaxX(rect)) + ")\n");
+          b.append("  rect=Rectangle(lat=" + ENCODER.quantizeYCeil(rectMinY(rect)) + " TO " + ENCODER.quantizeY(rectMaxY(rect)) + " lon=" + minLon + " TO " + ENCODER.quantizeX(rectMaxX(rect)) + ")\n");
           if (true) {
             fail("wrong hit (first of possibly more):\n\n" + b);
           } else {
diff --git a/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java b/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
index 11acf62..9ada389 100644
--- a/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
+++ b/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointShapeQueries.java
@@ -20,7 +20,9 @@ import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
 import org.apache.lucene.document.ShapeField.QueryRelation;
 import org.apache.lucene.geo.Component2D;
 import org.apache.lucene.geo.GeoTestUtil;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Line;
+import org.apache.lucene.geo.Rectangle;
 
 /** random bounding box, line, and polygon query tests for random generated {@code latitude, longitude} points */
 public class TestLatLonPointShapeQueries extends BaseLatLonShapeTestCase {
@@ -73,21 +75,8 @@ public class TestLatLonPointShapeQueries extends BaseLatLonShapeTestCase {
 
     @Override
     public boolean testBBoxQuery(double minLat, double maxLat, double minLon, double maxLon, Object shape) {
-      if (queryRelation == QueryRelation.CONTAINS) {
-        return false;
-      }
-      Point p = (Point)shape;
-      double lat = encoder.quantizeY(p.lat);
-      double lon = encoder.quantizeX(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;
-      }
-      return isDisjoint == false;
+      Component2D rectangle2D = LatLonGeometry.create(new Rectangle(minLat, maxLat, minLon, maxLon));
+      return testComponentQuery(rectangle2D, shape);
     }
 
     @Override
diff --git a/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java b/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java
index 045cc39..53b1ead 100644
--- a/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java
+++ b/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java
@@ -22,6 +22,7 @@ import org.apache.lucene.geo.Circle;
 import org.apache.lucene.geo.Component2D;
 import org.apache.lucene.geo.GeoEncodingUtils;
 import org.apache.lucene.geo.GeoTestUtil;
+import org.apache.lucene.geo.GeoUtils;
 import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Line;
 import org.apache.lucene.geo.Polygon;
@@ -433,7 +434,7 @@ public class TestLatLonShape extends LuceneTestCase {
     IndexSearcher s = newSearcher(r);
 
     // search by same point
-    Query q = LatLonShape.newBoxQuery(FIELDNAME, QueryRelation.INTERSECTS, p.lat, p.lat, p.lon, p.lon);
+    Query q = LatLonShape.newPointQuery(FIELDNAME, QueryRelation.INTERSECTS, new double[] {p.lat, p.lon});
     assertEquals(1, s.count(q));
     IOUtils.close(r, dir);
   }
@@ -777,10 +778,7 @@ public class TestLatLonShape extends LuceneTestCase {
 
     double lat = GeoTestUtil.nextLatitude();
     double lon = GeoTestUtil.nextLongitude();
-    double radiusMeters = random().nextDouble() * Circle.MAX_RADIUS;
-    while (radiusMeters == 0 || radiusMeters == Circle.MAX_RADIUS) {
-      radiusMeters = random().nextDouble() * Circle.MAX_RADIUS;
-    }
+    final double radiusMeters = random().nextDouble() * GeoUtils.EARTH_MEAN_RADIUS_METERS * Math.PI / 2.0 + 1.0;
     Circle circle = new Circle(lat, lon, radiusMeters);
     Component2D circle2D = LatLonGeometry.create(circle);
     int expected;
diff --git a/lucene/core/src/test/org/apache/lucene/geo/TestCircle.java b/lucene/core/src/test/org/apache/lucene/geo/TestCircle.java
index 8005d72..37f545b 100644
--- a/lucene/core/src/test/org/apache/lucene/geo/TestCircle.java
+++ b/lucene/core/src/test/org/apache/lucene/geo/TestCircle.java
@@ -41,15 +41,15 @@ public class TestCircle extends LuceneTestCase {
     IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
       new Circle(43.5, 45.23, -1000);
     });
-    assertTrue(expected.getMessage().contains("radius must be bigger than 0, got -1000.0"));
+    assertTrue(expected.getMessage().contains("radiusMeters: '-1000.0' is invalid"));
   }
 
-  /** radius must be lower than 3185504.3857 */
+  /** radius must cannot be infinite */
   public void testInfiniteRadius() {
     IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
       new Circle(43.5, 45.23, Double.POSITIVE_INFINITY);
     });
-    assertTrue(expected.getMessage().contains("radius must be lower than 3185504.3857, got Infinity"));
+    assertTrue(expected.getMessage().contains("radiusMeters: 'Infinity' is invalid"));
   }
 
   /** equals and hashcode */
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java
index af113e1..ebb5deb 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java
@@ -20,16 +20,12 @@ import org.apache.lucene.document.Document;
 import org.apache.lucene.document.LatLonDocValuesField;
 import org.apache.lucene.geo.BaseGeoPointTestCase;
 import org.apache.lucene.geo.GeoEncodingUtils;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Polygon;
 
 public class TestLatLonDocValuesQueries extends BaseGeoPointTestCase {
 
   @Override
-  protected boolean supportsPolygons() {
-    return true;
-  }
-
-  @Override
   protected void addPointToDoc(String field, Document doc, double lat, double lon) {
     doc.add(new LatLonDocValuesField(field, lat, lon));
   }
@@ -50,6 +46,11 @@ public class TestLatLonDocValuesQueries extends BaseGeoPointTestCase {
   }
 
   @Override
+  protected Query newGeometryQuery(String field, LatLonGeometry... geometry) {
+    return LatLonDocValuesField.newSlowGeometryQuery(field, geometry);
+  }
+
+  @Override
   protected double quantizeLat(double latRaw) {
     return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(latRaw));
   }
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
index d3fce00..265367f 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
@@ -22,6 +22,7 @@ import org.apache.lucene.document.Document;
 import org.apache.lucene.document.LatLonPoint;
 import org.apache.lucene.geo.BaseGeoPointTestCase;
 import org.apache.lucene.geo.GeoEncodingUtils;
+import org.apache.lucene.geo.LatLonGeometry;
 import org.apache.lucene.geo.Polygon;
 import org.apache.lucene.index.DirectoryReader;
 import org.apache.lucene.index.IndexReader;
@@ -52,6 +53,11 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
   }
 
   @Override
+  protected Query newGeometryQuery(String field, LatLonGeometry... geometry) {
+    return LatLonPoint.newGeometryQuery(field, geometry);
+  }
+
+  @Override
   protected double quantizeLat(double latRaw) {
     return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(latRaw));
   }
diff --git a/lucene/test-framework/src/java/org/apache/lucene/geo/BaseGeoPointTestCase.java b/lucene/test-framework/src/java/org/apache/lucene/geo/BaseGeoPointTestCase.java
index c080db1..a4b47f0 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/geo/BaseGeoPointTestCase.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/geo/BaseGeoPointTestCase.java
@@ -24,6 +24,7 @@ import java.util.BitSet;
 import java.util.HashSet;
 import java.util.Locale;
 import java.util.Set;
+import java.util.function.Consumer;
 
 import org.apache.lucene.analysis.MockAnalyzer;
 import org.apache.lucene.codecs.Codec;
@@ -96,14 +97,34 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   protected Rectangle nextBox() {
     return org.apache.lucene.geo.GeoTestUtil.nextBox();
   }
+
+  protected Circle nextCircle() {
+    return org.apache.lucene.geo.GeoTestUtil.nextCircle();
+  }
   
   protected Polygon nextPolygon() {
     return org.apache.lucene.geo.GeoTestUtil.nextPolygon();
   }
 
-  /** Whether this impl supports polygons. */
-  protected boolean supportsPolygons() {
-    return true;
+  protected LatLonGeometry[] nextGeometry() {
+    final int length = random().nextInt(4) + 1;
+    final LatLonGeometry[] geometries = new LatLonGeometry[length];
+    for (int i = 0; i < length; i++) {
+      final LatLonGeometry geometry;
+      switch (random().nextInt(3)) {
+        case 0:
+          geometry = nextBox();
+          break;
+        case 1:
+          geometry = nextCircle();
+          break;
+        default:
+          geometry = nextPolygon();
+          break;
+      }
+      geometries[i] = geometry;
+    }
+    return geometries;
   }
 
   /** Valid values that should not cause exception */
@@ -291,7 +312,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   
   /** test we can search for a polygon */
   public void testPolygonBasics() throws Exception {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     Directory dir = newDirectory();
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
 
@@ -314,7 +334,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   
   /** test we can search for a polygon with a hole (but still includes the doc) */
   public void testPolygonHole() throws Exception {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     Directory dir = newDirectory();
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
 
@@ -339,7 +358,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   
   /** test we can search for a polygon with a hole (that excludes the doc) */
   public void testPolygonHoleExcludes() throws Exception {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     Directory dir = newDirectory();
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
 
@@ -364,7 +382,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   
   /** test we can search for a multi-polygon */
   public void testMultiPolygonBasics() throws Exception {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     Directory dir = newDirectory();
     RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
 
@@ -389,7 +406,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   
   /** null field name not allowed */
   public void testPolygonNullField() {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
       newPolygonQuery(null, new Polygon(
           new double[] { 18, 18, 19, 19, 18 },
@@ -583,27 +599,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
 
       Query query = newRectQuery(FIELD_NAME, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
 
-      final FixedBitSet hits = new FixedBitSet(r.maxDoc());
-      s.search(query, new SimpleCollector() {
-
-          private int docBase;
-
-          @Override
-          public ScoreMode scoreMode() {
-            return ScoreMode.COMPLETE_NO_SCORES;
-          }
-
-          @Override
-          protected void doSetNextReader(LeafReaderContext context) throws IOException {
-            docBase = context.docBase;
-          }
-
-          @Override
-          public void collect(int doc) {
-            hits.set(docBase+doc);
-          }
-        });
-
+      final FixedBitSet hits = searchIndex(s, query, r.maxDoc());
+      
       boolean fail = false;
 
       for(int docID=0;docID<lats.length/2;docID++) {
@@ -743,6 +740,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
 
   protected abstract Query newPolygonQuery(String field, Polygon... polygon);
 
+  protected abstract Query newGeometryQuery(String field, LatLonGeometry... geometry);
+
   static final boolean rectContainsPoint(Rectangle rect, double pointLat, double pointLon) {
     assert Double.isNaN(pointLat) == false;
     
@@ -773,9 +772,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     }
     verifyRandomRectangles(lats, lons);
     verifyRandomDistances(lats, lons);
-    if (supportsPolygons()) {
-      verifyRandomPolygons(lats, lons);
-    }
+    verifyRandomPolygons(lats, lons);
+    verifyRandomGeometries(lats, lons);
   }
 
   protected void verifyRandomRectangles(double[] lats, double[] lons) throws Exception {
@@ -797,27 +795,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     Set<Integer> deleted = new HashSet<>();
     // RandomIndexWriter is too slow here:
     IndexWriter w = new IndexWriter(dir, iwc);
-    for(int id=0;id<lats.length;id++) {
-      Document doc = new Document();
-      doc.add(newStringField("id", ""+id, Field.Store.NO));
-      doc.add(new NumericDocValuesField("id", id));
-      if (Double.isNaN(lats[id]) == false) {
-        addPointToDoc(FIELD_NAME, doc, lats[id], lons[id]);
-      }
-      w.addDocument(doc);
-      if (id > 0 && random().nextInt(100) == 42) {
-        int idToDelete = random().nextInt(id);
-        w.deleteDocuments(new Term("id", ""+idToDelete));
-        deleted.add(idToDelete);
-        if (VERBOSE) {
-          System.out.println("  delete id=" + idToDelete);
-        }
-      }
-    }
-
-    if (random().nextBoolean()) {
-      w.forceMerge(1);
-    }
+    indexPoints(lats, lons, deleted, w);
+    
     final IndexReader r = DirectoryReader.open(w);
     w.close();
 
@@ -842,26 +821,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
         System.out.println("  query=" + query);
       }
 
-      final FixedBitSet hits = new FixedBitSet(maxDoc);
-      s.search(query, new SimpleCollector() {
-
-          private int docBase;
-
-          @Override
-          public ScoreMode scoreMode() {
-            return ScoreMode.COMPLETE_NO_SCORES;
-          }
-
-          @Override
-          protected void doSetNextReader(LeafReaderContext context) throws IOException {
-            docBase = context.docBase;
-          }
-
-          @Override
-          public void collect(int doc) {
-            hits.set(docBase+doc);
-          }
-        });
+      final FixedBitSet hits = searchIndex(s, query, maxDoc);
 
       boolean fail = false;
       NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
@@ -879,24 +839,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
         }
 
         if (hits.get(docID) != expected) {
-          StringBuilder b = new StringBuilder();
-          b.append("docID=(").append(docID).append(")\n");
-
-          if (expected) {
-            b.append("FAIL: id=").append(id).append(" should match but did not\n");
-          } else {
-            b.append("FAIL: id=").append(id).append(" should not match but did\n");
-          }
-          b.append("  box=").append(rect).append("\n");
-          b.append("  query=").append(query).append(" docID=").append(docID).append("\n");
-          b.append("  lat=").append(lats[id]).append(" lon=").append(lons[id]).append("\n");
-          b.append("  deleted?=").append(liveDocs != null && liveDocs.get(docID) == false);
-          if (true) {
-            fail("wrong hit (first of possibly more):\n\n" + b);
-          } else {
-            System.out.println(b.toString());
-            fail = true;
-          }
+          buildError(docID, expected, id, lats, lons, query, liveDocs, (b) ->  b.append("  rect=").append(rect));
+          fail = true;
         }
       }
       if (fail) {
@@ -926,27 +870,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     Set<Integer> deleted = new HashSet<>();
     // RandomIndexWriter is too slow here:
     IndexWriter w = new IndexWriter(dir, iwc);
-    for(int id=0;id<lats.length;id++) {
-      Document doc = new Document();
-      doc.add(newStringField("id", ""+id, Field.Store.NO));
-      doc.add(new NumericDocValuesField("id", id));
-      if (Double.isNaN(lats[id]) == false) {
-        addPointToDoc(FIELD_NAME, doc, lats[id], lons[id]);
-      }
-      w.addDocument(doc);
-      if (id > 0 && random().nextInt(100) == 42) {
-        int idToDelete = random().nextInt(id);
-        w.deleteDocuments(new Term("id", ""+idToDelete));
-        deleted.add(idToDelete);
-        if (VERBOSE) {
-          System.out.println("  delete id=" + idToDelete);
-        }
-      }
-    }
-
-    if (random().nextBoolean()) {
-      w.forceMerge(1);
-    }
+    indexPoints(lats, lons, deleted, w);
+    
     final IndexReader r = DirectoryReader.open(w);
     w.close();
 
@@ -981,27 +906,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
         System.out.println("  query=" + query);
       }
 
-      final FixedBitSet hits = new FixedBitSet(maxDoc);
-      s.search(query, new SimpleCollector() {
-
-          private int docBase;
-
-          @Override
-          public ScoreMode scoreMode() {
-            return ScoreMode.COMPLETE_NO_SCORES;
-          }
-
-          @Override
-          protected void doSetNextReader(LeafReaderContext context) throws IOException {
-            docBase = context.docBase;
-          }
-
-          @Override
-          public void collect(int doc) {
-            hits.set(docBase+doc);
-          }
-        });
-
+      final FixedBitSet hits = searchIndex(s, query, maxDoc);
+      
       boolean fail = false;
       NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
       for(int docID=0;docID<maxDoc;docID++) {
@@ -1018,26 +924,14 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
         }
 
         if (hits.get(docID) != expected) {
-          StringBuilder b = new StringBuilder();
-
-          if (expected) {
-            b.append("FAIL: id=").append(id).append(" should match but did not\n");
-          } else {
-            b.append("FAIL: id=").append(id).append(" should not match but did\n");
-          }
-          b.append("  query=").append(query).append(" docID=").append(docID).append("\n");
-          b.append("  lat=").append(lats[id]).append(" lon=").append(lons[id]).append("\n");
-          b.append("  deleted?=").append(liveDocs != null && liveDocs.get(docID) == false);
-          if (Double.isNaN(lats[id]) == false) {
-            double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, lats[id], lons[id]);
-            b.append("  centerLat=").append(centerLat).append(" centerLon=").append(centerLon).append(" distanceMeters=").append(distanceMeters).append(" vs radiusMeters=").append(radiusMeters);
-          }
-          if (true) {
-            fail("wrong hit (first of possibly more):\n\n" + b);
-          } else {
-            System.out.println(b.toString());
-            fail = true;
-          }
+          Consumer<StringBuilder> explain = (b) -> {
+            if (Double.isNaN(lats[id]) == false) {
+              double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, lats[id], lons[id]);
+              b.append("  centerLat=").append(centerLat).append(" centerLon=").append(centerLon).append(" distanceMeters=").append(distanceMeters).append(" vs radiusMeters=").append(radiusMeters);
+            }
+          };
+          buildError(docID, expected, id, lats, lons, query, liveDocs, explain);
+          fail = true;
         }
       }
       if (fail) {
@@ -1067,27 +961,85 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     Set<Integer> deleted = new HashSet<>();
     // RandomIndexWriter is too slow here:
     IndexWriter w = new IndexWriter(dir, iwc);
-    for(int id=0;id<lats.length;id++) {
-      Document doc = new Document();
-      doc.add(newStringField("id", ""+id, Field.Store.NO));
-      doc.add(new NumericDocValuesField("id", id));
-      if (Double.isNaN(lats[id]) == false) {
-        addPointToDoc(FIELD_NAME, doc, lats[id], lons[id]);
+    indexPoints(lats, lons, deleted, w);
+    
+    final IndexReader r = DirectoryReader.open(w);
+    w.close();
+
+    // We can't wrap with "exotic" readers because points needs to work:
+    IndexSearcher s = newSearcher(r);
+
+    final int iters = atLeast(75);
+
+    Bits liveDocs = MultiBits.getLiveDocs(s.getIndexReader());
+    int maxDoc = s.getIndexReader().maxDoc();
+
+    for (int iter=0;iter<iters;iter++) {
+
+      if (VERBOSE) {
+        System.out.println("\nTEST: iter=" + iter + " s=" + s);
       }
-      w.addDocument(doc);
-      if (id > 0 && random().nextInt(100) == 42) {
-        int idToDelete = random().nextInt(id);
-        w.deleteDocuments(new Term("id", ""+idToDelete));
-        deleted.add(idToDelete);
-        if (VERBOSE) {
-          System.out.println("  delete id=" + idToDelete);
+
+      // Polygon
+      Polygon polygon = nextPolygon();
+      Query query = newPolygonQuery(FIELD_NAME, polygon);
+
+      if (VERBOSE) {
+        System.out.println("  query=" + query);
+      }
+
+      final FixedBitSet hits = searchIndex(s, query, maxDoc);
+
+      boolean fail = false;
+      NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
+      for(int docID=0;docID<maxDoc;docID++) {
+        assertEquals(docID, docIDToID.nextDoc());
+        int id = (int) docIDToID.longValue();
+        boolean expected;
+        if (liveDocs != null && liveDocs.get(docID) == false) {
+          // document is deleted
+          expected = false;
+        } else if (Double.isNaN(lats[id])) {
+          expected = false;
+        } else {
+          expected = GeoTestUtil.containsSlowly(polygon, lats[id], lons[id]);
         }
+
+        if (hits.get(docID) != expected) {
+          buildError(docID, expected, id, lats, lons, query, liveDocs, (b) ->  b.append("  polygon=").append(polygon));
+          fail = true;
+        }
+      }
+      if (fail) {
+        fail("some hits were wrong");
       }
     }
 
-    if (random().nextBoolean()) {
-      w.forceMerge(1);
+    IOUtils.close(r, dir);
+  }
+  
+  protected void verifyRandomGeometries(double[] lats, double[] lons) throws Exception {
+    IndexWriterConfig iwc = newIndexWriterConfig();
+    // Else seeds may not reproduce:
+    iwc.setMergeScheduler(new SerialMergeScheduler());
+    // Else we can get O(N^2) merging:
+    int mbd = iwc.getMaxBufferedDocs();
+    if (mbd != -1 && mbd < lats.length/100) {
+      iwc.setMaxBufferedDocs(lats.length/100);
+    }
+    Directory dir;
+    if (lats.length > 100000) {
+      dir = newFSDirectory(createTempDir(getClass().getSimpleName()));
+    } else {
+      dir = newDirectory();
     }
+
+    Set<Integer> deleted = new HashSet<>();
+    
+    // RandomIndexWriter is too slow here:
+    IndexWriter w = new IndexWriter(dir, iwc);
+    indexPoints(lats, lons, deleted, w);
+    
     final IndexReader r = DirectoryReader.open(w);
     w.close();
 
@@ -1106,33 +1058,16 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
       }
 
       // Polygon
-      Polygon polygon = nextPolygon();
-      Query query = newPolygonQuery(FIELD_NAME, polygon);
+      LatLonGeometry[] geometries = nextGeometry();
+      Query query = newGeometryQuery(FIELD_NAME, geometries);
 
       if (VERBOSE) {
         System.out.println("  query=" + query);
       }
 
-      final FixedBitSet hits = new FixedBitSet(maxDoc);
-      s.search(query, new SimpleCollector() {
+      final FixedBitSet hits = searchIndex(s, query, maxDoc);
 
-          private int docBase;
-
-          @Override
-          public ScoreMode scoreMode() {
-            return ScoreMode.COMPLETE_NO_SCORES;
-          }
-
-          @Override
-          protected void doSetNextReader(LeafReaderContext context) throws IOException {
-            docBase = context.docBase;
-          }
-
-          @Override
-          public void collect(int doc) {
-            hits.set(docBase+doc);
-          }
-        });
+      Component2D component2D = LatLonGeometry.create(geometries);
 
       boolean fail = false;
       NumericDocValues docIDToID = MultiDocValues.getNumericValues(r, "id");
@@ -1146,27 +1081,12 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
         } else if (Double.isNaN(lats[id])) {
           expected = false;
         } else {
-          expected = GeoTestUtil.containsSlowly(polygon, lats[id], lons[id]);
+          expected = component2D.contains(quantizeLon(lons[id]), quantizeLat(lats[id]));
         }
 
         if (hits.get(docID) != expected) {
-          StringBuilder b = new StringBuilder();
-
-          if (expected) {
-            b.append("FAIL: id=").append(id).append(" should match but did not\n");
-          } else {
-            b.append("FAIL: id=").append(id).append(" should not match but did\n");
-          }
-          b.append("  query=").append(query).append(" docID=").append(docID).append("\n");
-          b.append("  lat=").append(lats[id]).append(" lon=").append(lons[id]).append("\n");
-          b.append("  deleted?=").append(liveDocs != null && liveDocs.get(docID) == false);
-          b.append("  polygon=").append(polygon);
-          if (true) {
-            fail("wrong hit (first of possibly more):\n\n" + b);
-          } else {
-            System.out.println(b.toString());
-            fail = true;
-          }
+          buildError(docID, expected, id, lats, lons, query, liveDocs, (b) ->  b.append("  geometry=").append(Arrays.toString(geometries)));
+          fail = true;
         }
       }
       if (fail) {
@@ -1177,6 +1097,73 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     IOUtils.close(r, dir);
   }
 
+  private void indexPoints(double[] lats, double[] lons, Set<Integer> deleted, IndexWriter w) throws IOException {
+    for(int id=0;id<lats.length;id++) {
+      Document doc = new Document();
+      doc.add(newStringField("id", ""+id, Field.Store.NO));
+      doc.add(new NumericDocValuesField("id", id));
+      if (Double.isNaN(lats[id]) == false) {
+        addPointToDoc(FIELD_NAME, doc, lats[id], lons[id]);
+      }
+      w.addDocument(doc);
+      if (id > 0 && random().nextInt(100) == 42) {
+        int idToDelete = random().nextInt(id);
+        w.deleteDocuments(new Term("id", ""+idToDelete));
+        deleted.add(idToDelete);
+        if (VERBOSE) {
+          System.out.println("  delete id=" + idToDelete);
+        }
+      }
+    }
+
+    if (random().nextBoolean()) {
+      w.forceMerge(1);
+    }
+  }
+
+  private FixedBitSet searchIndex(IndexSearcher s, Query query, int maxDoc) throws IOException {
+    final FixedBitSet hits = new FixedBitSet(maxDoc);
+    s.search(query, new SimpleCollector() {
+
+      private int docBase;
+
+      @Override
+      public ScoreMode scoreMode() {
+        return ScoreMode.COMPLETE_NO_SCORES;
+      }
+
+      @Override
+      protected void doSetNextReader(LeafReaderContext context)  {
+        docBase = context.docBase;
+      }
+
+      @Override
+      public void collect(int doc) {
+        hits.set(docBase+doc);
+      }
+    });
+    return hits;
+  }
+
+  private void buildError(int docID, boolean expected, int id, double[] lats, double[] lons, Query query,
+                          Bits liveDocs, Consumer<StringBuilder> explain) {
+    StringBuilder b = new StringBuilder();
+    if (expected) {
+      b.append("FAIL: id=").append(id).append(" should match but did not\n");
+    } else {
+      b.append("FAIL: id=").append(id).append(" should not match but did\n");
+    }
+    b.append("  query=").append(query).append(" docID=").append(docID).append("\n");
+    b.append("  lat=").append(lats[id]).append(" lon=").append(lons[id]).append("\n");
+    b.append("  deleted?=").append(liveDocs != null && liveDocs.get(docID) == false);
+    explain.accept(b);
+    if (true) {
+      fail("wrong hit (first of possibly more):\n\n" + b);
+    } else {
+      System.out.println(b.toString());
+    }
+  }
+
   public void testRectBoundariesAreInclusive() throws Exception {
     Rectangle rect;
     // TODO: why this dateline leniency???
@@ -1350,7 +1337,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     dir.close();
   }
 
-  public void testEquals() throws Exception {   
+  public void testEquals() throws Exception {
     Query q1, q2;
 
     Rectangle rect = nextBox();
@@ -1383,12 +1370,10 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
     lons[3] = rect.maxLon;
     lats[4] = rect.minLat;
     lons[4] = rect.minLon;
-    if (supportsPolygons()) {
-      q1 = newPolygonQuery("field", new Polygon(lats, lons));
-      q2 = newPolygonQuery("field", new Polygon(lats, lons));
-      assertEquals(q1, q2);
-      assertFalse(q1.equals(newPolygonQuery("field2", new Polygon(lats, lons))));
-    }
+    q1 = newPolygonQuery("field", new Polygon(lats, lons));
+    q2 = newPolygonQuery("field", new Polygon(lats, lons));
+    assertEquals(q1, q2);
+    assertFalse(q1.equals(newPolygonQuery("field2", new Polygon(lats, lons))));
   }
   
   /** return topdocs over a small set of points in field "point" */
@@ -1477,7 +1462,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   }
   
   public void testSmallSetPoly() throws Exception {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     TopDocs td = searchSmallSet(newPolygonQuery("point",
         new Polygon(
         new double[]{33.073130, 32.9942669, 32.938386, 33.0374494,
@@ -1489,7 +1473,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
   }
 
   public void testSmallSetPolyWholeMap() throws Exception {
-    assumeTrue("Impl does not support polygons", supportsPolygons());
     TopDocs td = searchSmallSet(newPolygonQuery("point",
                       new Polygon(
                       new double[] {GeoUtils.MIN_LAT_INCL, GeoUtils.MAX_LAT_INCL, GeoUtils.MAX_LAT_INCL, GeoUtils.MIN_LAT_INCL, GeoUtils.MIN_LAT_INCL},
diff --git a/lucene/test-framework/src/java/org/apache/lucene/geo/GeoTestUtil.java b/lucene/test-framework/src/java/org/apache/lucene/geo/GeoTestUtil.java
index 826fd78..062fc4a 100644
--- a/lucene/test-framework/src/java/org/apache/lucene/geo/GeoTestUtil.java
+++ b/lucene/test-framework/src/java/org/apache/lucene/geo/GeoTestUtil.java
@@ -394,7 +394,7 @@ public class GeoTestUtil {
   public static Circle nextCircle() {
     double lat = nextLatitude();
     double lon = nextLongitude();
-    double radiusMeters = random().nextDouble() * Circle.MAX_RADIUS;
+    double radiusMeters = random().nextDouble() * GeoUtils.EARTH_MEAN_RADIUS_METERS * Math.PI / 2.0 + 1.0;
     return new Circle(lat, lon, radiusMeters);
   }
 


[lucene-solr] 02/02: fix changes

Posted by iv...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ivera pushed a commit to branch branch_8x
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git

commit 5d683255d825892c0828f5feb13cf7d4455ffa4e
Author: iverase <iv...@apache.org>
AuthorDate: Tue Nov 3 09:00:24 2020 +0100

    fix changes
---
 lucene/CHANGES.txt | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index cfa788a..a57c459 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -11,7 +11,6 @@ API Changes
 
 New Features
 ---------------------
-(No changes)
 
 * LUCENE-9552: New LatLonPoint query that accepts an array of LatLonGeometries. (Ignacio Vera)