You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2016/03/01 18:07:29 UTC
[46/50] [abbrv] lucene-solr git commit: LUCENE-7057: cleanup some
sandiness around LatLonPoint
LUCENE-7057: cleanup some sandiness around LatLonPoint
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/2264600f
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/2264600f
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/2264600f
Branch: refs/heads/jira/SOLR-445
Commit: 2264600ffe4649abb0edbe7a6882ffc82f6e918b
Parents: b6ec959
Author: Robert Muir <rm...@apache.org>
Authored: Tue Mar 1 07:04:16 2016 -0500
Committer: Robert Muir <rm...@apache.org>
Committed: Tue Mar 1 07:04:45 2016 -0500
----------------------------------------------------------------------
.../org/apache/lucene/document/LatLonPoint.java | 212 +++++++++++------
.../document/LatLonPointDistanceQuery.java | 193 ++++++++++++++++
.../document/LatLonPointInPolygonQuery.java | 226 +++++++++++++++++++
.../org/apache/lucene/document/package.html | 7 +-
.../lucene/search/PointDistanceQuery.java | 179 ---------------
.../lucene/search/PointInPolygonQuery.java | 207 -----------------
.../apache/lucene/document/TestLatLonPoint.java | 197 ++++++++--------
.../document/TestLatLonPointDistanceQuery.java | 190 ++++++++++++++++
.../lucene/search/TestLatLonPointQueries.java | 62 ++---
9 files changed, 874 insertions(+), 599 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
index 92b4a3a..b0902f5 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
@@ -19,11 +19,10 @@ package org.apache.lucene.document;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.index.FieldInfo;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.ConstantScoreQuery;
-import org.apache.lucene.search.PointDistanceQuery;
-import org.apache.lucene.search.PointInPolygonQuery;
import org.apache.lucene.search.PointRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.util.GeoUtils;
@@ -49,36 +48,45 @@ import org.apache.lucene.spatial.util.GeoUtils;
// to the field is not actually what gets indexed. Float would be 1E-5 error vs 1E-7, but it might be
// a better tradeoff? then it would be completely transparent to the user and lucene would be "lossless".
public class LatLonPoint extends Field {
+ /**
+ * Type for an indexed LatLonPoint
+ * <p>
+ * Each point stores two dimensions with 4 bytes per dimension.
+ */
public static final FieldType TYPE = new FieldType();
static {
TYPE.setDimensions(2, Integer.BYTES);
TYPE.freeze();
}
+
+ /**
+ * Change the values of this field
+ * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
+ * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
+ * @throws IllegalArgumentException if latitude or longitude are out of bounds
+ */
+ public void setLocationValue(double latitude, double longitude) {
+ byte[] bytes = new byte[8];
+ NumericUtils.intToBytes(encodeLatitude(latitude), bytes, 0);
+ NumericUtils.intToBytes(encodeLongitude(longitude), bytes, Integer.BYTES);
+ fieldsData = new BytesRef(bytes);
+ }
/**
- * Creates a new LatLonPoint with the specified lat and lon
+ * Creates a new LatLonPoint with the specified latitude and longitude
* @param name field name
- * @param lat double latitude
- * @param lon double longitude
- * @throws IllegalArgumentException if the field name is null or lat or lon are out of bounds
+ * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
+ * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
+ * @throws IllegalArgumentException if the field name is null or latitude or longitude are out of bounds
*/
- public LatLonPoint(String name, double lat, double lon) {
+ public LatLonPoint(String name, double latitude, double longitude) {
super(name, TYPE);
- if (GeoUtils.isValidLat(lat) == false) {
- throw new IllegalArgumentException("invalid lat (" + lat + "): must be -90 to 90");
- }
- if (GeoUtils.isValidLon(lon) == false) {
- throw new IllegalArgumentException("invalid lon (" + lon + "): must be -180 to 180");
- }
- byte[] bytes = new byte[8];
- NumericUtils.intToBytes(encodeLat(lat), bytes, 0);
- NumericUtils.intToBytes(encodeLon(lon), bytes, Integer.BYTES);
- fieldsData = new BytesRef(bytes);
+ setLocationValue(latitude, longitude);
}
private static final int BITS = 32;
- private static final double LON_SCALE = (0x1L<<BITS)/360.0D;
- private static final double LAT_SCALE = (0x1L<<BITS)/180.0D;
+ private static final double LONGITUDE_SCALE = (0x1L<<BITS)/360.0D;
+ private static final double LATITUDE_SCALE = (0x1L<<BITS)/180.0D;
@Override
public String toString() {
@@ -89,9 +97,9 @@ public class LatLonPoint extends Field {
result.append(':');
BytesRef bytes = (BytesRef) fieldsData;
- result.append(decodeLat(BytesRef.deepCopyOf(bytes).bytes, 0));
+ result.append(decodeLatitude(BytesRef.deepCopyOf(bytes).bytes, 0));
result.append(',');
- result.append(decodeLon(BytesRef.deepCopyOf(bytes).bytes, Integer.BYTES));
+ result.append(decodeLongitude(BytesRef.deepCopyOf(bytes).bytes, Integer.BYTES));
result.append('>');
return result.toString();
@@ -99,79 +107,125 @@ public class LatLonPoint extends Field {
// public helper methods (e.g. for queries)
- /** Quantizes double (64 bit) latitude into 32 bits */
- public static int encodeLat(double lat) {
- assert GeoUtils.isValidLat(lat): "lat=" + lat;
- long x = (long) (lat * LAT_SCALE);
- assert x < Integer.MAX_VALUE: "lat=" + lat + " mapped to Integer.MAX_VALUE + " + (x - Integer.MAX_VALUE);
- assert x > Integer.MIN_VALUE: "lat=" + lat + " mapped to Integer.MIN_VALUE";
- return (int) x;
+ /**
+ * Quantizes double (64 bit) latitude into 32 bits
+ * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
+ * @return encoded value as a 32-bit {@code int}
+ * @throws IllegalArgumentException if latitude is out of bounds
+ */
+ public static int encodeLatitude(double latitude) {
+ if (GeoUtils.isValidLat(latitude) == false) {
+ throw new IllegalArgumentException("invalid latitude: " + latitude + ", must be -90 to 90");
+ }
+ // the maximum possible value cannot be encoded without overflow
+ if (latitude == 90.0D) {
+ latitude = Math.nextDown(latitude);
+ }
+ return Math.toIntExact((long) (latitude * LATITUDE_SCALE));
}
- /** Quantizes double (64 bit) longitude into 32 bits */
- public static int encodeLon(double lon) {
- assert GeoUtils.isValidLon(lon): "lon=" + lon;
- long x = (long) (lon * LON_SCALE);
- assert x < Integer.MAX_VALUE;
- assert x > Integer.MIN_VALUE;
- return (int) x;
+ /**
+ * Quantizes double (64 bit) longitude into 32 bits
+ * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
+ * @return encoded value as a 32-bit {@code int}
+ * @throws IllegalArgumentException if longitude is out of bounds
+ */
+ public static int encodeLongitude(double longitude) {
+ if (GeoUtils.isValidLon(longitude) == false) {
+ throw new IllegalArgumentException("invalid longitude: " + longitude + ", must be -180 to 180");
+ }
+ // the maximum possible value cannot be encoded without overflow
+ if (longitude == 180.0D) {
+ longitude = Math.nextDown(longitude);
+ }
+ return Math.toIntExact((long) (longitude * LONGITUDE_SCALE));
}
- /** Turns quantized value from {@link #encodeLat} back into a double. */
- public static double decodeLat(int x) {
- return x / LAT_SCALE;
+ /**
+ * Turns quantized value from {@link #encodeLatitude} back into a double.
+ * @param encoded encoded value: 32-bit quantized value.
+ * @return decoded latitude value.
+ */
+ public static double decodeLatitude(int encoded) {
+ double result = encoded / LATITUDE_SCALE;
+ assert GeoUtils.isValidLat(result);
+ return result;
}
- /** Turns quantized value from byte array back into a double. */
- public static double decodeLat(byte[] src, int offset) {
- return decodeLat(NumericUtils.bytesToInt(src, offset));
+ /**
+ * Turns quantized value from byte array back into a double.
+ * @param src byte array containing 4 bytes to decode at {@code offset}
+ * @param offset offset into {@code src} to decode from.
+ * @return decoded latitude value.
+ */
+ public static double decodeLatitude(byte[] src, int offset) {
+ return decodeLatitude(NumericUtils.bytesToInt(src, offset));
}
- /** Turns quantized value from {@link #encodeLon} back into a double. */
- public static double decodeLon(int x) {
- return x / LON_SCALE;
+ /**
+ * Turns quantized value from {@link #encodeLongitude} back into a double.
+ * @param encoded encoded value: 32-bit quantized value.
+ * @return decoded longitude value.
+ */
+ public static double decodeLongitude(int encoded) {
+ double result = encoded / LONGITUDE_SCALE;
+ assert GeoUtils.isValidLon(result);
+ return result;
}
- /** Turns quantized value from byte array back into a double. */
- public static double decodeLon(byte[] src, int offset) {
- return decodeLon(NumericUtils.bytesToInt(src, offset));
+ /**
+ * Turns quantized value from byte array back into a double.
+ * @param src byte array containing 4 bytes to decode at {@code offset}
+ * @param offset offset into {@code src} to decode from.
+ * @return decoded longitude value.
+ */
+ public static double decodeLongitude(byte[] src, int offset) {
+ return decodeLongitude(NumericUtils.bytesToInt(src, offset));
}
/** sugar encodes a single point as a 2D byte array */
- private static byte[][] encode(double lat, double lon) {
+ private static byte[][] encode(double latitude, double longitude) {
byte[][] bytes = new byte[2][];
bytes[0] = new byte[4];
- NumericUtils.intToBytes(encodeLat(lat), bytes[0], 0);
+ NumericUtils.intToBytes(encodeLatitude(latitude), bytes[0], 0);
bytes[1] = new byte[4];
- NumericUtils.intToBytes(encodeLon(lon), bytes[1], 0);
+ NumericUtils.intToBytes(encodeLongitude(longitude), bytes[1], 0);
return bytes;
}
-
+
+ /** helper: checks a fieldinfo and throws exception if its definitely not a LatLonPoint */
+ static void checkCompatible(FieldInfo fieldInfo) {
+ if (fieldInfo.getPointDimensionCount() != TYPE.pointDimensionCount()) {
+ throw new IllegalArgumentException("field=\"" + fieldInfo.name + "\" was indexed with numDims=" + fieldInfo.getPointDimensionCount() +
+ " but this point type has numDims=" + TYPE.pointDimensionCount() +
+ ", is the field really a LatLonPoint?");
+ }
+ if (fieldInfo.getPointNumBytes() != TYPE.pointNumBytes()) {
+ throw new IllegalArgumentException("field=\"" + fieldInfo.name + "\" was indexed with bytesPerDim=" + fieldInfo.getPointNumBytes() +
+ " but this point type has bytesPerDim=" + TYPE.pointNumBytes() +
+ ", is the field really a LatLonPoint?");
+ }
+ }
+
// static methods for generating queries
/**
* Create a query for matching a bounding box.
* <p>
* The box may cross over the dateline.
+ * @param field field name. cannot be null.
+ * @param minLatitude latitude lower bound: must be within standard +/-90 coordinate bounds.
+ * @param maxLatitude latitude upper bound: must be within standard +/-90 coordinate bounds.
+ * @param minLongitude longitude lower bound: must be within standard +/-180 coordinate bounds.
+ * @param maxLongitude longitude upper bound: must be within standard +/-180 coordinate bounds.
+ * @return query matching points within this box
+ * @throws IllegalArgumentException if {@code field} is null, or the box has invalid coordinates.
*/
- public static Query newBoxQuery(String field, double minLat, double maxLat, double minLon, double maxLon) {
- if (GeoUtils.isValidLat(minLat) == false) {
- throw new IllegalArgumentException("minLat=" + minLat + " is not a valid latitude");
- }
- if (GeoUtils.isValidLat(maxLat) == false) {
- throw new IllegalArgumentException("maxLat=" + maxLat + " is not a valid latitude");
- }
- if (GeoUtils.isValidLon(minLon) == false) {
- throw new IllegalArgumentException("minLon=" + minLon + " is not a valid longitude");
- }
- if (GeoUtils.isValidLon(maxLon) == false) {
- throw new IllegalArgumentException("maxLon=" + maxLon + " is not a valid longitude");
- }
-
- byte[][] lower = encode(minLat, minLon);
- byte[][] upper = encode(maxLat, maxLon);
+ public static Query newBoxQuery(String field, double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) {
+ byte[][] lower = encode(minLatitude, minLongitude);
+ byte[][] upper = encode(maxLatitude, maxLongitude);
// Crosses date line: we just rewrite into OR of two bboxes, with longitude as an open range:
- if (maxLon < minLon) {
+ if (maxLongitude < minLongitude) {
// Disable coord here because a multi-valued doc could match both rects and get unfairly boosted:
BooleanQuery.Builder q = new BooleanQuery.Builder();
q.setDisableCoord(true);
@@ -198,9 +252,9 @@ public class LatLonPoint extends Field {
@Override
protected String toString(int dimension, byte[] value) {
if (dimension == 0) {
- return Double.toString(decodeLat(value, 0));
+ return Double.toString(decodeLatitude(value, 0));
} else if (dimension == 1) {
- return Double.toString(decodeLon(value, 0));
+ return Double.toString(decodeLongitude(value, 0));
} else {
throw new AssertionError();
}
@@ -210,17 +264,31 @@ public class LatLonPoint extends Field {
/**
* Create a query for matching points within the specified distance of the supplied location.
+ * @param field field name. cannot be null.
+ * @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
+ * @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
+ * @param radiusMeters maximum distance from the center in meters: must be non-negative and finite.
+ * @return query matching points within this distance
+ * @throws IllegalArgumentException if {@code field} is null, location has invalid coordinates, or radius is invalid.
*/
public static Query newDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
- return new PointDistanceQuery(field, latitude, longitude, radiusMeters);
+ return new LatLonPointDistanceQuery(field, latitude, longitude, radiusMeters);
}
/**
* Create a query for matching a polygon.
* <p>
* The supplied {@code polyLats}/{@code polyLons} must be clockwise or counter-clockwise.
+ * @param field field name. cannot be null.
+ * @param polyLats latitude values for points of the polygon: must be within standard +/-90 coordinate bounds.
+ * @param polyLons longitude values for points of the polygon: must be within standard +/-180 coordinate bounds.
+ * @return query matching points within this polygon
+ * @throws IllegalArgumentException if {@code field} is null, {@code polyLats} is null or has invalid coordinates,
+ * {@code polyLons} is null or has invalid coordinates, if {@code polyLats} has a different
+ * length than {@code polyLons}, if the polygon has less than 4 points, or if polygon is
+ * not closed (first and last points should be the same)
*/
public static Query newPolygonQuery(String field, double[] polyLats, double[] polyLons) {
- return new PointInPolygonQuery(field, polyLats, polyLons);
+ return new LatLonPointInPolygonQuery(field, polyLats, polyLons);
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java
new file mode 100644
index 0000000..de4805b
--- /dev/null
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java
@@ -0,0 +1,193 @@
+/*
+ * 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.index.FieldInfo;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.PointValues;
+import org.apache.lucene.index.PointValues.IntersectVisitor;
+import org.apache.lucene.index.PointValues.Relation;
+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.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.spatial.util.GeoDistanceUtils;
+import org.apache.lucene.spatial.util.GeoRect;
+import org.apache.lucene.spatial.util.GeoUtils;
+import org.apache.lucene.util.DocIdSetBuilder;
+
+/**
+ * Distance query for {@link LatLonPoint}.
+ */
+final class LatLonPointDistanceQuery extends Query {
+ final String field;
+ final double latitude;
+ final double longitude;
+ final double radiusMeters;
+
+ public LatLonPointDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
+ if (field == null) {
+ throw new IllegalArgumentException("field cannot be null");
+ }
+ if (Double.isFinite(radiusMeters) == false || radiusMeters < 0) {
+ throw new IllegalArgumentException("radiusMeters: '" + radiusMeters + "' is invalid");
+ }
+ if (GeoUtils.isValidLat(latitude) == false) {
+ throw new IllegalArgumentException("latitude: '" + latitude + "' is invalid");
+ }
+ if (GeoUtils.isValidLon(longitude) == false) {
+ throw new IllegalArgumentException("longitude: '" + longitude + "' is invalid");
+ }
+ this.field = field;
+ this.latitude = latitude;
+ this.longitude = longitude;
+ this.radiusMeters = radiusMeters;
+ }
+
+ @Override
+ public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+ GeoRect box = GeoUtils.circleToBBox(longitude, latitude, radiusMeters);
+ final GeoRect box1;
+ final GeoRect box2;
+
+ // crosses dateline: split
+ if (box.maxLon < box.minLon) {
+ box1 = new GeoRect(-180.0, box.maxLon, box.minLat, box.maxLat);
+ box2 = new GeoRect(box.minLon, 180.0, box.minLat, box.maxLat);
+ } else {
+ box1 = box;
+ box2 = null;
+ }
+
+ return new ConstantScoreWeight(this) {
+
+ @Override
+ public Scorer scorer(LeafReaderContext context) throws IOException {
+ LeafReader reader = context.reader();
+ PointValues values = reader.getPointValues();
+ if (values == null) {
+ // No docs in this segment had any points fields
+ return null;
+ }
+ FieldInfo fieldInfo = reader.getFieldInfos().fieldInfo(field);
+ if (fieldInfo == null) {
+ // No docs in this segment indexed this field at all
+ return null;
+ }
+ LatLonPoint.checkCompatible(fieldInfo);
+
+ DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc());
+ values.intersect(field,
+ new IntersectVisitor() {
+ @Override
+ public void visit(int docID) {
+ result.add(docID);
+ }
+
+ @Override
+ public void visit(int docID, byte[] packedValue) {
+ assert packedValue.length == 8;
+ double lat = LatLonPoint.decodeLatitude(packedValue, 0);
+ double lon = LatLonPoint.decodeLongitude(packedValue, Integer.BYTES);
+ if (GeoDistanceUtils.haversin(latitude, longitude, lat, lon) <= radiusMeters) {
+ visit(docID);
+ }
+ }
+
+ // algorithm: we create a bounding box (two bounding boxes if we cross the dateline).
+ // 1. check our bounding box(es) first. if the subtree is entirely outside of those, bail.
+ // 2. see if the subtree is fully contained. if the subtree is enormous along the x axis, wrapping half way around the world, etc: then this can't work, just go to step 3.
+ // 3. recurse naively.
+ @Override
+ public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
+ double latMin = LatLonPoint.decodeLatitude(minPackedValue, 0);
+ double lonMin = LatLonPoint.decodeLongitude(minPackedValue, Integer.BYTES);
+ double latMax = LatLonPoint.decodeLatitude(maxPackedValue, 0);
+ double lonMax = LatLonPoint.decodeLongitude(maxPackedValue, Integer.BYTES);
+
+ if ((latMax < box1.minLat || lonMax < box1.minLon || latMin > box1.maxLat || lonMin > box1.maxLon) &&
+ (box2 == null || latMax < box2.minLat || lonMax < box2.minLon || latMin > box2.maxLat || lonMin > box2.maxLon)) {
+ // we are fully outside of bounding box(es), don't proceed any further.
+ return Relation.CELL_OUTSIDE_QUERY;
+ } else if (lonMax - longitude < 90 && longitude - lonMin < 90 &&
+ GeoDistanceUtils.haversin(latitude, longitude, latMin, lonMin) <= radiusMeters &&
+ GeoDistanceUtils.haversin(latitude, longitude, latMin, lonMax) <= radiusMeters &&
+ GeoDistanceUtils.haversin(latitude, longitude, latMax, lonMin) <= radiusMeters &&
+ GeoDistanceUtils.haversin(latitude, longitude, latMax, lonMax) <= radiusMeters) {
+ // we are fully enclosed, collect everything within this subtree
+ return Relation.CELL_INSIDE_QUERY;
+ } else {
+ // recurse: its inside our bounding box(es), but not fully, or it wraps around.
+ return Relation.CELL_CROSSES_QUERY;
+ }
+ }
+ });
+
+ return new ConstantScoreScorer(this, score(), result.build().iterator());
+ }
+ };
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + field.hashCode();
+ long temp;
+ temp = Double.doubleToLongBits(latitude);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(longitude);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(radiusMeters);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (!super.equals(obj)) return false;
+ if (getClass() != obj.getClass()) return false;
+ LatLonPointDistanceQuery other = (LatLonPointDistanceQuery) obj;
+ if (!field.equals(other.field)) return false;
+ if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) return false;
+ if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude)) return false;
+ if (Double.doubleToLongBits(radiusMeters) != Double.doubleToLongBits(other.radiusMeters)) return false;
+ return true;
+ }
+
+ @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();
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java
new file mode 100644
index 0000000..d75e615
--- /dev/null
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointInPolygonQuery.java
@@ -0,0 +1,226 @@
+/*
+ * 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 java.util.Arrays;
+
+import org.apache.lucene.index.PointValues.IntersectVisitor;
+import org.apache.lucene.index.PointValues.Relation;
+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.Scorer;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.index.PointValues;
+import org.apache.lucene.index.FieldInfo;
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.util.DocIdSetBuilder;
+import org.apache.lucene.spatial.util.GeoRelationUtils;
+import org.apache.lucene.spatial.util.GeoUtils;
+
+/** Finds all previously indexed points that fall within the specified polygon.
+ *
+ * <p>The field must be indexed with using {@link org.apache.lucene.document.LatLonPoint} added per document.
+ *
+ * @lucene.experimental */
+
+final class LatLonPointInPolygonQuery extends Query {
+ final String field;
+ final double minLat;
+ final double maxLat;
+ final double minLon;
+ final double maxLon;
+ final double[] polyLats;
+ final double[] polyLons;
+
+ /** The lats/lons must be clockwise or counter-clockwise. */
+ public LatLonPointInPolygonQuery(String field, double[] polyLats, double[] polyLons) {
+ this.field = field;
+ if (field == null) {
+ throw new IllegalArgumentException("field cannot be null");
+ }
+ if (polyLats == null) {
+ throw new IllegalArgumentException("polyLats cannot be null");
+ }
+ if (polyLons == null) {
+ throw new IllegalArgumentException("polyLons cannot be null");
+ }
+ if (polyLats.length != polyLons.length) {
+ throw new IllegalArgumentException("polyLats and polyLons must be equal length");
+ }
+ if (polyLats.length < 4) {
+ throw new IllegalArgumentException("at least 4 polygon points required");
+ }
+ if (polyLats[0] != polyLats[polyLats.length-1]) {
+ throw new IllegalArgumentException("first and last points of the polygon must be the same (it must close itself): polyLats[0]=" + polyLats[0] + " polyLats[" + (polyLats.length-1) + "]=" + polyLats[polyLats.length-1]);
+ }
+ if (polyLons[0] != polyLons[polyLons.length-1]) {
+ throw new IllegalArgumentException("first and last points of the polygon must be the same (it must close itself): polyLons[0]=" + polyLons[0] + " polyLons[" + (polyLons.length-1) + "]=" + polyLons[polyLons.length-1]);
+ }
+
+ this.polyLats = polyLats;
+ this.polyLons = polyLons;
+
+ // TODO: we could also compute the maximal inner bounding box, to make relations faster to compute?
+
+ double minLon = Double.POSITIVE_INFINITY;
+ double minLat = Double.POSITIVE_INFINITY;
+ double maxLon = Double.NEGATIVE_INFINITY;
+ double maxLat = Double.NEGATIVE_INFINITY;
+ for(int i=0;i<polyLats.length;i++) {
+ double lat = polyLats[i];
+ if (GeoUtils.isValidLat(lat) == false) {
+ throw new IllegalArgumentException("polyLats[" + i + "]=" + lat + " is not a valid latitude");
+ }
+ minLat = Math.min(minLat, lat);
+ maxLat = Math.max(maxLat, lat);
+ double lon = polyLons[i];
+ if (GeoUtils.isValidLon(lon) == false) {
+ throw new IllegalArgumentException("polyLons[" + i + "]=" + lat + " is not a valid longitude");
+ }
+ minLon = Math.min(minLon, lon);
+ maxLon = Math.max(maxLon, lon);
+ }
+ this.minLon = minLon;
+ this.maxLon = maxLon;
+ this.minLat = minLat;
+ this.maxLat = maxLat;
+ }
+
+ @Override
+ public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+
+ // I don't use RandomAccessWeight here: it's no good to approximate with "match all docs"; this is an inverted structure and should be
+ // used in the first pass:
+
+ // TODO: except that the polygon verify is costly! The approximation should be all docs in all overlapping cells, and matches() should
+ // then check the polygon
+
+ return new ConstantScoreWeight(this) {
+
+ @Override
+ public Scorer scorer(LeafReaderContext context) throws IOException {
+ LeafReader reader = context.reader();
+ PointValues values = reader.getPointValues();
+ if (values == null) {
+ // No docs in this segment had any points fields
+ return null;
+ }
+ FieldInfo fieldInfo = reader.getFieldInfos().fieldInfo(field);
+ if (fieldInfo == null) {
+ // No docs in this segment indexed this field at all
+ return null;
+ }
+ LatLonPoint.checkCompatible(fieldInfo);
+
+ DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc());
+ values.intersect(field,
+ new IntersectVisitor() {
+ @Override
+ public void visit(int docID) {
+ result.add(docID);
+ }
+
+ @Override
+ public void visit(int docID, byte[] packedValue) {
+ assert packedValue.length == 8;
+ double lat = LatLonPoint.decodeLatitude(packedValue, 0);
+ double lon = LatLonPoint.decodeLongitude(packedValue, Integer.BYTES);
+ if (GeoRelationUtils.pointInPolygon(polyLons, polyLats, lat, lon)) {
+ result.add(docID);
+ }
+ }
+
+ @Override
+ public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
+ double cellMinLat = LatLonPoint.decodeLatitude(minPackedValue, 0);
+ double cellMinLon = LatLonPoint.decodeLongitude(minPackedValue, Integer.BYTES);
+ double cellMaxLat = LatLonPoint.decodeLatitude(maxPackedValue, 0);
+ double cellMaxLon = LatLonPoint.decodeLongitude(maxPackedValue, Integer.BYTES);
+
+ if (cellMinLat <= minLat && cellMaxLat >= maxLat && cellMinLon <= minLon && cellMaxLon >= maxLon) {
+ // Cell fully encloses the query
+ return Relation.CELL_CROSSES_QUERY;
+ } else if (GeoRelationUtils.rectWithinPolyPrecise(cellMinLon, cellMinLat, cellMaxLon, cellMaxLat,
+ polyLons, polyLats,
+ minLon, minLat, maxLon, maxLat)) {
+ return Relation.CELL_INSIDE_QUERY;
+ } else if (GeoRelationUtils.rectCrossesPolyPrecise(cellMinLon, cellMinLat, cellMaxLon, cellMaxLat,
+ polyLons, polyLats,
+ minLon, minLat, maxLon, maxLat)) {
+ return Relation.CELL_CROSSES_QUERY;
+ } else {
+ return Relation.CELL_OUTSIDE_QUERY;
+ }
+ }
+ });
+
+ return new ConstantScoreScorer(this, score(), result.build().iterator());
+ }
+ };
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ LatLonPointInPolygonQuery that = (LatLonPointInPolygonQuery) o;
+
+ if (Arrays.equals(polyLons, that.polyLons) == false) {
+ return false;
+ }
+ if (Arrays.equals(polyLats, that.polyLats) == false) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public final int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + Arrays.hashCode(polyLons);
+ result = 31 * result + Arrays.hashCode(polyLats);
+ return result;
+ }
+
+ @Override
+ public String toString(String field) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(getClass().getSimpleName());
+ sb.append(':');
+ if (this.field.equals(field) == false) {
+ sb.append(" field=");
+ sb.append(this.field);
+ sb.append(':');
+ }
+ sb.append(" Points: ");
+ for (int i=0; i<polyLons.length; ++i) {
+ sb.append("[")
+ .append(polyLons[i])
+ .append(", ")
+ .append(polyLats[i])
+ .append("] ");
+ }
+ return sb.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/java/org/apache/lucene/document/package.html
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/package.html b/lucene/sandbox/src/java/org/apache/lucene/document/package.html
index 6f26128..b6a077e 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/package.html
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/package.html
@@ -23,6 +23,11 @@
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
</head>
<body>
-This package contains a single Field implementation, GeoPointField, to index a lat/lon geospatial point.
+This package contains several point types:
+<ul>
+ <li>{@link org.apache.lucene.document.BigIntegerPoint BigIntegerPoint} for 128-bit integers</li>
+ <li>{@link org.apache.lucene.document.InetAddressPoint InetAddressPoint} for IPv4 and IPv6 network addresses</li>
+ <li>{@link org.apache.lucene.document.LatLonPoint LatLonPoint} for latitude/longitude geospatial points</li>
+</ul>
</body>
</html>
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/java/org/apache/lucene/search/PointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/PointDistanceQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/PointDistanceQuery.java
deleted file mode 100644
index 4cfeb2f..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/search/PointDistanceQuery.java
+++ /dev/null
@@ -1,179 +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.search;
-
-import java.io.IOException;
-
-import org.apache.lucene.document.LatLonPoint;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.PointValues;
-import org.apache.lucene.index.PointValues.IntersectVisitor;
-import org.apache.lucene.index.PointValues.Relation;
-import org.apache.lucene.spatial.util.GeoDistanceUtils;
-import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoUtils;
-import org.apache.lucene.util.DocIdSetBuilder;
-import org.apache.lucene.util.NumericUtils;
-
-/**
- * Distance query for {@link LatLonPoint}.
- */
-public class PointDistanceQuery extends Query {
- final String field;
- final double latitude;
- final double longitude;
- final double radiusMeters;
-
- public PointDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
- if (field == null) {
- throw new IllegalArgumentException("field cannot be null");
- }
- if (GeoUtils.isValidLat(latitude) == false) {
- throw new IllegalArgumentException("latitude: '" + latitude + "' is invalid");
- }
- if (GeoUtils.isValidLon(longitude) == false) {
- throw new IllegalArgumentException("longitude: '" + longitude + "' is invalid");
- }
- this.field = field;
- this.latitude = latitude;
- this.longitude = longitude;
- this.radiusMeters = radiusMeters;
- }
-
- @Override
- public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
- GeoRect box = GeoUtils.circleToBBox(longitude, latitude, radiusMeters);
- final GeoRect box1;
- final GeoRect box2;
-
- // crosses dateline: split
- if (box.maxLon < box.minLon) {
- box1 = new GeoRect(-180.0, box.maxLon, box.minLat, box.maxLat);
- box2 = new GeoRect(box.minLon, 180.0, box.minLat, box.maxLat);
- } else {
- box1 = box;
- box2 = null;
- }
-
- return new ConstantScoreWeight(this) {
-
- @Override
- public Scorer scorer(LeafReaderContext context) throws IOException {
- LeafReader reader = context.reader();
- PointValues values = reader.getPointValues();
- if (values == null) {
- // No docs in this segment had any points fields
- return null;
- }
-
- DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc());
- values.intersect(field,
- new IntersectVisitor() {
- @Override
- public void visit(int docID) {
- result.add(docID);
- }
-
- @Override
- public void visit(int docID, byte[] packedValue) {
- assert packedValue.length == 8;
- double lat = LatLonPoint.decodeLat(NumericUtils.bytesToInt(packedValue, 0));
- double lon = LatLonPoint.decodeLon(NumericUtils.bytesToInt(packedValue, Integer.BYTES));
- if (GeoDistanceUtils.haversin(latitude, longitude, lat, lon) <= radiusMeters) {
- visit(docID);
- }
- }
-
- // algorithm: we create a bounding box (two bounding boxes if we cross the dateline).
- // 1. check our bounding box(es) first. if the subtree is entirely outside of those, bail.
- // 2. see if the subtree is fully contained. if the subtree is enormous along the x axis, wrapping half way around the world, etc: then this can't work, just go to step 3.
- // 3. recurse naively.
- @Override
- public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
- double latMin = LatLonPoint.decodeLat(NumericUtils.bytesToInt(minPackedValue, 0));
- double lonMin = LatLonPoint.decodeLon(NumericUtils.bytesToInt(minPackedValue, Integer.BYTES));
- double latMax = LatLonPoint.decodeLat(NumericUtils.bytesToInt(maxPackedValue, 0));
- double lonMax = LatLonPoint.decodeLon(NumericUtils.bytesToInt(maxPackedValue, Integer.BYTES));
-
- if ((latMax < box1.minLat || lonMax < box1.minLon || latMin > box1.maxLat || lonMin > box1.maxLon) &&
- (box2 == null || latMax < box2.minLat || lonMax < box2.minLon || latMin > box2.maxLat || lonMin > box2.maxLon)) {
- // we are fully outside of bounding box(es), don't proceed any further.
- return Relation.CELL_OUTSIDE_QUERY;
- } else if (lonMax - longitude < 90 && longitude - lonMin < 90 &&
- GeoDistanceUtils.haversin(latitude, longitude, latMin, lonMin) <= radiusMeters &&
- GeoDistanceUtils.haversin(latitude, longitude, latMin, lonMax) <= radiusMeters &&
- GeoDistanceUtils.haversin(latitude, longitude, latMax, lonMin) <= radiusMeters &&
- GeoDistanceUtils.haversin(latitude, longitude, latMax, lonMax) <= radiusMeters) {
- // we are fully enclosed, collect everything within this subtree
- return Relation.CELL_INSIDE_QUERY;
- } else {
- // recurse: its inside our bounding box(es), but not fully, or it wraps around.
- return Relation.CELL_CROSSES_QUERY;
- }
- }
- });
-
- return new ConstantScoreScorer(this, score(), result.build().iterator());
- }
- };
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = super.hashCode();
- result = prime * result + field.hashCode();
- long temp;
- temp = Double.doubleToLongBits(latitude);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(longitude);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(radiusMeters);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) return true;
- if (!super.equals(obj)) return false;
- if (getClass() != obj.getClass()) return false;
- PointDistanceQuery other = (PointDistanceQuery) obj;
- if (!field.equals(other.field)) return false;
- if (Double.doubleToLongBits(latitude) != Double.doubleToLongBits(other.latitude)) return false;
- if (Double.doubleToLongBits(longitude) != Double.doubleToLongBits(other.longitude)) return false;
- if (Double.doubleToLongBits(radiusMeters) != Double.doubleToLongBits(other.radiusMeters)) return false;
- return true;
- }
-
- @Override
- public String toString(String field) {
- StringBuilder sb = new StringBuilder();
- if (!this.field.equals(field)) {
- sb.append(field);
- sb.append(':');
- }
- sb.append(latitude);
- sb.append(",");
- sb.append(longitude);
- sb.append(" +/- ");
- sb.append(radiusMeters);
- sb.append(" meters");
- return sb.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/java/org/apache/lucene/search/PointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/search/PointInPolygonQuery.java b/lucene/sandbox/src/java/org/apache/lucene/search/PointInPolygonQuery.java
deleted file mode 100644
index 6ae7c03..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/search/PointInPolygonQuery.java
+++ /dev/null
@@ -1,207 +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.search;
-
-import java.io.IOException;
-import java.util.Arrays;
-
-import org.apache.lucene.document.LatLonPoint;
-import org.apache.lucene.index.PointValues.IntersectVisitor;
-import org.apache.lucene.index.PointValues.Relation;
-import org.apache.lucene.index.PointValues;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.util.DocIdSetBuilder;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-import org.apache.lucene.spatial.util.GeoUtils;
-
-/** Finds all previously indexed points that fall within the specified polygon.
- *
- * <p>The field must be indexed with using {@link org.apache.lucene.document.LatLonPoint} added per document.
- *
- * @lucene.experimental */
-
-public class PointInPolygonQuery extends Query {
- final String field;
- final double minLat;
- final double maxLat;
- final double minLon;
- final double maxLon;
- final double[] polyLats;
- final double[] polyLons;
-
- /** The lats/lons must be clockwise or counter-clockwise. */
- public PointInPolygonQuery(String field, double[] polyLats, double[] polyLons) {
- this.field = field;
- if (polyLats.length != polyLons.length) {
- throw new IllegalArgumentException("polyLats and polyLons must be equal length");
- }
- if (polyLats.length < 4) {
- throw new IllegalArgumentException("at least 4 polygon points required");
- }
- if (polyLats[0] != polyLats[polyLats.length-1]) {
- throw new IllegalArgumentException("first and last points of the polygon must be the same (it must close itself): polyLats[0]=" + polyLats[0] + " polyLats[" + (polyLats.length-1) + "]=" + polyLats[polyLats.length-1]);
- }
- if (polyLons[0] != polyLons[polyLons.length-1]) {
- throw new IllegalArgumentException("first and last points of the polygon must be the same (it must close itself): polyLons[0]=" + polyLons[0] + " polyLons[" + (polyLons.length-1) + "]=" + polyLons[polyLons.length-1]);
- }
-
- this.polyLats = polyLats;
- this.polyLons = polyLons;
-
- // TODO: we could also compute the maximal inner bounding box, to make relations faster to compute?
-
- double minLon = Double.POSITIVE_INFINITY;
- double minLat = Double.POSITIVE_INFINITY;
- double maxLon = Double.NEGATIVE_INFINITY;
- double maxLat = Double.NEGATIVE_INFINITY;
- for(int i=0;i<polyLats.length;i++) {
- double lat = polyLats[i];
- if (GeoUtils.isValidLat(lat) == false) {
- throw new IllegalArgumentException("polyLats[" + i + "]=" + lat + " is not a valid latitude");
- }
- minLat = Math.min(minLat, lat);
- maxLat = Math.max(maxLat, lat);
- double lon = polyLons[i];
- if (GeoUtils.isValidLon(lon) == false) {
- throw new IllegalArgumentException("polyLons[" + i + "]=" + lat + " is not a valid longitude");
- }
- minLon = Math.min(minLon, lon);
- maxLon = Math.max(maxLon, lon);
- }
- this.minLon = minLon;
- this.maxLon = maxLon;
- this.minLat = minLat;
- this.maxLat = maxLat;
- }
-
- @Override
- public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
-
- // I don't use RandomAccessWeight here: it's no good to approximate with "match all docs"; this is an inverted structure and should be
- // used in the first pass:
-
- // TODO: except that the polygon verify is costly! The approximation should be all docs in all overlapping cells, and matches() should
- // then check the polygon
-
- return new ConstantScoreWeight(this) {
-
- @Override
- public Scorer scorer(LeafReaderContext context) throws IOException {
- LeafReader reader = context.reader();
- PointValues values = reader.getPointValues();
- if (values == null) {
- // No docs in this segment had any points fields
- return null;
- }
-
- DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc());
- values.intersect(field,
- new IntersectVisitor() {
- @Override
- public void visit(int docID) {
- result.add(docID);
- }
-
- @Override
- public void visit(int docID, byte[] packedValue) {
- assert packedValue.length == 8;
- double lat = LatLonPoint.decodeLat(NumericUtils.bytesToInt(packedValue, 0));
- double lon = LatLonPoint.decodeLon(NumericUtils.bytesToInt(packedValue, Integer.BYTES));
- if (GeoRelationUtils.pointInPolygon(polyLons, polyLats, lat, lon)) {
- result.add(docID);
- }
- }
-
- @Override
- public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
- double cellMinLat = LatLonPoint.decodeLat(NumericUtils.bytesToInt(minPackedValue, 0));
- double cellMinLon = LatLonPoint.decodeLon(NumericUtils.bytesToInt(minPackedValue, Integer.BYTES));
- double cellMaxLat = LatLonPoint.decodeLat(NumericUtils.bytesToInt(maxPackedValue, 0));
- double cellMaxLon = LatLonPoint.decodeLon(NumericUtils.bytesToInt(maxPackedValue, Integer.BYTES));
-
- if (cellMinLat <= minLat && cellMaxLat >= maxLat && cellMinLon <= minLon && cellMaxLon >= maxLon) {
- // Cell fully encloses the query
- return Relation.CELL_CROSSES_QUERY;
- } else if (GeoRelationUtils.rectWithinPolyPrecise(cellMinLon, cellMinLat, cellMaxLon, cellMaxLat,
- polyLons, polyLats,
- minLon, minLat, maxLon, maxLat)) {
- return Relation.CELL_INSIDE_QUERY;
- } else if (GeoRelationUtils.rectCrossesPolyPrecise(cellMinLon, cellMinLat, cellMaxLon, cellMaxLat,
- polyLons, polyLats,
- minLon, minLat, maxLon, maxLat)) {
- return Relation.CELL_CROSSES_QUERY;
- } else {
- return Relation.CELL_OUTSIDE_QUERY;
- }
- }
- });
-
- return new ConstantScoreScorer(this, score(), result.build().iterator());
- }
- };
- }
-
- @Override
- @SuppressWarnings({"unchecked","rawtypes"})
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- if (!super.equals(o)) return false;
-
- PointInPolygonQuery that = (PointInPolygonQuery) o;
-
- if (Arrays.equals(polyLons, that.polyLons) == false) {
- return false;
- }
- if (Arrays.equals(polyLats, that.polyLats) == false) {
- return false;
- }
-
- return true;
- }
-
- @Override
- public final int hashCode() {
- int result = super.hashCode();
- result = 31 * result + Arrays.hashCode(polyLons);
- result = 31 * result + Arrays.hashCode(polyLats);
- return result;
- }
-
- @Override
- public String toString(String field) {
- final StringBuilder sb = new StringBuilder();
- sb.append(getClass().getSimpleName());
- sb.append(':');
- if (this.field.equals(field) == false) {
- sb.append(" field=");
- sb.append(this.field);
- sb.append(':');
- }
- sb.append(" Points: ");
- for (int i=0; i<polyLons.length; ++i) {
- sb.append("[")
- .append(polyLons[i])
- .append(", ")
- .append(polyLats[i])
- .append("] ");
- }
- return sb.toString();
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
index 34f4304..b67dec8 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
@@ -16,40 +16,22 @@
*/
package org.apache.lucene.document;
-import java.io.IOException;
-import java.util.BitSet;
-
-import org.apache.lucene.codecs.FilterCodec;
-import org.apache.lucene.codecs.PointFormat;
-import org.apache.lucene.codecs.PointReader;
-import org.apache.lucene.codecs.PointWriter;
-import org.apache.lucene.codecs.lucene60.Lucene60PointReader;
-import org.apache.lucene.codecs.lucene60.Lucene60PointWriter;
import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.RandomIndexWriter;
-import org.apache.lucene.index.SegmentReadState;
-import org.apache.lucene.index.SegmentWriteState;
import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.Sort;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.util.TestUtil;
-import org.apache.lucene.util.bkd.BKDWriter;
/** Simple tests for {@link LatLonPoint} */
public class TestLatLonPoint extends LuceneTestCase {
- /** Add a single address and search for it in a box */
+ /** Add a single point and search for it in a box */
// NOTE: we don't currently supply an exact search, only ranges, because of the lossiness...
- public void testBasics() throws Exception {
+ public void testBoxQuery() throws Exception {
Directory dir = newDirectory();
RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
- // add a doc with an address
+ // add a doc with a point
Document document = new Document();
document.add(new LatLonPoint("field", 18.313694, -65.227444));
writer.addDocument(document);
@@ -70,93 +52,122 @@ public class TestLatLonPoint extends LuceneTestCase {
// looks crazy due to lossiness
assertEquals("field:[17.99999997485429 TO 18.999999999068677},[-65.9999999217689 TO -64.99999998137355}", LatLonPoint.newBoxQuery("field", 18, 19, -66, -65).toString());
+
+ // distance query does not quantize inputs
+ assertEquals("field:18.0,19.0 +/- 25.0 meters", LatLonPoint.newDistanceQuery("field", 18, 19, 25).toString());
}
- public void testRadiusRandom() throws Exception {
- for (int iters = 0; iters < 100; iters++) {
- doRandomTest(10, 100);
- }
+ /** Valid values that should not cause exception */
+ public void testExtremeValues() {
+ new LatLonPoint("foo", 90.0, 180.0);
+ new LatLonPoint("foo", 90.0, -180.0);
+ new LatLonPoint("foo", -90.0, 180.0);
+ new LatLonPoint("foo", -90.0, -180.0);
}
- @Nightly
- public void testRadiusRandomHuge() throws Exception {
- for (int iters = 0; iters < 10; iters++) {
- doRandomTest(2000, 100);
- }
+ /** Invalid values */
+ public void testOutOfRangeValues() {
+ IllegalArgumentException expected;
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", Math.nextUp(90.0), 50.0);
+ });
+ assertTrue(expected.getMessage().contains("invalid latitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", Math.nextDown(-90.0), 50.0);
+ });
+ assertTrue(expected.getMessage().contains("invalid latitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", 90.0, Math.nextUp(180.0));
+ });
+ assertTrue(expected.getMessage().contains("invalid longitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", 90.0, Math.nextDown(-180.0));
+ });
+ assertTrue(expected.getMessage().contains("invalid longitude"));
}
- private void doRandomTest(int numDocs, int numQueries) throws IOException {
- Directory dir = newDirectory();
- IndexWriterConfig iwc = newIndexWriterConfig();
- int pointsInLeaf = 2 + random().nextInt(4);
- iwc.setCodec(new FilterCodec("Lucene60", TestUtil.getDefaultCodec()) {
- @Override
- public PointFormat pointFormat() {
- return new PointFormat() {
- @Override
- public PointWriter fieldsWriter(SegmentWriteState writeState) throws IOException {
- return new Lucene60PointWriter(writeState, pointsInLeaf, BKDWriter.DEFAULT_MAX_MB_SORT_IN_HEAP);
- }
+ /** NaN: illegal */
+ public void testNaNValues() {
+ IllegalArgumentException expected;
- @Override
- public PointReader fieldsReader(SegmentReadState readState) throws IOException {
- return new Lucene60PointReader(readState);
- }
- };
- }
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", Double.NaN, 50.0);
+ });
+ assertTrue(expected.getMessage().contains("invalid latitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", 50.0, Double.NaN);
});
- RandomIndexWriter writer = new RandomIndexWriter(random(), dir, iwc);
+ assertTrue(expected.getMessage().contains("invalid longitude"));
+ }
+
+ /** Inf: illegal */
+ public void testInfValues() {
+ IllegalArgumentException expected;
- for (int i = 0; i < numDocs; i++) {
- double latRaw = -90 + 180.0 * random().nextDouble();
- double lonRaw = -180 + 360.0 * random().nextDouble();
- // pre-normalize up front, so we can just use quantized value for testing and do simple exact comparisons
- double lat = LatLonPoint.decodeLat(LatLonPoint.encodeLat(latRaw));
- double lon = LatLonPoint.decodeLon(LatLonPoint.encodeLon(lonRaw));
- Document doc = new Document();
- doc.add(new LatLonPoint("field", lat, lon));
- doc.add(new StoredField("lat", lat));
- doc.add(new StoredField("lon", lon));
- writer.addDocument(doc);
- }
- IndexReader reader = writer.getReader();
- IndexSearcher searcher = new IndexSearcher(reader);
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", Double.POSITIVE_INFINITY, 50.0);
+ });
+ assertTrue(expected.getMessage().contains("invalid latitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", Double.NEGATIVE_INFINITY, 50.0);
+ });
+ assertTrue(expected.getMessage().contains("invalid latitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", 50.0, Double.POSITIVE_INFINITY);
+ });
+ assertTrue(expected.getMessage().contains("invalid longitude"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ new LatLonPoint("foo", 50.0, Double.NEGATIVE_INFINITY);
+ });
+ assertTrue(expected.getMessage().contains("invalid longitude"));
+ }
+
+ public void testEncodeDecode() throws Exception {
+ // just for testing quantization error
+ final double ENCODING_TOLERANCE = 1e-7;
- for (int i = 0; i < numQueries; i++) {
+ int iters = atLeast(10000);
+ for(int iter=0;iter<iters;iter++) {
double lat = -90 + 180.0 * random().nextDouble();
+ double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(lat));
+ assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, ENCODING_TOLERANCE);
+
double lon = -180 + 360.0 * random().nextDouble();
- double radius = 50000000 * random().nextDouble();
+ double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lon));
+ assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, ENCODING_TOLERANCE);
+ }
+
+ // check edge/interesting cases explicitly
+ assertEquals(0.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(0.0)), ENCODING_TOLERANCE);
+ assertEquals(90.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(90.0)), ENCODING_TOLERANCE);
+ assertEquals(-90.0, LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(-90.0)), ENCODING_TOLERANCE);
- BitSet expected = new BitSet();
- for (int doc = 0; doc < reader.maxDoc(); doc++) {
- double docLatitude = reader.document(doc).getField("lat").numericValue().doubleValue();
- double docLongitude = reader.document(doc).getField("lon").numericValue().doubleValue();
- double distance = GeoDistanceUtils.haversin(lat, lon, docLatitude, docLongitude);
- if (distance <= radius) {
- expected.set(doc);
- }
- }
+ assertEquals(0.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(0.0)), ENCODING_TOLERANCE);
+ assertEquals(180.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(180.0)), ENCODING_TOLERANCE);
+ assertEquals(-180.0, LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(-180.0)), ENCODING_TOLERANCE);
+ }
- TopDocs topDocs = searcher.search(LatLonPoint.newDistanceQuery("field", lat, lon, radius), reader.maxDoc(), Sort.INDEXORDER);
- BitSet actual = new BitSet();
- for (ScoreDoc doc : topDocs.scoreDocs) {
- actual.set(doc.doc);
- }
+ public void testEncodeDecodeIsStable() throws Exception {
+ int iters = atLeast(1000);
+ for(int iter=0;iter<iters;iter++) {
+ double lat = -90 + 180.0 * random().nextDouble();
+ double lon = -180 + 360.0 * random().nextDouble();
- try {
- assertEquals(expected, actual);
- } catch (AssertionError e) {
- for (int doc = 0; doc < reader.maxDoc(); doc++) {
- double docLatitude = reader.document(doc).getField("lat").numericValue().doubleValue();
- double docLongitude = reader.document(doc).getField("lon").numericValue().doubleValue();
- double distance = GeoDistanceUtils.haversin(lat, lon, docLatitude, docLongitude);
- System.out.println("" + doc + ": (" + docLatitude + "," + docLongitude + "), distance=" + distance);
- }
- throw e;
- }
+ double latEnc = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(lat));
+ double lonEnc = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lon));
+
+ double latEnc2 = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(latEnc));
+ double lonEnc2 = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lonEnc));
+ assertEquals(latEnc, latEnc2, 0.0);
+ assertEquals(lonEnc, lonEnc2, 0.0);
}
- reader.close();
- writer.close();
- dir.close();
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointDistanceQuery.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointDistanceQuery.java
new file mode 100644
index 0000000..3d47b44
--- /dev/null
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointDistanceQuery.java
@@ -0,0 +1,190 @@
+/*
+ * 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 java.util.BitSet;
+
+import org.apache.lucene.codecs.FilterCodec;
+import org.apache.lucene.codecs.PointFormat;
+import org.apache.lucene.codecs.PointReader;
+import org.apache.lucene.codecs.PointWriter;
+import org.apache.lucene.codecs.lucene60.Lucene60PointReader;
+import org.apache.lucene.codecs.lucene60.Lucene60PointWriter;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.SegmentReadState;
+import org.apache.lucene.index.SegmentWriteState;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopDocs;
+import org.apache.lucene.spatial.util.GeoDistanceUtils;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.TestUtil;
+import org.apache.lucene.util.LuceneTestCase;
+import org.apache.lucene.util.bkd.BKDWriter;
+
+/** Simple tests for {@link LatLonPoint#newDistanceQuery} */
+public class TestLatLonPointDistanceQuery extends LuceneTestCase {
+
+ /** test we can search for a point */
+ public void testBasics() throws Exception {
+ Directory dir = newDirectory();
+ RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
+
+ // add a doc with a location
+ Document document = new Document();
+ document.add(new LatLonPoint("field", 18.313694, -65.227444));
+ writer.addDocument(document);
+
+ // search within 50km and verify we found our doc
+ IndexReader reader = writer.getReader();
+ IndexSearcher searcher = newSearcher(reader);
+ assertEquals(1, searcher.count(LatLonPoint.newDistanceQuery("field", 18, -65, 50_000)));
+
+ reader.close();
+ writer.close();
+ dir.close();
+ }
+
+ /** negative distance queries are not allowed */
+ public void testNegativeRadius() {
+ IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
+ LatLonPoint.newDistanceQuery("field", 18, 19, -1);
+ });
+ assertTrue(expected.getMessage().contains("radiusMeters"));
+ assertTrue(expected.getMessage().contains("is invalid"));
+ }
+
+ /** NaN distance queries are not allowed */
+ public void testNaNRadius() {
+ IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> {
+ LatLonPoint.newDistanceQuery("field", 18, 19, Double.NaN);
+ });
+ assertTrue(expected.getMessage().contains("radiusMeters"));
+ assertTrue(expected.getMessage().contains("is invalid"));
+ }
+
+ /** Inf distance queries are not allowed */
+ public void testInfRadius() {
+ IllegalArgumentException expected;
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ LatLonPoint.newDistanceQuery("field", 18, 19, Double.POSITIVE_INFINITY);
+ });
+ assertTrue(expected.getMessage().contains("radiusMeters"));
+ assertTrue(expected.getMessage().contains("is invalid"));
+
+ expected = expectThrows(IllegalArgumentException.class, () -> {
+ LatLonPoint.newDistanceQuery("field", 18, 19, Double.NEGATIVE_INFINITY);
+ });
+ assertTrue(expected.getMessage().contains("radiusMeters"));
+ assertTrue(expected.getMessage().contains("is invalid"));
+ }
+
+ /** Run a few iterations with just 10 docs, hopefully easy to debug */
+ public void testRandom() throws Exception {
+ for (int iters = 0; iters < 100; iters++) {
+ doRandomTest(10, 100);
+ }
+ }
+
+ /** Runs with thousands of docs */
+ @Nightly
+ public void testRandomHuge() throws Exception {
+ for (int iters = 0; iters < 10; iters++) {
+ doRandomTest(2000, 100);
+ }
+ }
+
+ private void doRandomTest(int numDocs, int numQueries) throws IOException {
+ Directory dir = newDirectory();
+ IndexWriterConfig iwc = newIndexWriterConfig();
+ int pointsInLeaf = 2 + random().nextInt(4);
+ iwc.setCodec(new FilterCodec("Lucene60", TestUtil.getDefaultCodec()) {
+ @Override
+ public PointFormat pointFormat() {
+ return new PointFormat() {
+ @Override
+ public PointWriter fieldsWriter(SegmentWriteState writeState) throws IOException {
+ return new Lucene60PointWriter(writeState, pointsInLeaf, BKDWriter.DEFAULT_MAX_MB_SORT_IN_HEAP);
+ }
+
+ @Override
+ public PointReader fieldsReader(SegmentReadState readState) throws IOException {
+ return new Lucene60PointReader(readState);
+ }
+ };
+ }
+ });
+ RandomIndexWriter writer = new RandomIndexWriter(random(), dir, iwc);
+
+ for (int i = 0; i < numDocs; i++) {
+ double latRaw = -90 + 180.0 * random().nextDouble();
+ double lonRaw = -180 + 360.0 * random().nextDouble();
+ // pre-normalize up front, so we can just use quantized value for testing and do simple exact comparisons
+ double lat = LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(latRaw));
+ double lon = LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lonRaw));
+ Document doc = new Document();
+ doc.add(new LatLonPoint("field", lat, lon));
+ doc.add(new StoredField("lat", lat));
+ doc.add(new StoredField("lon", lon));
+ writer.addDocument(doc);
+ }
+ IndexReader reader = writer.getReader();
+ IndexSearcher searcher = new IndexSearcher(reader);
+
+ for (int i = 0; i < numQueries; i++) {
+ double lat = -90 + 180.0 * random().nextDouble();
+ double lon = -180 + 360.0 * random().nextDouble();
+ double radius = 50000000 * random().nextDouble();
+
+ BitSet expected = new BitSet();
+ for (int doc = 0; doc < reader.maxDoc(); doc++) {
+ double docLatitude = reader.document(doc).getField("lat").numericValue().doubleValue();
+ double docLongitude = reader.document(doc).getField("lon").numericValue().doubleValue();
+ double distance = GeoDistanceUtils.haversin(lat, lon, docLatitude, docLongitude);
+ if (distance <= radius) {
+ expected.set(doc);
+ }
+ }
+
+ TopDocs topDocs = searcher.search(LatLonPoint.newDistanceQuery("field", lat, lon, radius), reader.maxDoc(), Sort.INDEXORDER);
+ BitSet actual = new BitSet();
+ for (ScoreDoc doc : topDocs.scoreDocs) {
+ actual.set(doc.doc);
+ }
+
+ try {
+ assertEquals(expected, actual);
+ } catch (AssertionError e) {
+ for (int doc = 0; doc < reader.maxDoc(); doc++) {
+ double docLatitude = reader.document(doc).getField("lat").numericValue().doubleValue();
+ double docLongitude = reader.document(doc).getField("lon").numericValue().doubleValue();
+ double distance = GeoDistanceUtils.haversin(lat, lon, docLatitude, docLongitude);
+ System.out.println("" + doc + ": (" + docLatitude + "," + docLongitude + "), distance=" + distance);
+ }
+ throw e;
+ }
+ }
+ reader.close();
+ writer.close();
+ dir.close();
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/2264600f/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java b/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
index 7798b97..8eb1be0 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
@@ -23,9 +23,8 @@ import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.spatial.util.GeoRect;
public class TestLatLonPointQueries extends BaseGeoPointTestCase {
- // todo deconflict GeoPoint and BKD encoding methods and error tolerance
+ // TODO: remove this!
public static final double BKD_TOLERANCE = 1e-7;
- public static final double ENCODING_TOLERANCE = 1e-7;
@Override
protected void addPointToDoc(String field, Document doc, double lat, double lon) {
@@ -57,13 +56,13 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
assert Double.isNaN(pointLat) == false;
- int rectLatMinEnc = LatLonPoint.encodeLat(rect.minLat);
- int rectLatMaxEnc = LatLonPoint.encodeLat(rect.maxLat);
- int rectLonMinEnc = LatLonPoint.encodeLon(rect.minLon);
- int rectLonMaxEnc = LatLonPoint.encodeLon(rect.maxLon);
+ int rectLatMinEnc = LatLonPoint.encodeLatitude(rect.minLat);
+ int rectLatMaxEnc = LatLonPoint.encodeLatitude(rect.maxLat);
+ int rectLonMinEnc = LatLonPoint.encodeLongitude(rect.minLon);
+ int rectLonMaxEnc = LatLonPoint.encodeLongitude(rect.maxLon);
- int pointLatEnc = LatLonPoint.encodeLat(pointLat);
- int pointLonEnc = LatLonPoint.encodeLon(pointLon);
+ int pointLatEnc = LatLonPoint.encodeLatitude(pointLat);
+ int pointLonEnc = LatLonPoint.encodeLongitude(pointLon);
if (rect.minLon < rect.maxLon) {
return pointLatEnc >= rectLatMinEnc &&
@@ -81,12 +80,12 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
@Override
protected double quantizeLat(double latRaw) {
- return LatLonPoint.decodeLat(LatLonPoint.encodeLat(latRaw));
+ return LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(latRaw));
}
@Override
protected double quantizeLon(double lonRaw) {
- return LatLonPoint.decodeLon(LatLonPoint.encodeLon(lonRaw));
+ return LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lonRaw));
}
// todo reconcile with GeoUtils (see LUCENE-6996)
@@ -112,13 +111,13 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
return null;
}
- int rectLatMinEnc = LatLonPoint.encodeLat(rect.minLat);
- int rectLatMaxEnc = LatLonPoint.encodeLat(rect.maxLat);
- int rectLonMinEnc = LatLonPoint.encodeLon(rect.minLon);
- int rectLonMaxEnc = LatLonPoint.encodeLon(rect.maxLon);
+ int rectLatMinEnc = LatLonPoint.encodeLatitude(rect.minLat);
+ int rectLatMaxEnc = LatLonPoint.encodeLatitude(rect.maxLat);
+ int rectLonMinEnc = LatLonPoint.encodeLongitude(rect.minLon);
+ int rectLonMaxEnc = LatLonPoint.encodeLongitude(rect.maxLon);
- int pointLatEnc = LatLonPoint.encodeLat(pointLat);
- int pointLonEnc = LatLonPoint.encodeLon(pointLon);
+ int pointLatEnc = LatLonPoint.encodeLatitude(pointLat);
+ int pointLonEnc = LatLonPoint.encodeLongitude(pointLon);
if (rect.minLon < rect.maxLon) {
return pointLatEnc >= rectLatMinEnc &&
@@ -147,35 +146,4 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
final double d = GeoDistanceUtils.haversin(centerLat, centerLon, pointLat, pointLon);
return d >= minRadiusMeters && d <= radiusMeters;
}
-
- public void testEncodeDecode() throws Exception {
- int iters = atLeast(10000);
- boolean small = random().nextBoolean();
- for(int iter=0;iter<iters;iter++) {
- double lat = randomLat(small);
- double latEnc = LatLonPoint.decodeLat(LatLonPoint.encodeLat(lat));
- assertEquals("lat=" + lat + " latEnc=" + latEnc + " diff=" + (lat - latEnc), lat, latEnc, ENCODING_TOLERANCE);
-
- double lon = randomLon(small);
- double lonEnc = LatLonPoint.decodeLon(LatLonPoint.encodeLon(lon));
- assertEquals("lon=" + lon + " lonEnc=" + lonEnc + " diff=" + (lon - lonEnc), lon, lonEnc, ENCODING_TOLERANCE);
- }
- }
-
- public void testScaleUnscaleIsStable() throws Exception {
- int iters = atLeast(1000);
- boolean small = random().nextBoolean();
- for(int iter=0;iter<iters;iter++) {
- double lat = randomLat(small);
- double lon = randomLon(small);
-
- double latEnc = LatLonPoint.decodeLat(LatLonPoint.encodeLat(lat));
- double lonEnc = LatLonPoint.decodeLon(LatLonPoint.encodeLon(lon));
-
- double latEnc2 = LatLonPoint.decodeLat(LatLonPoint.encodeLat(latEnc));
- double lonEnc2 = LatLonPoint.decodeLon(LatLonPoint.encodeLon(lonEnc));
- assertEquals(latEnc, latEnc2, 0.0);
- assertEquals(lonEnc, lonEnc2, 0.0);
- }
- }
}