mirror of https://github.com/apache/lucene.git
LUCENE-9552: Adds a LatLonPoint query that accepts an array of LatLonGeometries (#1940)
This commit is contained in:
parent
6a7131ee24
commit
8bfbed8d4c
|
@ -212,6 +212,8 @@ New Features
|
|||
* LUCENE-9572: TypeAsSynonymFilter has been enhanced support ignoring some types, and to allow
|
||||
the generated synonyms to copy some or all flags from the original token (Gus Heck).
|
||||
|
||||
* LUCENE-9552: New LatLonPoint query that accepts an array of LatLonGeometries. (Ignacio Vera)
|
||||
|
||||
Improvements
|
||||
---------------------
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
@ -42,39 +39,44 @@ import org.apache.lucene.search.Weight;
|
|||
import org.apache.lucene.util.DocIdSetBuilder;
|
||||
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
|
||||
|
@ -84,7 +86,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;
|
||||
|
@ -101,7 +103,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);
|
||||
}
|
||||
|
@ -109,7 +111,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) {
|
||||
|
@ -141,9 +143,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];
|
||||
|
@ -175,7 +177,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 {
|
||||
|
@ -217,9 +219,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
|
||||
|
@ -227,7 +229,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;
|
||||
}
|
||||
|
||||
|
@ -237,9 +239,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
|
||||
|
@ -252,7 +254,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();
|
||||
}
|
||||
}
|
|
@ -178,37 +178,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;
|
||||
if (minLon == 180.0 && minLon > maxLon) {
|
||||
minLon = -180;
|
||||
}
|
||||
this.minY = minYenc;
|
||||
this.maxY = maxYenc;
|
||||
|
||||
if (minLon > maxLon == true) {
|
||||
this.crossesDateline = true;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -20,15 +20,11 @@ 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));
|
||||
|
@ -49,6 +45,11 @@ public class TestLatLonDocValuesQueries extends BaseGeoPointTestCase {
|
|||
return LatLonDocValuesField.newSlowPolygonQuery(field, polygons);
|
||||
}
|
||||
|
||||
@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));
|
||||
|
|
|
@ -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;
|
||||
|
@ -51,6 +52,11 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
|
|||
return LatLonPoint.newPolygonQuery(field, polygons);
|
||||
}
|
||||
|
||||
@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));
|
||||
|
|
|
@ -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;
|
||||
|
@ -97,13 +98,33 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
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,26 +599,7 @@ 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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
indexPoints(lats, lons, deleted, w);
|
||||
|
||||
if (random().nextBoolean()) {
|
||||
w.forceMerge(1);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
indexPoints(lats, lons, deleted, w);
|
||||
|
||||
if (random().nextBoolean()) {
|
||||
w.forceMerge(1);
|
||||
}
|
||||
final IndexReader r = DirectoryReader.open(w);
|
||||
w.close();
|
||||
|
||||
|
@ -981,26 +906,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");
|
||||
|
@ -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,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
indexPoints(lats, lons, deleted, w);
|
||||
|
||||
if (random().nextBoolean()) {
|
||||
w.forceMerge(1);
|
||||
}
|
||||
final IndexReader r = DirectoryReader.open(w);
|
||||
w.close();
|
||||
|
||||
|
@ -1113,26 +988,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");
|
||||
|
@ -1150,23 +1006,8 @@ 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);
|
||||
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(" polygon=").append(polygon));
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
|
@ -1177,6 +1018,152 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
|
|||
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();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Polygon
|
||||
LatLonGeometry[] geometries = nextGeometry();
|
||||
Query query = newGeometryQuery(FIELD_NAME, geometries);
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println(" query=" + query);
|
||||
}
|
||||
|
||||
final FixedBitSet hits = searchIndex(s, query, maxDoc);
|
||||
|
||||
Component2D component2D = LatLonGeometry.create(geometries);
|
||||
|
||||
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 = component2D.contains(quantizeLon(lons[id]), quantizeLat(lats[id]));
|
||||
}
|
||||
|
||||
if (hits.get(docID) != expected) {
|
||||
buildError(docID, expected, id, lats, lons, query, liveDocs, (b) -> b.append(" geometry=").append(Arrays.toString(geometries)));
|
||||
fail = true;
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fail("some hits were wrong");
|
||||
}
|
||||
}
|
||||
|
||||
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???
|
||||
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue