You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2016/03/22 22:12:34 UTC
lucene-solr:branch_6x: LUCENE-7127: remove epsilon-based testing from
lucene/spatial, fix distance bugs.
Repository: lucene-solr
Updated Branches:
refs/heads/branch_6x c5da271b9 -> ee1aca864
LUCENE-7127: remove epsilon-based testing from lucene/spatial, fix distance bugs.
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/ee1aca86
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/ee1aca86
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/ee1aca86
Branch: refs/heads/branch_6x
Commit: ee1aca86435e501d02c23a93f480f076a8b72f34
Parents: c5da271
Author: Robert Muir <rm...@apache.org>
Authored: Tue Mar 22 16:53:25 2016 -0400
Committer: Robert Muir <rm...@apache.org>
Committed: Tue Mar 22 16:55:33 2016 -0400
----------------------------------------------------------------------
lucene/CHANGES.txt | 4 +
.../document/TestLatLonPointDistanceQuery.java | 108 -------
.../lucene/search/TestLatLonPointQueries.java | 45 ---
.../search/GeoPointDistanceQueryImpl.java | 24 +-
.../lucene/spatial/util/GeoDistanceUtils.java | 8 +-
.../lucene/spatial/util/GeoEncodingUtils.java | 6 -
.../lucene/spatial/util/GeoProjectionUtils.java | 158 ----------
.../lucene/spatial/util/GeoRelationUtils.java | 250 ---------------
.../apache/lucene/spatial/util/GeoUtils.java | 44 +--
.../geopoint/search/TestGeoPointQuery.java | 84 +----
.../search/TestLegacyGeoPointQuery.java | 83 ++---
.../spatial/util/BaseGeoPointTestCase.java | 206 ++++++++++---
.../lucene/spatial/util/TestGeoUtils.java | 308 -------------------
13 files changed, 234 insertions(+), 1094 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 138f6bd..6dbacef 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -29,6 +29,10 @@ Optimizations
* LUCENE-7115: Speed up FieldCache.CacheEntry toString by setting initial
StringBuilder capacity (Gregory Chanan)
+Bug Fixes
+
+* LUCENE-7127: Fix corner case bugs in GeoPointDistanceQuery. (Robert Muir)
+
======================= Lucene 6.0.0 =======================
System Requirements
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/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
index 983b1a7..f015780 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointDistanceQuery.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPointDistanceQuery.java
@@ -16,29 +16,11 @@
*/
package org.apache.lucene.document;
-import java.io.IOException;
-import java.util.BitSet;
-
-import org.apache.lucene.codecs.FilterCodec;
-import org.apache.lucene.codecs.PointsFormat;
-import org.apache.lucene.codecs.PointsReader;
-import org.apache.lucene.codecs.PointsWriter;
-import org.apache.lucene.codecs.lucene60.Lucene60PointsReader;
-import org.apache.lucene.codecs.lucene60.Lucene60PointsWriter;
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.store.Directory;
import org.apache.lucene.util.LuceneTestCase;
-import org.apache.lucene.util.SloppyMath;
-import org.apache.lucene.util.TestUtil;
-import org.apache.lucene.util.bkd.BKDWriter;
/** Simple tests for {@link LatLonPoint#newDistanceQuery} */
public class TestLatLonPointDistanceQuery extends LuceneTestCase {
@@ -97,94 +79,4 @@ public class TestLatLonPointDistanceQuery extends LuceneTestCase {
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 PointsFormat pointsFormat() {
- return new PointsFormat() {
- @Override
- public PointsWriter fieldsWriter(SegmentWriteState writeState) throws IOException {
- return new Lucene60PointsWriter(writeState, pointsInLeaf, BKDWriter.DEFAULT_MAX_MB_SORT_IN_HEAP);
- }
-
- @Override
- public PointsReader fieldsReader(SegmentReadState readState) throws IOException {
- return new Lucene60PointsReader(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 = newSearcher(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 = SloppyMath.haversinMeters(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 = SloppyMath.haversinMeters(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/ee1aca86/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 df34940..71ef7ef 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
@@ -20,8 +20,6 @@ import org.apache.lucene.document.Document;
import org.apache.lucene.document.LatLonPoint;
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-import org.apache.lucene.util.SloppyMath;
public class TestLatLonPointQueries extends BaseGeoPointTestCase {
@@ -51,19 +49,6 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
}
@Override
- protected Boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- assert Double.isNaN(pointLat) == false;
-
- if (rect.minLon < rect.maxLon) {
- return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
- } else {
- // Rect crosses dateline:
- return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, -180.0, rect.maxLon)
- || GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, 180.0);
- }
- }
-
- @Override
protected double quantizeLat(double latRaw) {
return LatLonPoint.decodeLatitude(LatLonPoint.encodeLatitude(latRaw));
}
@@ -72,34 +57,4 @@ public class TestLatLonPointQueries extends BaseGeoPointTestCase {
protected double quantizeLon(double lonRaw) {
return LatLonPoint.decodeLongitude(LatLonPoint.encodeLongitude(lonRaw));
}
-
- @Override
- protected Boolean polyRectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- // TODO write better random polygon tests
-
- // note: logic must be slightly different than rectContainsPoint, to satisfy
- // insideness for cases exactly on boundaries.
-
- assert Double.isNaN(pointLat) == false;
- assert rect.crossesDateline() == false;
- double polyLats[] = new double[] { rect.minLat, rect.maxLat, rect.maxLat, rect.minLat, rect.minLat };
- double polyLons[] = new double[] { rect.minLon, rect.minLon, rect.maxLon, rect.maxLon, rect.minLon };
-
- // TODO: separately test this method is 100% correct, here treat it like a black box (like haversin)
- return GeoRelationUtils.pointInPolygon(polyLats, polyLons, pointLat, pointLon);
- }
-
- @Override
- protected Boolean circleContainsPoint(double centerLat, double centerLon, double radiusMeters, double pointLat, double pointLon) {
- double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
- boolean result = distanceMeters <= radiusMeters;
- //System.out.println(" shouldMatch? centerLon=" + centerLon + " centerLat=" + centerLat + " pointLon=" + pointLon + " pointLat=" + pointLat + " result=" + result + " distanceMeters=" + (distanceKM * 1000));
- return result;
- }
-
- @Override
- protected Boolean distanceRangeContainsPoint(double centerLat, double centerLon, double minRadiusMeters, double radiusMeters, double pointLat, double pointLon) {
- final double d = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
- return d >= minRadiusMeters && d <= radiusMeters;
- }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java
index 8f50db5..d4272a3 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java
@@ -19,7 +19,6 @@ package org.apache.lucene.spatial.geopoint.search;
import org.apache.lucene.search.MultiTermQuery;
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
import org.apache.lucene.util.SloppyMath;
/** Package private implementation for the public facing GeoPointDistanceQuery delegate class.
@@ -54,15 +53,28 @@ final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
@Override
protected boolean cellCrosses(final double minLat, final double maxLat, final double minLon, final double maxLon) {
- return GeoRelationUtils.rectCrossesCircle(minLat, maxLat, minLon, maxLon,
- distanceQuery.centerLat, centerLon, distanceQuery.radiusMeters, true);
+ if (maxLat < GeoPointDistanceQueryImpl.this.minLat ||
+ maxLon < GeoPointDistanceQueryImpl.this.minLon ||
+ minLat > GeoPointDistanceQueryImpl.this.maxLat ||
+ minLon > GeoPointDistanceQueryImpl.this.maxLon) {
+ return false;
+ } else {
+ return true;
+ }
}
@Override
protected boolean cellWithin(final double minLat, final double maxLat, final double minLon, final double maxLon) {
- return GeoRelationUtils.rectWithinCircle(minLat, maxLat, minLon, maxLon,
- distanceQuery.centerLat, centerLon,
- distanceQuery.radiusMeters, true);
+ // TODO: we call cellCrosses because of how the termsEnum logic works, helps us avoid some haversin() calls here.
+ if (cellCrosses(minLat, maxLat, minLon, maxLon) && maxLon - centerLon < 90 && centerLon - minLon < 90 &&
+ SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, minLat, minLon) <= distanceQuery.radiusMeters &&
+ SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, minLat, maxLon) <= distanceQuery.radiusMeters &&
+ SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, maxLat, minLon) <= distanceQuery.radiusMeters &&
+ SloppyMath.haversinMeters(distanceQuery.centerLat, centerLon, maxLat, maxLon) <= distanceQuery.radiusMeters) {
+ // we are fully enclosed, collect everything within this subtree
+ return true;
+ }
+ return false;
}
@Override
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoDistanceUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoDistanceUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoDistanceUtils.java
index 8956c4b..32a16cd 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoDistanceUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoDistanceUtils.java
@@ -18,16 +18,12 @@ package org.apache.lucene.spatial.util;
import org.apache.lucene.util.SloppyMath;
-import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
-
/**
* Reusable geo-spatial distance utility methods.
*
* @lucene.experimental
*/
public class GeoDistanceUtils {
- /** error threshold for point-distance queries (in percent) NOTE: Guideline from USGS is 0.005 **/
- public static final double DISTANCE_PCT_ERR = 0.005;
// No instance:
private GeoDistanceUtils() {
@@ -58,7 +54,7 @@ public class GeoDistanceUtils {
lat = StrictMath.toRadians(lat);
// get the diameter at the latitude
- final double diameter = 2 * GeoProjectionUtils.SEMIMAJOR_AXIS;
+ final double diameter = 2 * GeoUtils.SEMIMAJOR_AXIS;
// compute inverse haversine
double a = StrictMath.sin(distance/diameter);
@@ -125,7 +121,7 @@ public class GeoDistanceUtils {
*/
public static double distanceToDegreesLat(double lat, double distance) {
// get the diameter at the latitude
- final double diameter = 2 * GeoProjectionUtils.SEMIMAJOR_AXIS;
+ final double diameter = 2 * GeoUtils.SEMIMAJOR_AXIS;
// compute inverse haversine
double a = StrictMath.sin(distance/diameter);
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
index 38eef04..7bed892 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoEncodingUtils.java
@@ -84,12 +84,6 @@ public final class GeoEncodingUtils {
return (val / LAT_SCALE) + MIN_LAT_INCL;
}
- /** Compare two position values within a {@link GeoEncodingUtils#TOLERANCE} factor */
- public static double compare(final double v1, final double v2) {
- final double delta = v1-v2;
- return Math.abs(delta) <= TOLERANCE ? 0 : delta;
- }
-
/** Convert a geocoded morton long into a prefix coded geo term */
public static void geoCodedToPrefixCoded(long hash, int shift, BytesRefBuilder bytes) {
geoCodedToPrefixCodedBytes(hash, shift, bytes);
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoProjectionUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoProjectionUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoProjectionUtils.java
deleted file mode 100644
index 61aa09f..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoProjectionUtils.java
+++ /dev/null
@@ -1,158 +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.spatial.util;
-
-import static java.lang.StrictMath.sqrt;
-
-import static org.apache.lucene.util.SloppyMath.asin;
-import static org.apache.lucene.util.SloppyMath.cos;
-import static org.apache.lucene.util.SloppyMath.TO_DEGREES;
-import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
-
-import static org.apache.lucene.spatial.util.GeoUtils.MAX_LAT_INCL;
-import static org.apache.lucene.spatial.util.GeoUtils.MAX_LON_INCL;
-import static org.apache.lucene.spatial.util.GeoUtils.MIN_LAT_INCL;
-import static org.apache.lucene.spatial.util.GeoUtils.MIN_LON_INCL;
-import static org.apache.lucene.spatial.util.GeoUtils.PIO2;
-import static org.apache.lucene.spatial.util.GeoUtils.normalizeLat;
-import static org.apache.lucene.spatial.util.GeoUtils.normalizeLon;
-import static org.apache.lucene.spatial.util.GeoUtils.sloppySin;
-import static org.apache.lucene.spatial.util.GeoUtils.sloppyTan;
-
-/**
- * Reusable geo-spatial projection utility methods.
- *
- * @lucene.experimental
- */
-public class GeoProjectionUtils {
- // WGS84 earth-ellipsoid parameters
- /** major (a) axis in meters */
- public static final double SEMIMAJOR_AXIS = 6_378_137; // [m]
- /** earth flattening factor (f) */
- public static final double FLATTENING = 1.0/298.257223563;
- /** minor (b) axis in meters */
- public static final double SEMIMINOR_AXIS = SEMIMAJOR_AXIS * (1.0 - FLATTENING); //6_356_752.31420; // [m]
- /** first eccentricity (e) */
- public static final double ECCENTRICITY = sqrt((2.0 - FLATTENING) * FLATTENING);
- /** major axis squared (a2) */
- public static final double SEMIMAJOR_AXIS2 = SEMIMAJOR_AXIS * SEMIMAJOR_AXIS;
- /** minor axis squared (b2) */
- public static final double SEMIMINOR_AXIS2 = SEMIMINOR_AXIS * SEMIMINOR_AXIS;
- private static final double E2 = (SEMIMAJOR_AXIS2 - SEMIMINOR_AXIS2)/(SEMIMAJOR_AXIS2);
- private static final double EP2 = (SEMIMAJOR_AXIS2 - SEMIMINOR_AXIS2)/(SEMIMINOR_AXIS2);
-
- /** min longitude value in radians */
- public static final double MIN_LON_RADIANS = TO_RADIANS * MIN_LON_INCL;
- /** min latitude value in radians */
- public static final double MIN_LAT_RADIANS = TO_RADIANS * MIN_LAT_INCL;
- /** max longitude value in radians */
- public static final double MAX_LON_RADIANS = TO_RADIANS * MAX_LON_INCL;
- /** max latitude value in radians */
- public static final double MAX_LAT_RADIANS = TO_RADIANS * MAX_LAT_INCL;
-
- // No instance:
- private GeoProjectionUtils() {
- }
-
- /**
- * Converts from geodesic lat lon alt to geocentric earth-centered earth-fixed
- * @param lat geodesic latitude
- * @param lon geodesic longitude
- * @param alt geodesic altitude
- * @param ecf reusable earth-centered earth-fixed result
- * @return either a new ecef array or the reusable ecf parameter
- */
- public static final double[] llaToECF(double lat, double lon, double alt, double[] ecf) {
- lon = TO_RADIANS * lon;
- lat = TO_RADIANS * lat;
-
- final double sl = sloppySin(lat);
- final double s2 = sl*sl;
- final double cl = cos(lat);
-
- if (ecf == null) {
- ecf = new double[3];
- }
-
- if (lat < -PIO2 && lat > -1.001D * PIO2) {
- lat = -PIO2;
- } else if (lat > PIO2 && lat < 1.001D * PIO2) {
- lat = PIO2;
- }
- assert (lat >= -PIO2) || (lat <= PIO2);
-
- if (lon > StrictMath.PI) {
- lon -= (2*StrictMath.PI);
- }
-
- final double rn = SEMIMAJOR_AXIS / StrictMath.sqrt(1.0D - E2 * s2);
- ecf[0] = (rn+alt) * cl * cos(lon);
- ecf[1] = (rn+alt) * cl * sloppySin(lon);
- ecf[2] = ((rn*(1.0-E2))+alt)*sl;
-
- return ecf;
- }
-
- /**
- * Finds a point along a bearing from a given lat,lon geolocation using great circle arc
- *
- * @param lat origin latitude in degrees
- * @param lon origin longitude in degrees
- * @param bearing azimuthal bearing in degrees
- * @param dist distance in meters
- * @param pt resulting point
- * @return the point along a bearing at a given distance in meters
- */
- public static final double[] pointFromLonLatBearingGreatCircle(double lat, double lon, double bearing, double dist, double[] pt) {
-
- if (pt == null) {
- pt = new double[2];
- }
-
- lat *= TO_RADIANS;
- lon *= TO_RADIANS;
- bearing *= TO_RADIANS;
-
- final double cLat = cos(lat);
- final double sLat = sloppySin(lat);
- final double sinDoR = sloppySin(dist / GeoProjectionUtils.SEMIMAJOR_AXIS);
- final double cosDoR = cos(dist / GeoProjectionUtils.SEMIMAJOR_AXIS);
-
- pt[1] = asin(sLat*cosDoR + cLat * sinDoR * cos(bearing));
- pt[0] = TO_DEGREES * (lon + Math.atan2(sloppySin(bearing) * sinDoR * cLat, cosDoR - sLat * sloppySin(pt[1])));
- pt[1] *= TO_DEGREES;
-
- return pt;
- }
-
- /**
- * Finds the bearing (in degrees) between 2 geo points (lat, lon) using great circle arc
- * @param lat1 first point latitude in degrees
- * @param lon1 first point longitude in degrees
- * @param lat2 second point latitude in degrees
- * @param lon2 second point longitude in degrees
- * @return the bearing (in degrees) between the two provided points
- */
- public static double bearingGreatCircle(double lat1, double lon1, double lat2, double lon2) {
- double dLon = (lon2 - lon1) * TO_RADIANS;
- lat2 *= TO_RADIANS;
- lat1 *= TO_RADIANS;
- double y = sloppySin(dLon) * cos(lat2);
- double x = cos(lat1) * sloppySin(lat2) - sloppySin(lat1) * cos(lat2) * cos(dLon);
- return Math.atan2(y, x) * TO_DEGREES;
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
index 4a31202..ba94e9d 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoRelationUtils.java
@@ -16,8 +16,6 @@
*/
package org.apache.lucene.spatial.util;
-import org.apache.lucene.util.SloppyMath;
-
/**
* Reusable geo-relation utility methods
*/
@@ -277,252 +275,4 @@ public class GeoRelationUtils {
return !(rectCrossesPolyApprox(rMinLat, rMaxLat, rMinLon, rMaxLon, shapeLats, shapeLons, sMinLat, sMaxLat, sMinLon, sMaxLon)
|| !pointInPolygon(shapeLats, shapeLons, rMinLat, rMinLon));
}
-
- /////////////////////////
- // Circle relations
- /////////////////////////
-
- private static boolean rectAnyCornersInCircle(final double rMinLat, final double rMaxLat, final double rMinLon,
- final double rMaxLon, final double centerLat, final double centerLon,
- final double radiusMeters, final boolean approx) {
- if (approx == true) {
- return rectAnyCornersInCircleSloppy(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters);
- }
- double w = Math.abs(rMaxLon - rMinLon);
- if (w <= 90.0) {
- return SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMinLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMinLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMaxLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMaxLon) <= radiusMeters;
- }
- // partition
- w /= 4;
- final double p1 = rMinLon + w;
- final double p2 = p1 + w;
- final double p3 = p2 + w;
-
- return SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMinLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMinLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, p1) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, p1) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, p2) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, p2) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, p3) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, p3) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMaxLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMaxLon) <= radiusMeters;
- }
-
- private static boolean rectAnyCornersInCircleSloppy(final double rMinLat, final double rMaxLat, final double rMinLon, final double rMaxLon,
- final double centerLat, final double centerLon, final double radiusMeters) {
- return SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMinLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMinLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMaxLon) <= radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMaxLon) <= radiusMeters;
- }
-
- /**
- * Compute whether any of the 4 corners of the rectangle (defined by min/max X/Y) are outside the circle (defined
- * by centerLon, centerLat, radiusMeters)
- *
- * Note: exotic rectangles at the poles (e.g., those whose lat/lon distance ratios greatly deviate from 1) can not
- * be determined by using distance alone. For this reason the approx flag may be set to false, in which case the
- * space will be further divided to more accurately compute whether the rectangle crosses the circle
- */
- private static boolean rectAnyCornersOutsideCircle(final double rMinLat, final double rMaxLat, final double rMinLon,
- final double rMaxLon, final double centerLat, final double centerLon,
- final double radiusMeters, final boolean approx) {
- if (approx == true) {
- return rectAnyCornersOutsideCircleSloppy(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters);
- }
- // if span is less than 70 degrees we can approximate using distance alone
- if (Math.abs(rMaxLon - rMinLon) <= 70.0) {
- return SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMinLon) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMinLon) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMaxLon) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMaxLon) > radiusMeters;
- }
- return rectCrossesOblateCircle(centerLat, centerLon,
- radiusMeters,
- rMinLat, rMaxLat,
- rMinLon, rMaxLon);
- }
-
- /**
- * Compute whether the rectangle (defined by min/max Lat/Lon) crosses a potentially oblate circle
- *
- * TODO benchmark for replacing existing rectCrossesCircle.
- */
- private static boolean rectCrossesOblateCircle(double centerLat, double centerLon,
- double radiusMeters,
- double rMinLat, double rMaxLat,
- double rMinLon, double rMaxLon) {
- double w = Math.abs(rMaxLon - rMinLon);
- final int segs = (int)Math.ceil(w / 45.0);
- w /= segs;
- short i = 1;
- double p1 = rMinLon;
- double maxLon, midLon;
- double[] pt = new double[2];
-
- do {
- maxLon = (i == segs) ? rMaxLon : p1 + w;
-
- final double d1, d2;
- // short-circuit if we find a corner outside the circle
- if ( (d1 = SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, p1)) > radiusMeters
- || (d2 = SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, maxLon)) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, p1) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, maxLon) > radiusMeters) {
- return true;
- }
-
- // else we treat as an oblate circle by slicing the longitude space and checking the azimuthal range
- // OPTIMIZATION: this is only executed for latitude values "closeTo" the poles (e.g., 88.0 > lat < -88.0)
- if ( (rMaxLat > 88.0 || rMinLat < -88.0)
- && (pt = GeoProjectionUtils.pointFromLonLatBearingGreatCircle(rMinLat, p1,
- GeoProjectionUtils.bearingGreatCircle(rMinLat, p1, rMaxLat, p1), radiusMeters - d1, pt))[1] < rMinLat || pt[1] < rMaxLat
- || (pt = GeoProjectionUtils.pointFromLonLatBearingGreatCircle(rMinLat, maxLon,
- GeoProjectionUtils.bearingGreatCircle(rMinLat, maxLon, rMaxLat, maxLon), radiusMeters - d2, pt))[1] < rMinLat || pt[1] < rMaxLat
- || (pt = GeoProjectionUtils.pointFromLonLatBearingGreatCircle(rMinLat, maxLon,
- GeoProjectionUtils.bearingGreatCircle(rMinLat, maxLon, rMaxLat, (midLon = p1 + 0.5*(maxLon - p1))),
- radiusMeters - SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, midLon), pt))[1] < rMinLat
- || pt[1] < rMaxLat == false ) {
- return true;
- }
- p1 += w;
- } while (++i <= segs);
- return false;
- }
-
- private static boolean rectAnyCornersOutsideCircleSloppy(final double rMinLat, final double rMaxLat, final double rMinLon, final double rMaxLon,
- final double centerLat, final double centerLon, final double radiusMeters) {
- return SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMinLon) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMinLon) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMaxLat, rMaxLon) > radiusMeters
- || SloppyMath.haversinMeters(centerLat, centerLon, rMinLat, rMaxLon) > radiusMeters;
- }
-
- /**
- * Computes whether a rectangle is within a circle. Note: approx == true will be faster but less precise and may
- * fail on large rectangles
- */
- public static boolean rectWithinCircle(final double rMinLat, final double rMaxLat, final double rMinLon, final double rMaxLon,
- final double centerLat, final double centerLon, final double radiusMeters,
- final boolean approx) {
- return rectAnyCornersOutsideCircle(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters, approx) == false;
- }
-
- /**
- * Computes whether a rectangle crosses a circle. Note: approx == true will be faster but less precise and may
- * fail on large rectangles
- *
- * <p>NOTE: this is basic method that does not handle dateline or pole crossing. Unwrapping must be done before
- * calling this method.
- */
- public static boolean rectCrossesCircle(final double rMinLat, final double rMaxLat, final double rMinLon, final double rMaxLon,
- final double centerLat, final double centerLon, final double radiusMeters,
- final boolean approx) {
- if (approx == true) {
- if (rectAnyCornersInCircle(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters, approx)) {
- return true;
- }
- } else {
- if (rectAnyCornersInCircle(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters, approx) &&
- rectAnyCornersOutsideCircle(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters, approx)) {
- return true;
- }
- }
- if (isClosestPointOnRectWithinRange(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, radiusMeters, approx)) {
- return true;
- }
- return false;
- }
-
- private static boolean isClosestPointOnRectWithinRange(final double rMinLat, final double rMaxLat,
- final double rMinLon, final double rMaxLon,
- final double centerLat, final double centerLon,
- final double radiusMeters,
- final boolean approx) {
- double[] closestPt = {0, 0};
- GeoDistanceUtils.closestPointOnBBox(rMinLat, rMaxLat, rMinLon, rMaxLon, centerLat, centerLon, closestPt);
- boolean haverShortCut = SloppyMath.haversinMeters(centerLat, centerLon, closestPt[0], closestPt[1]) <= radiusMeters;
- if (approx == true || haverShortCut == true) {
- return haverShortCut;
- }
- double lon1 = rMinLon;
- double lon2 = rMaxLon;
- double lat1 = rMinLat;
- double lat2 = rMaxLat;
- if (closestPt[1] == rMinLon || closestPt[1] == rMaxLon) {
- lon1 = closestPt[1];
- lon2 = lon1;
- } else if (closestPt[0] == rMinLat || closestPt[0] == rMaxLat) {
- lat1 = closestPt[0];
- lat2 = lat1;
- }
-
- return lineCrossesSphere(lat1, lon1, 0,
- lat2, lon2, 0,
- centerLat, centerLon, 0,
- radiusMeters);
- }
-
- /**
- * Computes whether or a 3dimensional line segment intersects or crosses a sphere
- *
- * @param lon1 longitudinal location of the line segment start point (in degrees)
- * @param lat1 latitudinal location of the line segment start point (in degrees)
- * @param alt1 altitude of the line segment start point (in degrees)
- * @param lon2 longitudinal location of the line segment end point (in degrees)
- * @param lat2 latitudinal location of the line segment end point (in degrees)
- * @param alt2 altitude of the line segment end point (in degrees)
- * @param centerLon longitudinal location of center search point (in degrees)
- * @param centerLat latitudinal location of center search point (in degrees)
- * @param centerAlt altitude of the center point (in meters)
- * @param radiusMeters search sphere radius (in meters)
- * @return whether the provided line segment is a secant of the
- */
- private static boolean lineCrossesSphere(double lat1, double lon1, double alt1,
- double lat2, double lon2, double alt2,
- double centerLat, double centerLon, double centerAlt,
- double radiusMeters) {
- // convert to cartesian 3d (in meters)
- double[] ecf1 = GeoProjectionUtils.llaToECF(lat1, lon1, alt1, null);
- double[] ecf2 = GeoProjectionUtils.llaToECF(lat2, lon2, alt2, null);
- double[] cntr = GeoProjectionUtils.llaToECF(centerLat, centerLon, centerAlt, null);
-
- // convert radius from arc radius to cartesian radius
- double[] oneEighty = GeoProjectionUtils.pointFromLonLatBearingGreatCircle(centerLat, centerLon, 180.0d, radiusMeters, new double[3]);
- GeoProjectionUtils.llaToECF(oneEighty[1], oneEighty[0], 0, oneEighty);
-
- radiusMeters = GeoDistanceUtils.linearDistance(oneEighty, cntr);// Math.sqrt(oneEighty[0]*cntr[0] + oneEighty[1]*cntr[1] + oneEighty[2]*cntr[2]);
-
- final double dX = ecf2[0] - ecf1[0];
- final double dY = ecf2[1] - ecf1[1];
- final double dZ = ecf2[2] - ecf1[2];
- final double fX = ecf1[0] - cntr[0];
- final double fY = ecf1[1] - cntr[1];
- final double fZ = ecf1[2] - cntr[2];
-
- final double a = dX*dX + dY*dY + dZ*dZ;
- final double b = 2 * (fX*dX + fY*dY + fZ*dZ);
- final double c = (fX*fX + fY*fY + fZ*fZ) - (radiusMeters*radiusMeters);
-
- double discrim = (b*b)-(4*a*c);
- if (discrim < 0) {
- return false;
- }
-
- discrim = StrictMath.sqrt(discrim);
- final double a2 = 2*a;
- final double t1 = (-b - discrim)/a2;
- final double t2 = (-b + discrim)/a2;
-
- if ( (t1 < 0 || t1 > 1) ) {
- return !(t2 < 0 || t2 > 1);
- }
-
- return true;
- }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
index 935da60..a07ea72 100644
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/GeoUtils.java
@@ -16,10 +16,6 @@
*/
package org.apache.lucene.spatial.util;
-import java.util.ArrayList;
-
-import org.apache.lucene.util.SloppyMath;
-
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.lang.Math.PI;
@@ -30,11 +26,6 @@ import static org.apache.lucene.util.SloppyMath.cos;
import static org.apache.lucene.util.SloppyMath.TO_DEGREES;
import static org.apache.lucene.util.SloppyMath.TO_RADIANS;
import static org.apache.lucene.spatial.util.GeoEncodingUtils.TOLERANCE;
-import static org.apache.lucene.spatial.util.GeoProjectionUtils.MAX_LAT_RADIANS;
-import static org.apache.lucene.spatial.util.GeoProjectionUtils.MAX_LON_RADIANS;
-import static org.apache.lucene.spatial.util.GeoProjectionUtils.MIN_LAT_RADIANS;
-import static org.apache.lucene.spatial.util.GeoProjectionUtils.MIN_LON_RADIANS;
-import static org.apache.lucene.spatial.util.GeoProjectionUtils.SEMIMAJOR_AXIS;
/**
* Basic reusable geo-spatial utility methods
@@ -53,6 +44,19 @@ public final class GeoUtils {
/** Maximum latitude value. */
public static final double MAX_LAT_INCL = 90.0D;
+
+ /** min longitude value in radians */
+ public static final double MIN_LON_RADIANS = TO_RADIANS * MIN_LON_INCL;
+ /** min latitude value in radians */
+ public static final double MIN_LAT_RADIANS = TO_RADIANS * MIN_LAT_INCL;
+ /** max longitude value in radians */
+ public static final double MAX_LON_RADIANS = TO_RADIANS * MAX_LON_INCL;
+ /** max latitude value in radians */
+ public static final double MAX_LAT_RADIANS = TO_RADIANS * MAX_LAT_INCL;
+
+ // WGS84 earth-ellipsoid parameters
+ /** major (a) axis in meters */
+ public static final double SEMIMAJOR_AXIS = 6_378_137; // [m]
// No instance:
private GeoUtils() {
@@ -153,7 +157,7 @@ public final class GeoUtils {
// some sloppyish stuff, do we really need this to be done in a sloppy way?
// unless it is performance sensitive, we should try to remove.
- static final double PIO2 = Math.PI / 2D;
+ private static final double PIO2 = Math.PI / 2D;
/**
* Returns the trigonometric sine of an angle converted as a cos operation.
@@ -169,26 +173,8 @@ public final class GeoUtils {
* @see Math#sin(double)
*/
// TODO: deprecate/remove this? at least its no longer public.
- static double sloppySin(double a) {
+ private static double sloppySin(double a) {
return cos(a - PIO2);
}
-
- /**
- * Returns the trigonometric tangent of an angle converted in terms of a sin/cos operation.
- * <p>
- * Note that this is probably not quite right (untested)
- * <p>
- * Special cases:
- * <ul>
- * <li>If the argument is {@code NaN} or an infinity, then the result is {@code NaN}.
- * </ul>
- * @param a an angle, in radians.
- * @return the tangent of the argument.
- * @see Math#sin(double) aand Math#cos(double)
- */
- // TODO: deprecate/remove this? at least its no longer public.
- static double sloppyTan(double a) {
- return sloppySin(a) / cos(a);
- }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointQuery.java b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointQuery.java
index 53ff25c..747cd1f 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointQuery.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointQuery.java
@@ -22,23 +22,30 @@ import org.apache.lucene.spatial.util.GeoEncodingUtils;
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
+import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-import org.apache.lucene.util.SloppyMath;
-
-import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
/**
* random testing for GeoPoint query logic
*
* @lucene.experimental
*/
-
public class TestGeoPointQuery extends BaseGeoPointTestCase {
@Override
- protected boolean forceSmall() {
- return false;
+ protected double maxRadius(double latitude, double longitude) {
+ // TODO: clean this up
+ return GeoDistanceUtils.maxRadialDistanceMeters(latitude, longitude);
+ }
+
+ @Override
+ protected double quantizeLat(double lat) {
+ return GeoEncodingUtils.mortonUnhashLat(GeoEncodingUtils.mortonHash(lat, 0));
+ }
+
+ @Override
+ protected double quantizeLon(double lon) {
+ return GeoEncodingUtils.mortonUnhashLon(GeoEncodingUtils.mortonHash(0, lon));
}
@Override
@@ -58,7 +65,9 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
@Override
protected Query newDistanceRangeQuery(String field, double centerLat, double centerLon, double minRadiusMeters, double radiusMeters) {
- return new GeoPointDistanceRangeQuery(field, TermEncoding.PREFIX, centerLat, centerLon, minRadiusMeters, radiusMeters);
+ // LUCENE-7126: currently not valid for multi-valued documents, because it rewrites to a BooleanQuery!
+ // return new GeoPointDistanceRangeQuery(field, TermEncoding.PREFIX, centerLat, centerLon, minRadiusMeters, radiusMeters);
+ return null;
}
@Override
@@ -66,63 +75,4 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase {
return new GeoPointInPolygonQuery(field, TermEncoding.PREFIX, lats, lons);
}
- @Override
- protected Boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- if (GeoEncodingUtils.compare(pointLon, rect.minLon) == 0.0 ||
- GeoEncodingUtils.compare(pointLon, rect.maxLon) == 0.0 ||
- GeoEncodingUtils.compare(pointLat, rect.minLat) == 0.0 ||
- GeoEncodingUtils.compare(pointLat, rect.maxLat) == 0.0) {
- // Point is very close to rect boundary
- return null;
- }
-
- if (rect.minLon < rect.maxLon) {
- return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
- } else {
- // Rect crosses dateline:
- return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, -180.0, rect.maxLon)
- || GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, 180.0);
- }
- }
-
- @Override
- protected Boolean polyRectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- return rectContainsPoint(rect, pointLat, pointLon);
- }
-
- @Override
- protected Boolean circleContainsPoint(double centerLat, double centerLon, double radiusMeters, double pointLat, double pointLon) {
- if (radiusQueryCanBeWrong(centerLat, centerLon, pointLon, pointLat, radiusMeters)) {
- return null;
- } else {
- return SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon) <= radiusMeters;
- }
- }
-
- @Override
- protected Boolean distanceRangeContainsPoint(double centerLat, double centerLon, double minRadiusMeters, double radiusMeters, double pointLat, double pointLon) {
- if (radiusQueryCanBeWrong(centerLat, centerLon, pointLon, pointLat, minRadiusMeters)
- || radiusQueryCanBeWrong(centerLat, centerLon, pointLon, pointLat, radiusMeters)) {
- return null;
- } else {
- final double d = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
- return d >= minRadiusMeters && d <= radiusMeters;
- }
- }
-
- private static boolean radiusQueryCanBeWrong(double centerLat, double centerLon, double ptLon, double ptLat,
- final double radius) {
- final long hashedCntr = GeoEncodingUtils.mortonHash(centerLat, centerLon);
- centerLon = GeoEncodingUtils.mortonUnhashLon(hashedCntr);
- centerLat = GeoEncodingUtils.mortonUnhashLat(hashedCntr);
- final long hashedPt = GeoEncodingUtils.mortonHash(ptLat, ptLon);
- ptLon = GeoEncodingUtils.mortonUnhashLon(hashedPt);
- ptLat = GeoEncodingUtils.mortonUnhashLat(hashedPt);
-
- double ptDistance = SloppyMath.haversinMeters(centerLat, centerLon, ptLat, ptLon);
- double delta = StrictMath.abs(ptDistance - radius);
-
- // if its within the distance error then it can be wrong
- return delta < (ptDistance*DISTANCE_PCT_ERR);
- }
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointQuery.java b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointQuery.java
index 90bfb10..73b3082 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointQuery.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointQuery.java
@@ -22,11 +22,8 @@ import org.apache.lucene.spatial.util.GeoEncodingUtils;
import org.apache.lucene.spatial.geopoint.document.GeoPointField;
import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
import org.apache.lucene.spatial.util.BaseGeoPointTestCase;
+import org.apache.lucene.spatial.util.GeoDistanceUtils;
import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-import org.apache.lucene.util.SloppyMath;
-
-import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
/**
* random testing for GeoPoint query logic (with deprecated numeric encoding)
@@ -36,8 +33,19 @@ import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
public class TestLegacyGeoPointQuery extends BaseGeoPointTestCase {
@Override
- protected boolean forceSmall() {
- return false;
+ protected double maxRadius(double latitude, double longitude) {
+ // TODO: clean this up
+ return GeoDistanceUtils.maxRadialDistanceMeters(latitude, longitude);
+ }
+
+ @Override
+ protected double quantizeLat(double lat) {
+ return GeoEncodingUtils.mortonUnhashLat(GeoEncodingUtils.mortonHash(lat, 0));
+ }
+
+ @Override
+ protected double quantizeLon(double lon) {
+ return GeoEncodingUtils.mortonUnhashLon(GeoEncodingUtils.mortonHash(0, lon));
}
@Override
@@ -57,7 +65,9 @@ public class TestLegacyGeoPointQuery extends BaseGeoPointTestCase {
@Override
protected Query newDistanceRangeQuery(String field, double centerLat, double centerLon, double minRadiusMeters, double radiusMeters) {
- return new GeoPointDistanceRangeQuery(field, TermEncoding.NUMERIC, centerLat, centerLon, minRadiusMeters, radiusMeters);
+ // LUCENE-7126: currently not valid for multi-valued documents, because it rewrites to a BooleanQuery!
+ // return new GeoPointDistanceRangeQuery(field, TermEncoding.NUMERIC, centerLat, centerLon, minRadiusMeters, radiusMeters);
+ return null;
}
@Override
@@ -65,63 +75,14 @@ public class TestLegacyGeoPointQuery extends BaseGeoPointTestCase {
return new GeoPointInPolygonQuery(field, TermEncoding.NUMERIC, lats, lons);
}
+ // legacy encoding is too slow somehow for this random test, its not up to the task.
@Override
- protected Boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- if (GeoEncodingUtils.compare(pointLon, rect.minLon) == 0.0 ||
- GeoEncodingUtils.compare(pointLon, rect.maxLon) == 0.0 ||
- GeoEncodingUtils.compare(pointLat, rect.minLat) == 0.0 ||
- GeoEncodingUtils.compare(pointLat, rect.maxLat) == 0.0) {
- // Point is very close to rect boundary
- return null;
- }
-
- if (rect.minLon < rect.maxLon) {
- return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
- } else {
- // Rect crosses dateline:
- return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, -180.0, rect.maxLon)
- || GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, 180.0);
- }
- }
-
- @Override
- protected Boolean polyRectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
- return rectContainsPoint(rect, pointLat, pointLon);
- }
-
- @Override
- protected Boolean circleContainsPoint(double centerLat, double centerLon, double radiusMeters, double pointLat, double pointLon) {
- if (radiusQueryCanBeWrong(centerLat, centerLon, pointLon, pointLat, radiusMeters)) {
- return null;
- } else {
- return SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon) <= radiusMeters;
- }
+ public void testRandomDistance() throws Exception {
+ assumeTrue("legacy encoding is too slow/hangs on this test", false);
}
@Override
- protected Boolean distanceRangeContainsPoint(double centerLat, double centerLon, double minRadiusMeters, double radiusMeters, double pointLat, double pointLon) {
- if (radiusQueryCanBeWrong(centerLat, centerLon, pointLon, pointLat, minRadiusMeters)
- || radiusQueryCanBeWrong(centerLat, centerLon, pointLon, pointLat, radiusMeters)) {
- return null;
- } else {
- final double d = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
- return d >= minRadiusMeters && d <= radiusMeters;
- }
- }
-
- private static boolean radiusQueryCanBeWrong(double centerLat, double centerLon, double ptLon, double ptLat,
- final double radius) {
- final long hashedCntr = GeoEncodingUtils.mortonHash(centerLat, centerLon);
- centerLon = GeoEncodingUtils.mortonUnhashLon(hashedCntr);
- centerLat = GeoEncodingUtils.mortonUnhashLat(hashedCntr);
- final long hashedPt = GeoEncodingUtils.mortonHash(ptLat, ptLon);
- ptLon = GeoEncodingUtils.mortonUnhashLon(hashedPt);
- ptLat = GeoEncodingUtils.mortonUnhashLat(hashedPt);
-
- double ptDistance = SloppyMath.haversinMeters(centerLat, centerLon, ptLat, ptLon);
- double delta = StrictMath.abs(ptDistance - radius);
-
- // if its within the distance error then it can be wrong
- return delta < (ptDistance*DISTANCE_PCT_ERR);
+ public void testRandomDistanceHuge() throws Exception {
+ assumeTrue("legacy encoding is too slow/hangs on this test", false);
}
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/ee1aca86/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java b/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
index a4f70d9..6f3a73c 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/util/BaseGeoPointTestCase.java
@@ -21,6 +21,7 @@ import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -28,9 +29,16 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
+import org.apache.lucene.codecs.FilterCodec;
+import org.apache.lucene.codecs.PointsFormat;
+import org.apache.lucene.codecs.PointsReader;
+import org.apache.lucene.codecs.PointsWriter;
+import org.apache.lucene.codecs.lucene60.Lucene60PointsReader;
+import org.apache.lucene.codecs.lucene60.Lucene60PointsWriter;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.NumericDocValuesField;
+import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
@@ -39,16 +47,22 @@ import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.MultiDocValues;
import org.apache.lucene.index.NumericDocValues;
import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.SegmentReadState;
+import org.apache.lucene.index.SegmentWriteState;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
+import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.SimpleCollector;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.FixedBitSet;
import org.apache.lucene.util.IOUtils;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.SloppyMath;
import org.apache.lucene.util.TestUtil;
+import org.apache.lucene.util.bkd.BKDWriter;
import org.junit.BeforeClass;
// TODO: cutover TestGeoUtils too?
@@ -72,17 +86,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
originLat = GeoUtils.normalizeLat(GeoUtils.MIN_LAT_INCL + latRange + (GeoUtils.MAX_LAT_INCL - GeoUtils.MIN_LAT_INCL - 2 * latRange) * random().nextDouble());
}
- /** Return true when testing on a non-small region may be too slow (GeoPoint*Query) */
- protected boolean forceSmall() {
- return false;
- }
-
// A particularly tricky adversary for BKD tree:
public void testSamePointManyTimes() throws Exception {
-
- // For GeoPointQuery, only run this test nightly:
- assumeTrue("GeoPoint*Query is too slow otherwise", TEST_NIGHTLY || forceSmall() == false);
-
int numPoints = atLeast(1000);
boolean small = random().nextBoolean();
@@ -100,12 +105,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
}
public void testAllLatEqual() throws Exception {
-
- // For GeoPointQuery, only run this test nightly:
- assumeTrue("GeoPoint*Query is too slow otherwise", TEST_NIGHTLY || forceSmall() == false);
-
int numPoints = atLeast(10000);
- boolean small = forceSmall() || random().nextBoolean();
+ boolean small = random().nextBoolean();
double lat = randomLat(small);
double[] lats = new double[numPoints];
double[] lons = new double[numPoints];
@@ -151,12 +152,8 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
}
public void testAllLonEqual() throws Exception {
-
- // For GeoPointQuery, only run this test nightly:
- assumeTrue("GeoPoint*Query is too slow otherwise", TEST_NIGHTLY || forceSmall() == false);
-
int numPoints = atLeast(10000);
- boolean small = forceSmall() || random().nextBoolean();
+ boolean small = random().nextBoolean();
double theLon = randomLon(small);
double[] lats = new double[numPoints];
double[] lons = new double[numPoints];
@@ -204,10 +201,6 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
}
public void testMultiValued() throws Exception {
-
- // For GeoPointQuery, only run this test nightly:
- assumeTrue("GeoPoint*Query is too slow otherwise", TEST_NIGHTLY || forceSmall() == false);
-
int numPoints = atLeast(10000);
// Every doc has 2 points:
double[] lats = new double[2*numPoints];
@@ -289,19 +282,10 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
double latDoc2 = lats[2*docID+1];
double lonDoc2 = lons[2*docID+1];
- Boolean result1 = rectContainsPoint(rect, latDoc1, lonDoc1);
- if (result1 == null) {
- // borderline case: cannot test
- continue;
- }
-
- Boolean result2 = rectContainsPoint(rect, latDoc2, lonDoc2);
- if (result2 == null) {
- // borderline case: cannot test
- continue;
- }
+ boolean result1 = rectContainsPoint(rect, latDoc1, lonDoc1);
+ boolean result2 = rectContainsPoint(rect, latDoc2, lonDoc2);
- boolean expected = result1 == Boolean.TRUE || result2 == Boolean.TRUE;
+ boolean expected = result1 || result2;
if (hits.get(docID) != expected) {
String id = s.doc(docID).get("id");
@@ -447,6 +431,10 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
protected double quantizeLon(double lon) {
return lon;
}
+
+ protected double maxRadius(double latitude, double longitude) {
+ return 50000000D; // bigger than earth, shouldnt matter
+ }
protected GeoRect randomRect(boolean small, boolean canCrossDateLine) {
double lat0 = randomLat(small);
@@ -482,16 +470,44 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
protected abstract Query newPolygonQuery(String field, double[] lats, double[] lons);
- /** Returns null if it's borderline case */
- protected abstract Boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon);
+ static final boolean rectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
+ assert Double.isNaN(pointLat) == false;
- /** Returns null if it's borderline case */
- protected abstract Boolean polyRectContainsPoint(GeoRect rect, double pointLat, double pointLon);
+ if (rect.minLon < rect.maxLon) {
+ return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, rect.maxLon);
+ } else {
+ // Rect crosses dateline:
+ return GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, -180.0, rect.maxLon)
+ || GeoRelationUtils.pointInRectPrecise(pointLat, pointLon, rect.minLat, rect.maxLat, rect.minLon, 180.0);
+ }
+ }
+
+ static final boolean polyRectContainsPoint(GeoRect rect, double pointLat, double pointLon) {
+ // TODO write better random polygon tests
+
+ // note: logic must be slightly different than rectContainsPoint, to satisfy
+ // insideness for cases exactly on boundaries.
+
+ assert Double.isNaN(pointLat) == false;
+ assert rect.crossesDateline() == false;
+ double polyLats[] = new double[] { rect.minLat, rect.maxLat, rect.maxLat, rect.minLat, rect.minLat };
+ double polyLons[] = new double[] { rect.minLon, rect.minLon, rect.maxLon, rect.maxLon, rect.minLon };
+
+ // TODO: separately test this method is 100% correct, here treat it like a black box (like haversin)
+ return GeoRelationUtils.pointInPolygon(polyLats, polyLons, pointLat, pointLon);
+ }
- /** Returns null if it's borderline case */
- protected abstract Boolean circleContainsPoint(double centerLat, double centerLon, double radiusMeters, double pointLat, double pointLon);
+ static final boolean circleContainsPoint(double centerLat, double centerLon, double radiusMeters, double pointLat, double pointLon) {
+ double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
+ boolean result = distanceMeters <= radiusMeters;
+ //System.out.println(" shouldMatch? centerLon=" + centerLon + " centerLat=" + centerLat + " pointLon=" + pointLon + " pointLat=" + pointLat + " result=" + result + " distanceMeters=" + (distanceKM * 1000));
+ return result;
+ }
- protected abstract Boolean distanceRangeContainsPoint(double centerLat, double centerLon, double minRadiusMeters, double radiusMeters, double pointLat, double pointLon);
+ static final boolean distanceRangeContainsPoint(double centerLat, double centerLon, double minRadiusMeters, double radiusMeters, double pointLat, double pointLon) {
+ final double d = SloppyMath.haversinMeters(centerLat, centerLon, pointLat, pointLon);
+ return d >= minRadiusMeters && d <= radiusMeters;
+ }
private static abstract class VerifyHits {
@@ -525,7 +541,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
for(int docID=0;docID<maxDoc;docID++) {
int id = (int) docIDToID.get(docID);
- Boolean expected;
+ boolean expected;
if (deleted.contains(id)) {
expected = false;
} else if (Double.isNaN(lats[id])) {
@@ -534,8 +550,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
expected = shouldMatch(lats[id], lons[id]);
}
- // null means it's a borderline case which is allowed to be wrong:
- if (expected != null && hits.get(docID) != expected) {
+ if (hits.get(docID) != expected) {
// Print only one failed hit; add a true || in here to see all failures:
if (failFast == false || failed.getAndSet(true) == false) {
@@ -568,7 +583,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
/** Return true if we definitely should match, false if we definitely
* should not match, and null if it's a borderline case which might
* go either way. */
- protected abstract Boolean shouldMatch(double lat, double lon);
+ protected abstract boolean shouldMatch(double lat, double lon);
protected abstract void describe(int docID, double lat, double lon);
}
@@ -666,7 +681,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
verifyHits = new VerifyHits() {
@Override
- protected Boolean shouldMatch(double pointLat, double pointLon) {
+ protected boolean shouldMatch(double pointLat, double pointLon) {
return rectContainsPoint(rect, pointLat, pointLon);
}
@Override
@@ -688,7 +703,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
radiusMeters = random().nextDouble() * 333000 + 1.0;
} else {
// So the query can cover at most 50% of the earth's surface:
- radiusMeters = random().nextDouble() * GeoProjectionUtils.SEMIMAJOR_AXIS * Math.PI / 2.0 + 1.0;
+ radiusMeters = random().nextDouble() * GeoUtils.SEMIMAJOR_AXIS * Math.PI / 2.0 + 1.0;
}
// generate a random minimum radius between 1% and 95% the max radius
@@ -715,7 +730,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
verifyHits = new VerifyHits() {
@Override
- protected Boolean shouldMatch(double pointLat, double pointLon) {
+ protected boolean shouldMatch(double pointLat, double pointLon) {
if (rangeQuery == false) {
return circleContainsPoint(centerLat, centerLon, radiusMeters, pointLat, pointLon);
} else {
@@ -755,7 +770,7 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
verifyHits = new VerifyHits() {
@Override
- protected Boolean shouldMatch(double pointLat, double pointLon) {
+ protected boolean shouldMatch(double pointLat, double pointLon) {
return polyRectContainsPoint(bbox, pointLat, pointLon);
}
@@ -834,4 +849,95 @@ public abstract class BaseGeoPointTestCase extends LuceneTestCase {
w.close();
dir.close();
}
+
+ /** Run a few iterations with just 10 docs, hopefully easy to debug */
+ public void testRandomDistance() throws Exception {
+ for (int iters = 0; iters < 100; iters++) {
+ doRandomDistanceTest(10, 100);
+ }
+ }
+
+ /** Runs with thousands of docs */
+ @Nightly
+ public void testRandomDistanceHuge() throws Exception {
+ for (int iters = 0; iters < 10; iters++) {
+ doRandomDistanceTest(2000, 100);
+ }
+ }
+
+ private void doRandomDistanceTest(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 PointsFormat pointsFormat() {
+ return new PointsFormat() {
+ @Override
+ public PointsWriter fieldsWriter(SegmentWriteState writeState) throws IOException {
+ return new Lucene60PointsWriter(writeState, pointsInLeaf, BKDWriter.DEFAULT_MAX_MB_SORT_IN_HEAP);
+ }
+
+ @Override
+ public PointsReader fieldsReader(SegmentReadState readState) throws IOException {
+ return new Lucene60PointsReader(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 = quantizeLat(latRaw);
+ double lon = quantizeLon(lonRaw);
+ Document doc = new Document();
+ addPointToDoc("field", doc, lat, lon);
+ doc.add(new StoredField("lat", lat));
+ doc.add(new StoredField("lon", lon));
+ writer.addDocument(doc);
+ }
+ IndexReader reader = writer.getReader();
+ IndexSearcher searcher = newSearcher(reader);
+
+ for (int i = 0; i < numQueries; i++) {
+ double lat = -90 + 180.0 * random().nextDouble();
+ double lon = -180 + 360.0 * random().nextDouble();
+ double radius = maxRadius(lat, lon) * 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 = SloppyMath.haversinMeters(lat, lon, docLatitude, docLongitude);
+ if (distance <= radius) {
+ expected.set(doc);
+ }
+ }
+
+ TopDocs topDocs = searcher.search(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) {
+ System.out.println("center: (" + lat + "," + lon + "), radius=" + radius);
+ 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 = SloppyMath.haversinMeters(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/ee1aca86/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java b/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
index f9dde50..fe8444f 100644
--- a/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
+++ b/lucene/spatial/src/test/org/apache/lucene/spatial/util/TestGeoUtils.java
@@ -16,23 +16,11 @@
*/
package org.apache.lucene.spatial.util;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
import org.apache.lucene.util.BytesRefBuilder;
import org.apache.lucene.util.LuceneTestCase;
import org.apache.lucene.util.SloppyMath;
import org.junit.BeforeClass;
-import com.carrotsearch.randomizedtesting.generators.RandomInts;
-
-import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR;
-
/**
* Tests class for methods in GeoUtils
*
@@ -77,41 +65,6 @@ public class TestGeoUtils extends LuceneTestCase {
assertEquals(0.0, result[1], 0.0);
}
- private static class Cell {
- static int nextCellID;
-
- final Cell parent;
- final int cellID;
- final double minLon, maxLon;
- final double minLat, maxLat;
- final int splitCount;
-
- public Cell(Cell parent,
- double minLon, double minLat,
- double maxLon, double maxLat,
- int splitCount) {
- assert maxLon >= minLon;
- assert maxLat >= minLat;
- this.parent = parent;
- this.minLon = minLon;
- this.minLat = minLat;
- this.maxLon = maxLon;
- this.maxLat = maxLat;
- this.cellID = nextCellID++;
- this.splitCount = splitCount;
- }
-
- /** Returns true if the quantized point lies within this cell, inclusive on all bounds. */
- public boolean contains(double lon, double lat) {
- return lon >= minLon && lon <= maxLon && lat >= minLat && lat <= maxLat;
- }
-
- @Override
- public String toString() {
- return "cell=" + cellID + (parent == null ? "" : " parentCellID=" + parent.cellID) + " lon: " + minLon + " TO " + maxLon + ", lat: " + minLat + " TO " + maxLat + ", splits: " + splitCount;
- }
- }
-
public long scaleLon(final double val) {
return (long) ((val-GeoUtils.MIN_LON_INCL) * LON_SCALE);
}
@@ -148,263 +101,6 @@ public class TestGeoUtils extends LuceneTestCase {
return result;
}
- private void findMatches(Set<Integer> hits, PrintWriter log, Cell root,
- double centerLon, double centerLat, double radiusMeters,
- double[] docLons, double[] docLats) {
-
- if (VERBOSE) {
- log.println(" root cell: " + root);
- }
-
- List<Cell> queue = new ArrayList<>();
- queue.add(root);
-
- int recurseDepth = RandomInts.randomIntBetween(random(), 5, 15);
-
- while (queue.size() > 0) {
- Cell cell = queue.get(queue.size()-1);
- queue.remove(queue.size()-1);
- if (VERBOSE) {
- log.println(" cycle: " + cell + " queue.size()=" + queue.size());
- }
-
- if (random().nextInt(10) == 7 || cell.splitCount > recurseDepth) {
- if (VERBOSE) {
- log.println(" leaf");
- }
- // Leaf cell: brute force check all docs that fall within this cell:
- for(int docID=0;docID<docLons.length;docID++) {
- if (cell.contains(docLons[docID], docLats[docID])) {
- double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, docLats[docID], docLons[docID]);
- if (distanceMeters <= radiusMeters) {
- if (VERBOSE) {
- log.println(" check doc=" + docID + ": match!");
- }
- hits.add(docID);
- } else {
- if (VERBOSE) {
- log.println(" check doc=" + docID + ": no match");
- }
- }
- }
- }
- } else {
-
- if (GeoRelationUtils.rectWithinCircle(cell.minLat, cell.maxLat, cell.minLon, cell.maxLon, centerLat, centerLon, radiusMeters, false)) {
- // Query circle fully contains this cell, just addAll:
- if (VERBOSE) {
- log.println(" circle fully contains cell: now addAll");
- }
- for(int docID=0;docID<docLons.length;docID++) {
- if (cell.contains(docLons[docID], docLats[docID])) {
- if (VERBOSE) {
- log.println(" addAll doc=" + docID);
- }
- hits.add(docID);
- }
- }
- continue;
- } else if (GeoRelationUtils.rectWithin(root.minLat, root.maxLat, root.minLon, root.maxLon,
- cell.minLat, cell.maxLat, cell.minLon, cell.maxLon)) {
- // Fall through below to "recurse"
- if (VERBOSE) {
- log.println(" cell fully contains circle: keep splitting");
- }
- } else if (GeoRelationUtils.rectCrossesCircle(cell.minLat, cell.maxLat, cell.minLon, cell.maxLon,
- centerLat, centerLon, radiusMeters, false)) {
- // Fall through below to "recurse"
- if (VERBOSE) {
- log.println(" cell overlaps circle: keep splitting");
- }
- } else {
- if (VERBOSE) {
- log.println(" no overlap: drop this cell");
- for(int docID=0;docID<docLons.length;docID++) {
- if (cell.contains(docLons[docID], docLats[docID])) {
- if (VERBOSE) {
- log.println(" skip doc=" + docID);
- }
- }
- }
- }
- continue;
- }
-
- // Randomly split:
- if (random().nextBoolean()) {
-
- // Split on lon:
- double splitValue = cell.minLon + (cell.maxLon - cell.minLon) * random().nextDouble();
- if (VERBOSE) {
- log.println(" now split on lon=" + splitValue);
- }
- Cell cell1 = new Cell(cell,
- cell.minLon, cell.minLat,
- splitValue, cell.maxLat,
- cell.splitCount+1);
- Cell cell2 = new Cell(cell,
- splitValue, cell.minLat,
- cell.maxLon, cell.maxLat,
- cell.splitCount+1);
- if (VERBOSE) {
- log.println(" split cell1: " + cell1);
- log.println(" split cell2: " + cell2);
- }
- queue.add(cell1);
- queue.add(cell2);
- } else {
-
- // Split on lat:
- double splitValue = cell.minLat + (cell.maxLat - cell.minLat) * random().nextDouble();
- if (VERBOSE) {
- log.println(" now split on lat=" + splitValue);
- }
- Cell cell1 = new Cell(cell,
- cell.minLon, cell.minLat,
- cell.maxLon, splitValue,
- cell.splitCount+1);
- Cell cell2 = new Cell(cell,
- cell.minLon, splitValue,
- cell.maxLon, cell.maxLat,
- cell.splitCount+1);
- if (VERBOSE) {
- log.println(" split cells:\n " + cell1 + "\n " + cell2);
- }
- queue.add(cell1);
- queue.add(cell2);
- }
- }
- }
- }
-
- /** Tests consistency of GeoEncodingUtils.rectWithinCircle, .rectCrossesCircle, .rectWithin and SloppyMath.haversine distance check */
- public void testGeoRelations() throws Exception {
-
- int numDocs = atLeast(1000);
-
- boolean useSmallRanges = random().nextBoolean();
-
- if (VERBOSE) {
- System.out.println("TEST: " + numDocs + " docs useSmallRanges=" + useSmallRanges);
- }
-
- double[] docLons = new double[numDocs];
- double[] docLats = new double[numDocs];
- for(int docID=0;docID<numDocs;docID++) {
- docLons[docID] = randomLon(useSmallRanges);
- docLats[docID] = randomLat(useSmallRanges);
- if (VERBOSE) {
- System.out.println(" doc=" + docID + ": lon=" + docLons[docID] + " lat=" + docLats[docID]);
- }
- }
-
- int iters = atLeast(10);
-
- iters = atLeast(50);
-
- for(int iter=0;iter<iters;iter++) {
-
- Cell.nextCellID = 0;
-
- double centerLon = randomLon(useSmallRanges);
- double centerLat = randomLat(useSmallRanges);
-
- // So the circle covers at most 50% of the earth's surface:
-
- double radiusMeters;
-
- // TODO: large exotic rectangles created by BKD may be inaccurate up to 2 times DISTANCE_PCT_ERR.
- // restricting size until LUCENE-6994 can be addressed
- if (true || useSmallRanges) {
- // Approx 3 degrees lon at the equator:
- radiusMeters = random().nextDouble() * 333000;
- } else {
- radiusMeters = random().nextDouble() * GeoProjectionUtils.SEMIMAJOR_AXIS * Math.PI / 2.0;
- }
-
- StringWriter sw = new StringWriter();
- PrintWriter log = new PrintWriter(sw, true);
-
- if (VERBOSE) {
- log.println("\nTEST: iter=" + iter + " radiusMeters=" + radiusMeters + " centerLon=" + centerLon + " centerLat=" + centerLat);
- }
-
- GeoRect bbox = GeoUtils.circleToBBox(centerLat, centerLon, radiusMeters);
-
- Set<Integer> hits = new HashSet<>();
-
- if (bbox.maxLon < bbox.minLon) {
- // Crosses dateline
- log.println(" circle crosses dateline; first left query");
- double unwrappedLon = centerLon;
- if (unwrappedLon > bbox.maxLon) {
- // unwrap left
- unwrappedLon += -360.0D;
- }
- findMatches(hits, log,
- new Cell(null,
- -180, bbox.minLat,
- bbox.maxLon, bbox.maxLat,
- 0),
- unwrappedLon, centerLat, radiusMeters, docLons, docLats);
- log.println(" circle crosses dateline; now right query");
- if (unwrappedLon < bbox.maxLon) {
- // unwrap right
- unwrappedLon += 360.0D;
- }
- findMatches(hits, log,
- new Cell(null,
- bbox.minLon, bbox.minLat,
- 180, bbox.maxLat,
- 0),
- unwrappedLon, centerLat, radiusMeters, docLons, docLats);
- } else {
- // Start with the root cell that fully contains the shape:
- findMatches(hits, log,
- new Cell(null,
- bbox.minLon, bbox.minLat,
- bbox.maxLon, bbox.maxLat,
- 0),
- centerLon, centerLat, radiusMeters,
- docLons, docLats);
- }
-
- if (VERBOSE) {
- log.println(" " + hits.size() + " hits");
- }
-
- int failCount = 0;
-
- // Done matching, now verify:
- for(int docID=0;docID<numDocs;docID++) {
- double distanceMeters = SloppyMath.haversinMeters(centerLat, centerLon, docLats[docID], docLons[docID]);
- final Boolean expected;
- final double percentError = Math.abs(distanceMeters - radiusMeters) / distanceMeters;
- if (percentError <= DISTANCE_PCT_ERR) {
- expected = null;
- } else {
- expected = distanceMeters <= radiusMeters;
- }
-
- boolean actual = hits.contains(docID);
- if (expected != null && actual != expected) {
- if (actual) {
- log.println("doc=" + docID + " matched but should not with distance error " + percentError + " on iteration " + iter);
- } else {
- log.println("doc=" + docID + " did not match but should with distance error " + percentError + " on iteration " + iter);
- }
- log.println(" lon=" + docLons[docID] + " lat=" + docLats[docID] + " distanceMeters=" + distanceMeters + " vs radiusMeters=" + radiusMeters);
- failCount++;
- }
- }
-
- if (failCount != 0) {
- System.out.print(sw.toString());
- fail(failCount + " incorrect hits (see above)");
- }
- }
- }
-
/**
* Tests stability of {@link GeoEncodingUtils#geoCodedToPrefixCoded}
*/
@@ -613,8 +309,4 @@ public class TestGeoUtils extends LuceneTestCase {
assertFalse(rect.crossesDateline());
}
}
-
- public void testRectCrossesCircle() throws Exception {
- assertTrue(GeoRelationUtils.rectCrossesCircle(-90, 0.0, -180, 180, 0.0, 0.667, 88000.0, false));
- }
}