From ca3cad194bc9413129b1a87804c2bcc1f47ece55 Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Mon, 21 Mar 2016 23:41:51 -0400 Subject: [PATCH] Separate simple tests from random tests, separate tests for different encodings. We can simplify the random tests to be easier to debug (like sandbox latlonpoint), but first we need to ensure that no coverage is lost: otherwise we might weaken tests with quantization. --- .../geopoint/search/TestGeoPointField.java | 238 +++++++++++++++ .../geopoint/search/TestGeoPointQuery.java | 283 +----------------- .../search/TestLegacyGeoPointField.java | 240 +++++++++++++++ .../search/TestLegacyGeoPointQuery.java | 127 ++++++++ .../lucene/spatial/util/TestGeoUtils.java | 45 +++ 5 files changed, 656 insertions(+), 277 deletions(-) create mode 100644 lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointField.java create mode 100644 lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointField.java create mode 100644 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/TestGeoPointField.java b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointField.java new file mode 100644 index 00000000000..fbbf643a9fa --- /dev/null +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestGeoPointField.java @@ -0,0 +1,238 @@ +/* + * 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.geopoint.search; + +import org.apache.lucene.analysis.MockAnalyzer; +import org.apache.lucene.document.Document; +import org.apache.lucene.document.Field; +import org.apache.lucene.document.StringField; +import org.apache.lucene.index.IndexReader; +import org.apache.lucene.index.RandomIndexWriter; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.TopDocs; +import org.apache.lucene.spatial.geopoint.document.GeoPointField; +import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding; +import org.apache.lucene.spatial.util.GeoRelationUtils; +import org.apache.lucene.spatial.util.GeoUtils; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.LuceneTestCase; +import org.apache.lucene.util.TestUtil; +import org.junit.AfterClass; +import org.junit.BeforeClass; + +/** Simple tests for GeoPoint */ +public class TestGeoPointField extends LuceneTestCase { + + private static Directory directory = null; + private static IndexReader reader = null; + private static IndexSearcher searcher = null; + private static final String FIELD_NAME = "point"; + + @BeforeClass + public static void beforeClass() throws Exception { + directory = newDirectory(); + + RandomIndexWriter writer = new RandomIndexWriter(random(), directory, + newIndexWriterConfig(new MockAnalyzer(random())) + .setMaxBufferedDocs(TestUtil.nextInt(random(), 100, 1000)) + .setMergePolicy(newLogMergePolicy())); + + // this is a simple systematic test + GeoPointField[] pts = new GeoPointField[] { + new GeoPointField(FIELD_NAME, -96.774, 32.763420, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -96.7759895324707, 32.7559529921407, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -96.77701950073242, 32.77866942010977, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -96.7706036567688, 32.7756745755423, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -139.73458170890808, 27.703618681345585, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -96.4538113027811, 32.94823588839368, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -96.65084838867188, 33.06047141970814, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -96.7772, 32.778650, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -177.23537676036358, -88.56029371730983, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -26.779373834241003, 33.541429799076354, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -77.35379276106497, 26.774024500421728, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -14.796283808944777, -90.0, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -178.8538113027811, 32.94823588839368, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, 178.8538113027811, 32.94823588839368, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -73.998776, 40.720611, GeoPointField.PREFIX_TYPE_NOT_STORED), + new GeoPointField(FIELD_NAME, -179.5, -44.5, GeoPointField.PREFIX_TYPE_NOT_STORED)}; + + for (GeoPointField p : pts) { + Document doc = new Document(); + doc.add(p); + writer.addDocument(doc); + } + + // add explicit multi-valued docs + for (int i=0; i { + bboxQuery(179.0, -92.0, 181.0, -91.0, 20); + }); + } + + public void testGeoDistanceQuery() throws Exception { + TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000, 20); + assertEquals("GeoDistanceQuery failed", 2, td.totalHits); + } + + /** see https://issues.apache.org/jira/browse/LUCENE-6905 */ + public void testNonEmptyTermsEnum() throws Exception { + TopDocs td = geoDistanceQuery(-177.23537676036358, -88.56029371730983, 7757.999232959935, 20); + assertEquals("GeoDistanceQuery failed", 2, td.totalHits); + } + + public void testMultiValuedQuery() throws Exception { + TopDocs td = bboxQuery(-96.4538113027811, 32.7559529921407, -96.7706036567688, 32.7756745755423, 20); + // 3 single valued docs + 2 multi-valued docs + assertEquals("testMultiValuedQuery failed", 5, td.totalHits); + } + + public void testTooBigRadius() throws Exception { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + geoDistanceQuery(0.0, 85.0, 4000000, 20); + }); + assertTrue(expected.getMessage().contains("exceeds maxRadius")); + } + + /** + * Explicitly large + */ + public void testGeoDistanceQueryHuge() throws Exception { + TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000000, 20); + assertEquals("GeoDistanceQuery failed", 16, td.totalHits); + } + + public void testGeoDistanceQueryCrossDateline() throws Exception { + TopDocs td = geoDistanceQuery(-179.9538113027811, 32.94823588839368, 120000, 20); + assertEquals("GeoDistanceQuery failed", 3, td.totalHits); + } + + // GeoDistanceQuery should not accept invalid lat/lon as origin + public void testInvalidGeoDistanceQuery() throws Exception { + expectThrows(Exception.class, () -> { + geoDistanceQuery(181.0, 92.0, 120000, 20); + }); + } + + public void testMaxDistanceRangeQuery() throws Exception { + TopDocs td = geoDistanceRangeQuery(0.0, 0.0, 10, 20000000, 20); + assertEquals("GeoDistanceRangeQuery failed", 24, td.totalHits); + } +} 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 2fd64bd3d83..3bf22d335fb 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 @@ -16,45 +16,26 @@ */ package org.apache.lucene.spatial.geopoint.search; -import org.apache.lucene.analysis.MockAnalyzer; import org.apache.lucene.document.Document; -import org.apache.lucene.document.Field; -import org.apache.lucene.document.FieldType; -import org.apache.lucene.document.StringField; -import org.apache.lucene.index.IndexReader; -import org.apache.lucene.index.RandomIndexWriter; -import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; -import org.apache.lucene.search.TopDocs; -import org.apache.lucene.store.Directory; 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.GeoRect; import org.apache.lucene.spatial.util.GeoRelationUtils; -import org.apache.lucene.spatial.util.GeoUtils; import org.apache.lucene.util.SloppyMath; -import org.apache.lucene.util.TestUtil; -import org.junit.AfterClass; -import org.junit.BeforeClass; import static org.apache.lucene.spatial.util.GeoDistanceUtils.DISTANCE_PCT_ERR; /** - * Unit testing for basic GeoPoint query logic + * random testing for GeoPoint query logic * * @lucene.experimental */ public class TestGeoPointQuery extends BaseGeoPointTestCase { - private static Directory directory = null; - private static IndexReader reader = null; - private static IndexSearcher searcher = null; - private static TermEncoding termEncoding = null; - private static FieldType fieldType = null; - @Override protected boolean forceSmall() { return false; @@ -62,118 +43,27 @@ public class TestGeoPointQuery extends BaseGeoPointTestCase { @Override protected void addPointToDoc(String field, Document doc, double lat, double lon) { - doc.add(new GeoPointField(field, lon, lat, fieldType)); + doc.add(new GeoPointField(field, lon, lat, GeoPointField.PREFIX_TYPE_NOT_STORED)); } @Override protected Query newRectQuery(String field, GeoRect rect) { - return new GeoPointInBBoxQuery(field, termEncoding, rect.minLon, rect.minLat, rect.maxLon, rect.maxLat); + return new GeoPointInBBoxQuery(field, TermEncoding.PREFIX, rect.minLon, rect.minLat, rect.maxLon, rect.maxLat); } @Override protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) { - return new GeoPointDistanceQuery(field, termEncoding, centerLon, centerLat, radiusMeters); + return new GeoPointDistanceQuery(field, TermEncoding.PREFIX, centerLon, centerLat, radiusMeters); } @Override protected Query newDistanceRangeQuery(String field, double centerLat, double centerLon, double minRadiusMeters, double radiusMeters) { - return new GeoPointDistanceRangeQuery(field, termEncoding, centerLon, centerLat, minRadiusMeters, radiusMeters); + return new GeoPointDistanceRangeQuery(field, TermEncoding.PREFIX, centerLon, centerLat, minRadiusMeters, radiusMeters); } @Override protected Query newPolygonQuery(String field, double[] lats, double[] lons) { - return new GeoPointInPolygonQuery(field, termEncoding, lons, lats); - } - - @BeforeClass - public static void beforeClass() throws Exception { - directory = newDirectory(); - termEncoding = randomTermEncoding(); - fieldType = randomFieldType(); - - RandomIndexWriter writer = new RandomIndexWriter(random(), directory, - newIndexWriterConfig(new MockAnalyzer(random())) - .setMaxBufferedDocs(TestUtil.nextInt(random(), 100, 1000)) - .setMergePolicy(newLogMergePolicy())); - - // this is a simple systematic test - GeoPointField[] pts = new GeoPointField[] { - new GeoPointField(FIELD_NAME, -96.774, 32.763420, fieldType), - new GeoPointField(FIELD_NAME, -96.7759895324707, 32.7559529921407, fieldType), - new GeoPointField(FIELD_NAME, -96.77701950073242, 32.77866942010977, fieldType), - new GeoPointField(FIELD_NAME, -96.7706036567688, 32.7756745755423, fieldType), - new GeoPointField(FIELD_NAME, -139.73458170890808, 27.703618681345585, fieldType), - new GeoPointField(FIELD_NAME, -96.4538113027811, 32.94823588839368, fieldType), - new GeoPointField(FIELD_NAME, -96.65084838867188, 33.06047141970814, fieldType), - new GeoPointField(FIELD_NAME, -96.7772, 32.778650, fieldType), - new GeoPointField(FIELD_NAME, -177.23537676036358, -88.56029371730983, fieldType), - new GeoPointField(FIELD_NAME, -26.779373834241003, 33.541429799076354, fieldType), - new GeoPointField(FIELD_NAME, -77.35379276106497, 26.774024500421728, fieldType), - new GeoPointField(FIELD_NAME, -14.796283808944777, -90.0, fieldType), - new GeoPointField(FIELD_NAME, -178.8538113027811, 32.94823588839368, fieldType), - new GeoPointField(FIELD_NAME, 178.8538113027811, 32.94823588839368, fieldType), - new GeoPointField(FIELD_NAME, -73.998776, 40.720611, fieldType), - new GeoPointField(FIELD_NAME, -179.5, -44.5, fieldType)}; - - for (GeoPointField p : pts) { - Document doc = new Document(); - doc.add(p); - writer.addDocument(doc); - } - - // add explicit multi-valued docs - for (int i=0; i { - bboxQuery(179.0, -92.0, 181.0, -91.0, 20); - }); - } - - public void testGeoDistanceQuery() throws Exception { - TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000, 20); - assertEquals("GeoDistanceQuery failed", 2, td.totalHits); - } - - /** see https://issues.apache.org/jira/browse/LUCENE-6905 */ - public void testNonEmptyTermsEnum() throws Exception { - TopDocs td = geoDistanceQuery(-177.23537676036358, -88.56029371730983, 7757.999232959935, 20); - assertEquals("GeoDistanceQuery failed", 2, td.totalHits); - } - - public void testMultiValuedQuery() throws Exception { - TopDocs td = bboxQuery(-96.4538113027811, 32.7559529921407, -96.7706036567688, 32.7756745755423, 20); - // 3 single valued docs + 2 multi-valued docs - assertEquals("testMultiValuedQuery failed", 5, td.totalHits); - } - - public void testTooBigRadius() throws Exception { - IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { - geoDistanceQuery(0.0, 85.0, 4000000, 20); - }); - assertTrue(expected.getMessage().contains("exceeds maxRadius")); - } - - /** - * Explicitly large - */ - public void testGeoDistanceQueryHuge() throws Exception { - TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000000, 20); - assertEquals("GeoDistanceQuery failed", 16, td.totalHits); - } - - public void testGeoDistanceQueryCrossDateline() throws Exception { - TopDocs td = geoDistanceQuery(-179.9538113027811, 32.94823588839368, 120000, 20); - assertEquals("GeoDistanceQuery failed", 3, td.totalHits); - } - - // GeoDistanceQuery should not accept invalid lat/lon as origin - public void testInvalidGeoDistanceQuery() throws Exception { - expectThrows(Exception.class, () -> { - geoDistanceQuery(181.0, 92.0, 120000, 20); - }); - } - - public void testMaxDistanceRangeQuery() throws Exception { - TopDocs td = geoDistanceRangeQuery(0.0, 0.0, 10, 20000000, 20); - assertEquals("GeoDistanceRangeQuery failed", 24, td.totalHits); - } - - public void testMortonEncoding() throws Exception { - long hash = GeoEncodingUtils.mortonHash(180, 90); - assertEquals(180.0, GeoEncodingUtils.mortonUnhashLon(hash), 0); - assertEquals(90.0, GeoEncodingUtils.mortonUnhashLat(hash), 0); - } - - public void testEncodeDecode() throws Exception { - int iters = atLeast(10000); - boolean small = random().nextBoolean(); - for(int iter=0;iter { + bboxQuery(179.0, -92.0, 181.0, -91.0, 20); + }); + } + + public void testGeoDistanceQuery() throws Exception { + TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000, 20); + assertEquals("GeoDistanceQuery failed", 2, td.totalHits); + } + + /** see https://issues.apache.org/jira/browse/LUCENE-6905 */ + public void testNonEmptyTermsEnum() throws Exception { + TopDocs td = geoDistanceQuery(-177.23537676036358, -88.56029371730983, 7757.999232959935, 20); + assertEquals("GeoDistanceQuery failed", 2, td.totalHits); + } + + public void testMultiValuedQuery() throws Exception { + TopDocs td = bboxQuery(-96.4538113027811, 32.7559529921407, -96.7706036567688, 32.7756745755423, 20); + // 3 single valued docs + 2 multi-valued docs + assertEquals("testMultiValuedQuery failed", 5, td.totalHits); + } + + public void testTooBigRadius() throws Exception { + IllegalArgumentException expected = expectThrows(IllegalArgumentException.class, () -> { + geoDistanceQuery(0.0, 85.0, 4000000, 20); + }); + assertTrue(expected.getMessage().contains("exceeds maxRadius")); + } + + /** + * Explicitly large + */ + public void testGeoDistanceQueryHuge() throws Exception { + TopDocs td = geoDistanceQuery(-96.4538113027811, 32.94823588839368, 6000000, 20); + assertEquals("GeoDistanceQuery failed", 16, td.totalHits); + } + + public void testGeoDistanceQueryCrossDateline() throws Exception { + TopDocs td = geoDistanceQuery(-179.9538113027811, 32.94823588839368, 120000, 20); + assertEquals("GeoDistanceQuery failed", 3, td.totalHits); + } + + // GeoDistanceQuery should not accept invalid lat/lon as origin + public void testInvalidGeoDistanceQuery() throws Exception { + expectThrows(Exception.class, () -> { + geoDistanceQuery(181.0, 92.0, 120000, 20); + }); + } + + public void testMaxDistanceRangeQuery() throws Exception { + TopDocs td = geoDistanceRangeQuery(0.0, 0.0, 10, 20000000, 20); + assertEquals("GeoDistanceRangeQuery failed", 24, td.totalHits); + } +} 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 new file mode 100644 index 00000000000..c20faffd0b2 --- /dev/null +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/geopoint/search/TestLegacyGeoPointQuery.java @@ -0,0 +1,127 @@ +/* + * 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.geopoint.search; + +import org.apache.lucene.document.Document; +import org.apache.lucene.search.Query; +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.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) + * @deprecated remove this when TermEncoding.NUMERIC is removed + */ +@Deprecated +public class TestLegacyGeoPointQuery extends BaseGeoPointTestCase { + + @Override + protected boolean forceSmall() { + return false; + } + + @Override + protected void addPointToDoc(String field, Document doc, double lat, double lon) { + doc.add(new GeoPointField(field, lon, lat, GeoPointField.NUMERIC_TYPE_NOT_STORED)); + } + + @Override + protected Query newRectQuery(String field, GeoRect rect) { + return new GeoPointInBBoxQuery(field, TermEncoding.NUMERIC, rect.minLon, rect.minLat, rect.maxLon, rect.maxLat); + } + + @Override + protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) { + return new GeoPointDistanceQuery(field, TermEncoding.NUMERIC, centerLon, centerLat, radiusMeters); + } + + @Override + protected Query newDistanceRangeQuery(String field, double centerLat, double centerLon, double minRadiusMeters, double radiusMeters) { + return new GeoPointDistanceRangeQuery(field, TermEncoding.NUMERIC, centerLon, centerLat, minRadiusMeters, radiusMeters); + } + + @Override + protected Query newPolygonQuery(String field, double[] lats, double[] lons) { + return new GeoPointInPolygonQuery(field, TermEncoding.NUMERIC, lons, lats); + } + + @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(pointLon, pointLat, rect.minLon, rect.minLat, rect.maxLon, rect.maxLat); + } else { + // Rect crosses dateline: + return GeoRelationUtils.pointInRectPrecise(pointLon, pointLat, -180.0, rect.minLat, rect.maxLon, rect.maxLat) + || GeoRelationUtils.pointInRectPrecise(pointLon, pointLat, rect.minLon, rect.minLat, 180.0, rect.maxLat); + } + } + + @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(centerLon, centerLat); + centerLon = GeoEncodingUtils.mortonUnhashLon(hashedCntr); + centerLat = GeoEncodingUtils.mortonUnhashLat(hashedCntr); + final long hashedPt = GeoEncodingUtils.mortonHash(ptLon, ptLat); + 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); + } +} 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 5529cdf2e90..37938328725 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 @@ -564,6 +564,47 @@ public class TestGeoUtils extends LuceneTestCase { } } + public void testMortonEncoding() throws Exception { + long hash = GeoEncodingUtils.mortonHash(180, 90); + assertEquals(180.0, GeoEncodingUtils.mortonUnhashLon(hash), 0); + assertEquals(90.0, GeoEncodingUtils.mortonUnhashLat(hash), 0); + } + + public void testEncodeDecode() throws Exception { + int iters = atLeast(10000); + boolean small = random().nextBoolean(); + for(int iter=0;iter