You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sh...@apache.org on 2018/07/28 04:50:00 UTC
[37/50] [abbrv] lucene-solr:jira/solr-11990: LUCENE-7314: Graduate
LatLonPoint and query classes to core
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointDistanceSort.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointDistanceSort.java b/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointDistanceSort.java
new file mode 100644
index 0000000..6d825d2
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/document/TestLatLonPointDistanceSort.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.document;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.lucene.geo.GeoTestUtil;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.index.RandomIndexWriter;
+import org.apache.lucene.index.SerialMergeScheduler;
+import org.apache.lucene.search.FieldDoc;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.MatchAllDocsQuery;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+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 static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
+import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
+import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
+import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
+
+/** Simple tests for {@link LatLonDocValuesField#newDistanceSort} */
+public class TestLatLonPointDistanceSort extends LuceneTestCase {
+
+ /** Add three points and sort by distance */
+ public void testDistanceSort() throws Exception {
+ Directory dir = newDirectory();
+ RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
+
+ // add some docs
+ Document doc = new Document();
+ doc.add(new LatLonDocValuesField("location", 40.759011, -73.9844722));
+ iw.addDocument(doc);
+
+ doc = new Document();
+ doc.add(new LatLonDocValuesField("location", 40.718266, -74.007819));
+ iw.addDocument(doc);
+
+ doc = new Document();
+ doc.add(new LatLonDocValuesField("location", 40.7051157, -74.0088305));
+ iw.addDocument(doc);
+
+ IndexReader reader = iw.getReader();
+ IndexSearcher searcher = newSearcher(reader);
+ iw.close();
+
+ Sort sort = new Sort(LatLonDocValuesField.newDistanceSort("location", 40.7143528, -74.0059731));
+ TopDocs td = searcher.search(new MatchAllDocsQuery(), 3, sort);
+
+ FieldDoc d = (FieldDoc) td.scoreDocs[0];
+ assertEquals(462.1028401330431, (Double)d.fields[0], 0.0D);
+
+ d = (FieldDoc) td.scoreDocs[1];
+ assertEquals(1054.9842850974826, (Double)d.fields[0], 0.0D);
+
+ d = (FieldDoc) td.scoreDocs[2];
+ assertEquals(5285.881528419706, (Double)d.fields[0], 0.0D);
+
+ reader.close();
+ dir.close();
+ }
+
+ /** Add two points (one doc missing) and sort by distance */
+ public void testMissingLast() throws Exception {
+ Directory dir = newDirectory();
+ RandomIndexWriter iw = new RandomIndexWriter(random(), dir);
+
+ // missing
+ Document doc = new Document();
+ iw.addDocument(doc);
+
+ doc = new Document();
+ doc.add(new LatLonDocValuesField("location", 40.718266, -74.007819));
+ iw.addDocument(doc);
+
+ doc = new Document();
+ doc.add(new LatLonDocValuesField("location", 40.7051157, -74.0088305));
+ iw.addDocument(doc);
+
+ IndexReader reader = iw.getReader();
+ IndexSearcher searcher = newSearcher(reader);
+ iw.close();
+
+ Sort sort = new Sort(LatLonDocValuesField.newDistanceSort("location", 40.7143528, -74.0059731));
+ TopDocs td = searcher.search(new MatchAllDocsQuery(), 3, sort);
+
+ FieldDoc d = (FieldDoc) td.scoreDocs[0];
+ assertEquals(462.1028401330431D, (Double)d.fields[0], 0.0D);
+
+ d = (FieldDoc) td.scoreDocs[1];
+ assertEquals(1054.9842850974826, (Double)d.fields[0], 0.0D);
+
+ d = (FieldDoc) td.scoreDocs[2];
+ assertEquals(Double.POSITIVE_INFINITY, (Double)d.fields[0], 0.0D);
+
+ reader.close();
+ dir.close();
+ }
+
+ /** 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);
+ }
+ }
+
+ // result class used for testing. holds an id+distance.
+ // we sort these with Arrays.sort and compare with lucene's results
+ static class Result implements Comparable<Result> {
+ int id;
+ double distance;
+
+ Result(int id, double distance) {
+ this.id = id;
+ this.distance = distance;
+ }
+
+ @Override
+ public int compareTo(Result o) {
+ int cmp = Double.compare(distance, o.distance);
+ if (cmp == 0) {
+ return Integer.compare(id, o.id);
+ }
+ return cmp;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(distance);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + id;
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (getClass() != obj.getClass()) return false;
+ Result other = (Result) obj;
+ if (Double.doubleToLongBits(distance) != Double.doubleToLongBits(other.distance)) return false;
+ if (id != other.id) return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "Result [id=" + id + ", distance=" + distance + "]";
+ }
+ }
+
+ private void doRandomTest(int numDocs, int numQueries) throws IOException {
+ Directory dir = newDirectory();
+ IndexWriterConfig iwc = newIndexWriterConfig();
+ // else seeds may not to reproduce:
+ iwc.setMergeScheduler(new SerialMergeScheduler());
+ RandomIndexWriter writer = new RandomIndexWriter(random(), dir, iwc);
+
+ for (int i = 0; i < numDocs; i++) {
+ Document doc = new Document();
+ doc.add(new StoredField("id", i));
+ doc.add(new NumericDocValuesField("id", i));
+ if (random().nextInt(10) > 7) {
+ double latRaw = GeoTestUtil.nextLatitude();
+ double lonRaw = GeoTestUtil.nextLongitude();
+ // pre-normalize up front, so we can just use quantized value for testing and do simple exact comparisons
+ double lat = decodeLatitude(encodeLatitude(latRaw));
+ double lon = decodeLongitude(encodeLongitude(lonRaw));
+
+ doc.add(new LatLonDocValuesField("field", lat, lon));
+ doc.add(new StoredField("lat", lat));
+ doc.add(new StoredField("lon", lon));
+ } // otherwise "missing"
+ writer.addDocument(doc);
+ }
+ IndexReader reader = writer.getReader();
+ IndexSearcher searcher = newSearcher(reader);
+
+ for (int i = 0; i < numQueries; i++) {
+ double lat = GeoTestUtil.nextLatitude();
+ double lon = GeoTestUtil.nextLongitude();
+ double missingValue = Double.POSITIVE_INFINITY;
+
+ Result expected[] = new Result[reader.maxDoc()];
+
+ for (int doc = 0; doc < reader.maxDoc(); doc++) {
+ Document targetDoc = reader.document(doc);
+ final double distance;
+ if (targetDoc.getField("lat") == null) {
+ distance = missingValue; // missing
+ } else {
+ double docLatitude = targetDoc.getField("lat").numericValue().doubleValue();
+ double docLongitude = targetDoc.getField("lon").numericValue().doubleValue();
+ distance = SloppyMath.haversinMeters(lat, lon, docLatitude, docLongitude);
+ }
+ int id = targetDoc.getField("id").numericValue().intValue();
+ expected[doc] = new Result(id, distance);
+ }
+
+ Arrays.sort(expected);
+
+ // randomize the topN a bit
+ int topN = TestUtil.nextInt(random(), 1, reader.maxDoc());
+ // sort by distance, then ID
+ SortField distanceSort = LatLonDocValuesField.newDistanceSort("field", lat, lon);
+ distanceSort.setMissingValue(missingValue);
+ Sort sort = new Sort(distanceSort,
+ new SortField("id", SortField.Type.INT));
+
+ TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), topN, sort);
+ for (int resultNumber = 0; resultNumber < topN; resultNumber++) {
+ FieldDoc fieldDoc = (FieldDoc) topDocs.scoreDocs[resultNumber];
+ Result actual = new Result((Integer) fieldDoc.fields[1], (Double) fieldDoc.fields[0]);
+ assertEquals(expected[resultNumber], actual);
+ }
+
+ // get page2 with searchAfter()
+ if (topN < reader.maxDoc()) {
+ int page2 = TestUtil.nextInt(random(), 1, reader.maxDoc() - topN);
+ TopDocs topDocs2 = searcher.searchAfter(topDocs.scoreDocs[topN - 1], new MatchAllDocsQuery(), page2, sort);
+ for (int resultNumber = 0; resultNumber < page2; resultNumber++) {
+ FieldDoc fieldDoc = (FieldDoc) topDocs2.scoreDocs[resultNumber];
+ Result actual = new Result((Integer) fieldDoc.fields[1], (Double) fieldDoc.fields[0]);
+ assertEquals(expected[topN + resultNumber], actual);
+ }
+ }
+ }
+ reader.close();
+ writer.close();
+ dir.close();
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java
new file mode 100644
index 0000000..bf1b8cb
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestLatLonDocValuesQueries.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.search;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.LatLonDocValuesField;
+import org.apache.lucene.geo.BaseGeoPointTestCase;
+import org.apache.lucene.geo.GeoEncodingUtils;
+import org.apache.lucene.geo.Polygon;
+
+public class TestLatLonDocValuesQueries extends BaseGeoPointTestCase {
+
+ @Override
+ protected boolean supportsPolygons() {
+ return false;
+ }
+
+ @Override
+ protected void addPointToDoc(String field, Document doc, double lat, double lon) {
+ doc.add(new LatLonDocValuesField(field, lat, lon));
+ }
+
+ @Override
+ protected Query newRectQuery(String field, double minLat, double maxLat, double minLon, double maxLon) {
+ return LatLonDocValuesField.newSlowBoxQuery(field, minLat, maxLat, minLon, maxLon);
+ }
+
+ @Override
+ protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) {
+ return LatLonDocValuesField.newSlowDistanceQuery(field, centerLat, centerLon, radiusMeters);
+ }
+
+ @Override
+ protected Query newPolygonQuery(String field, Polygon... polygons) {
+ fail();
+ return null;
+ }
+
+ @Override
+ protected double quantizeLat(double latRaw) {
+ return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(latRaw));
+ }
+
+ @Override
+ protected double quantizeLon(double lonRaw) {
+ return GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lonRaw));
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
new file mode 100644
index 0000000..f0e9612
--- /dev/null
+++ b/lucene/core/src/test/org/apache/lucene/search/TestLatLonPointQueries.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.search;
+
+import java.io.IOException;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.LatLonPoint;
+import org.apache.lucene.geo.BaseGeoPointTestCase;
+import org.apache.lucene.geo.GeoEncodingUtils;
+import org.apache.lucene.geo.Polygon;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.util.bkd.BKDWriter;
+
+public class TestLatLonPointQueries extends BaseGeoPointTestCase {
+
+ @Override
+ protected void addPointToDoc(String field, Document doc, double lat, double lon) {
+ doc.add(new LatLonPoint(field, lat, lon));
+ }
+
+ @Override
+ protected Query newRectQuery(String field, double minLat, double maxLat, double minLon, double maxLon) {
+ return LatLonPoint.newBoxQuery(field, minLat, maxLat, minLon, maxLon);
+ }
+
+ @Override
+ protected Query newDistanceQuery(String field, double centerLat, double centerLon, double radiusMeters) {
+ return LatLonPoint.newDistanceQuery(field, centerLat, centerLon, radiusMeters);
+ }
+
+ @Override
+ protected Query newPolygonQuery(String field, Polygon... polygons) {
+ return LatLonPoint.newPolygonQuery(field, polygons);
+ }
+
+ @Override
+ protected double quantizeLat(double latRaw) {
+ return GeoEncodingUtils.decodeLatitude(GeoEncodingUtils.encodeLatitude(latRaw));
+ }
+
+ @Override
+ protected double quantizeLon(double lonRaw) {
+ return GeoEncodingUtils.decodeLongitude(GeoEncodingUtils.encodeLongitude(lonRaw));
+ }
+
+ public void testDistanceQueryWithInvertedIntersection() throws IOException {
+ final int numMatchingDocs = atLeast(10 * BKDWriter.DEFAULT_MAX_POINTS_IN_LEAF_NODE);
+
+ try (Directory dir = newDirectory()) {
+
+ try (IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
+ for (int i = 0; i < numMatchingDocs; ++i) {
+ Document doc = new Document();
+ addPointToDoc("field", doc, 18.313694, -65.227444);
+ w.addDocument(doc);
+ }
+
+ // Add a handful of docs that don't match
+ for (int i = 0; i < 11; ++i) {
+ Document doc = new Document();
+ addPointToDoc("field", doc, 10, -65.227444);
+ w.addDocument(doc);
+ }
+ w.forceMerge(1);
+ }
+
+ try (IndexReader r = DirectoryReader.open(dir)) {
+ IndexSearcher searcher = newSearcher(r);
+ assertEquals(numMatchingDocs, searcher.count(newDistanceQuery("field", 18, -65, 50_000)));
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java
deleted file mode 100644
index 31037f9..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesBoxQuery.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.document;
-
-import java.io.IOException;
-
-import org.apache.lucene.geo.GeoEncodingUtils;
-import org.apache.lucene.geo.GeoUtils;
-import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.SortedNumericDocValues;
-import org.apache.lucene.search.ConstantScoreScorer;
-import org.apache.lucene.search.ConstantScoreWeight;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreMode;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.TwoPhaseIterator;
-import org.apache.lucene.search.Weight;
-
-/** Distance query for {@link LatLonDocValuesField}. */
-final class LatLonDocValuesBoxQuery extends Query {
-
- private final String field;
- private final int minLatitude, maxLatitude, minLongitude, maxLongitude;
- private final boolean crossesDateline;
-
- LatLonDocValuesBoxQuery(String field, double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) {
- GeoUtils.checkLatitude(minLatitude);
- GeoUtils.checkLatitude(maxLatitude);
- GeoUtils.checkLongitude(minLongitude);
- GeoUtils.checkLongitude(maxLongitude);
- if (field == null) {
- throw new IllegalArgumentException("field must not be null");
- }
- this.field = field;
- this.crossesDateline = minLongitude > maxLongitude; // make sure to compute this before rounding
- this.minLatitude = GeoEncodingUtils.encodeLatitudeCeil(minLatitude);
- this.maxLatitude = GeoEncodingUtils.encodeLatitude(maxLatitude);
- this.minLongitude = GeoEncodingUtils.encodeLongitudeCeil(minLongitude);
- this.maxLongitude = GeoEncodingUtils.encodeLongitude(maxLongitude);
- }
-
- @Override
- public String toString(String field) {
- StringBuilder sb = new StringBuilder();
- if (!this.field.equals(field)) {
- sb.append(this.field);
- sb.append(':');
- }
- sb.append("box(minLat=").append(GeoEncodingUtils.decodeLatitude(minLatitude));
- sb.append(", maxLat=").append(GeoEncodingUtils.decodeLatitude(maxLatitude));
- sb.append(", minLon=").append(GeoEncodingUtils.decodeLongitude(minLongitude));
- sb.append(", maxLon=").append(GeoEncodingUtils.decodeLongitude(maxLongitude));
- return sb.append(")").toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (sameClassAs(obj) == false) {
- return false;
- }
- LatLonDocValuesBoxQuery other = (LatLonDocValuesBoxQuery) obj;
- return field.equals(other.field) &&
- crossesDateline == other.crossesDateline &&
- minLatitude == other.minLatitude &&
- maxLatitude == other.maxLatitude &&
- minLongitude == other.minLongitude &&
- maxLongitude == other.maxLongitude;
- }
-
- @Override
- public int hashCode() {
- int h = classHash();
- h = 31 * h + field.hashCode();
- h = 31 * h + Boolean.hashCode(crossesDateline);
- h = 31 * h + Integer.hashCode(minLatitude);
- h = 31 * h + Integer.hashCode(maxLatitude);
- h = 31 * h + Integer.hashCode(minLongitude);
- h = 31 * h + Integer.hashCode(maxLongitude);
- return h;
- }
-
- @Override
- public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
- return new ConstantScoreWeight(this, boost) {
- @Override
- public Scorer scorer(LeafReaderContext context) throws IOException {
- final SortedNumericDocValues values = context.reader().getSortedNumericDocValues(field);
- if (values == null) {
- return null;
- }
-
- final TwoPhaseIterator iterator = new TwoPhaseIterator(values) {
- @Override
- public boolean matches() throws IOException {
- for (int i = 0, count = values.docValueCount(); i < count; ++i) {
- final long value = values.nextValue();
- final int lat = (int) (value >>> 32);
- if (lat < minLatitude || lat > maxLatitude) {
- // not within latitude range
- continue;
- }
-
- final int lon = (int) (value & 0xFFFFFFFF);
- if (crossesDateline) {
- if (lon > maxLongitude && lon < minLongitude) {
- // not within longitude range
- continue;
- }
- } else {
- if (lon < minLongitude || lon > maxLongitude) {
- // not within longitude range
- continue;
- }
- }
-
- return true;
- }
- return false;
- }
-
- @Override
- public float matchCost() {
- return 5; // 5 comparisons
- }
- };
- return new ConstantScoreScorer(this, boost, iterator);
- }
-
- @Override
- public boolean isCacheable(LeafReaderContext ctx) {
- return DocValues.isCacheable(ctx, field);
- }
-
- };
- }
-
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java
deleted file mode 100644
index df350e6..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesDistanceQuery.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.document;
-
-import java.io.IOException;
-
-import org.apache.lucene.geo.GeoEncodingUtils;
-import org.apache.lucene.geo.GeoUtils;
-import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.SortedNumericDocValues;
-import org.apache.lucene.search.ConstantScoreScorer;
-import org.apache.lucene.search.ConstantScoreWeight;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreMode;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.TwoPhaseIterator;
-import org.apache.lucene.search.Weight;
-
-/** Distance query for {@link LatLonDocValuesField}. */
-final class LatLonDocValuesDistanceQuery extends Query {
-
- private final String field;
- private final double latitude, longitude;
- private final double radiusMeters;
-
- LatLonDocValuesDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
- if (Double.isFinite(radiusMeters) == false || radiusMeters < 0) {
- throw new IllegalArgumentException("radiusMeters: '" + radiusMeters + "' is invalid");
- }
- GeoUtils.checkLatitude(latitude);
- GeoUtils.checkLongitude(longitude);
- if (field == null) {
- throw new IllegalArgumentException("field must not be null");
- }
- this.field = field;
- this.latitude = latitude;
- this.longitude = longitude;
- this.radiusMeters = radiusMeters;
- }
-
- @Override
- public String toString(String field) {
- StringBuilder sb = new StringBuilder();
- if (!this.field.equals(field)) {
- sb.append(this.field);
- sb.append(':');
- }
- sb.append(latitude);
- sb.append(",");
- sb.append(longitude);
- sb.append(" +/- ");
- sb.append(radiusMeters);
- sb.append(" meters");
- return sb.toString();
- }
-
- @Override
- public boolean equals(Object obj) {
- if (sameClassAs(obj) == false) {
- return false;
- }
- LatLonDocValuesDistanceQuery other = (LatLonDocValuesDistanceQuery) obj;
- return field.equals(other.field) &&
- Double.doubleToLongBits(latitude) == Double.doubleToLongBits(other.latitude) &&
- Double.doubleToLongBits(longitude) == Double.doubleToLongBits(other.longitude) &&
- Double.doubleToLongBits(radiusMeters) == Double.doubleToLongBits(other.radiusMeters);
- }
-
- @Override
- public int hashCode() {
- int h = classHash();
- h = 31 * h + field.hashCode();
- h = 31 * h + Double.hashCode(latitude);
- h = 31 * h + Double.hashCode(longitude);
- h = 31 * h + Double.hashCode(radiusMeters);
- return h;
- }
-
- @Override
- public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
- return new ConstantScoreWeight(this, boost) {
-
- private final GeoEncodingUtils.DistancePredicate distancePredicate = GeoEncodingUtils.createDistancePredicate(latitude, longitude, radiusMeters);
-
- @Override
- public Scorer scorer(LeafReaderContext context) throws IOException {
- final SortedNumericDocValues values = context.reader().getSortedNumericDocValues(field);
- if (values == null) {
- return null;
- }
-
- final TwoPhaseIterator iterator = new TwoPhaseIterator(values) {
-
- @Override
- public boolean matches() throws IOException {
- for (int i = 0, count = values.docValueCount(); i < count; ++i) {
- final long value = values.nextValue();
- final int lat = (int) (value >>> 32);
- final int lon = (int) (value & 0xFFFFFFFF);
- if (distancePredicate.test(lat, lon)) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public float matchCost() {
- return 100f; // TODO: what should it be?
- }
-
- };
- return new ConstantScoreScorer(this, boost, iterator);
- }
-
- @Override
- public boolean isCacheable(LeafReaderContext ctx) {
- return DocValues.isCacheable(ctx, field);
- }
-
- };
- }
-
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesField.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesField.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesField.java
deleted file mode 100644
index 6bcc394..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonDocValuesField.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.document;
-
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
-
-import org.apache.lucene.index.DocValuesType;
-import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.search.FieldDoc;
-import org.apache.lucene.search.IndexOrDocValuesQuery;
-import org.apache.lucene.search.MatchNoDocsQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.SortField;
-
-/**
- * An per-document location field.
- * <p>
- * Sorting by distance is efficient. Multiple values for the same field in one document
- * is allowed.
- * <p>
- * This field defines static factory methods for common operations:
- * <ul>
- * <li>{@link #newDistanceSort newDistanceSort()} for ordering documents by distance from a specified location.
- * </ul>
- * <p>
- * If you also need query operations, you should add a separate {@link LatLonPoint} instance.
- * If you also need to store the value, you should add a separate {@link StoredField} instance.
- * <p>
- * <b>WARNING</b>: Values are indexed with some loss of precision from the
- * original {@code double} values (4.190951585769653E-8 for the latitude component
- * and 8.381903171539307E-8 for longitude).
- * @see LatLonPoint
- */
-public class LatLonDocValuesField extends Field {
-
- /**
- * Type for a LatLonDocValuesField
- * <p>
- * Each value stores a 64-bit long where the upper 32 bits are the encoded latitude,
- * and the lower 32 bits are the encoded longitude.
- * @see org.apache.lucene.geo.GeoEncodingUtils#decodeLatitude(int)
- * @see org.apache.lucene.geo.GeoEncodingUtils#decodeLongitude(int)
- */
- public static final FieldType TYPE = new FieldType();
- static {
- TYPE.setDocValuesType(DocValuesType.SORTED_NUMERIC);
- TYPE.freeze();
- }
-
- /**
- * Creates a new LatLonDocValuesField with the specified latitude and longitude
- * @param name field name
- * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
- * @throws IllegalArgumentException if the field name is null or latitude or longitude are out of bounds
- */
- public LatLonDocValuesField(String name, double latitude, double longitude) {
- super(name, TYPE);
- setLocationValue(latitude, longitude);
- }
-
- /**
- * Change the values of this field
- * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
- * @throws IllegalArgumentException if latitude or longitude are out of bounds
- */
- public void setLocationValue(double latitude, double longitude) {
- int latitudeEncoded = encodeLatitude(latitude);
- int longitudeEncoded = encodeLongitude(longitude);
- fieldsData = Long.valueOf((((long)latitudeEncoded) << 32) | (longitudeEncoded & 0xFFFFFFFFL));
- }
-
- /** helper: checks a fieldinfo and throws exception if its definitely not a LatLonDocValuesField */
- static void checkCompatible(FieldInfo fieldInfo) {
- // dv properties could be "unset", if you e.g. used only StoredField with this same name in the segment.
- if (fieldInfo.getDocValuesType() != DocValuesType.NONE && fieldInfo.getDocValuesType() != TYPE.docValuesType()) {
- throw new IllegalArgumentException("field=\"" + fieldInfo.name + "\" was indexed with docValuesType=" + fieldInfo.getDocValuesType() +
- " but this type has docValuesType=" + TYPE.docValuesType() +
- ", is the field really a LatLonDocValuesField?");
- }
- }
-
- @Override
- public String toString() {
- StringBuilder result = new StringBuilder();
- result.append(getClass().getSimpleName());
- result.append(" <");
- result.append(name);
- result.append(':');
-
- long currentValue = (Long)fieldsData;
- result.append(decodeLatitude((int)(currentValue >> 32)));
- result.append(',');
- result.append(decodeLongitude((int)(currentValue & 0xFFFFFFFF)));
-
- result.append('>');
- return result.toString();
- }
-
- /**
- * Creates a SortField for sorting by distance from a location.
- * <p>
- * This sort orders documents by ascending distance from the location. The value returned in {@link FieldDoc} for
- * the hits contains a Double instance with the distance in meters.
- * <p>
- * If a document is missing the field, then by default it is treated as having {@link Double#POSITIVE_INFINITY} distance
- * (missing values sort last).
- * <p>
- * If a document contains multiple values for the field, the <i>closest</i> distance to the location is used.
- *
- * @param field field name. must not be null.
- * @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
- * @return SortField ordering documents by distance
- * @throws IllegalArgumentException if {@code field} is null or location has invalid coordinates.
- */
- public static SortField newDistanceSort(String field, double latitude, double longitude) {
- return new LatLonPointSortField(field, latitude, longitude);
- }
-
- /**
- * Create a query for matching a bounding box using doc values.
- * This query is usually slow as it does not use an index structure and needs
- * to verify documents one-by-one in order to know whether they match. It is
- * best used wrapped in an {@link IndexOrDocValuesQuery} alongside a
- * {@link LatLonPoint#newBoxQuery}.
- */
- public static Query newSlowBoxQuery(String field, double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) {
- // exact double values of lat=90.0D and lon=180.0D must be treated special as they are not represented in the encoding
- // and should not drag in extra bogus junk! TODO: should encodeCeil just throw ArithmeticException to be less trappy here?
- if (minLatitude == 90.0) {
- // range cannot match as 90.0 can never exist
- return new MatchNoDocsQuery("LatLonDocValuesField.newBoxQuery with minLatitude=90.0");
- }
- if (minLongitude == 180.0) {
- if (maxLongitude == 180.0) {
- // range cannot match as 180.0 can never exist
- return new MatchNoDocsQuery("LatLonDocValuesField.newBoxQuery with minLongitude=maxLongitude=180.0");
- } else if (maxLongitude < minLongitude) {
- // encodeCeil() with dateline wrapping!
- minLongitude = -180.0;
- }
- }
- return new LatLonDocValuesBoxQuery(field, minLatitude, maxLatitude, minLongitude, maxLongitude);
- }
-
- /**
- * Create a query for matching points within the specified distance of the supplied location.
- * This query is usually slow as it does not use an index structure and needs
- * to verify documents one-by-one in order to know whether they match. It is
- * best used wrapped in an {@link IndexOrDocValuesQuery} alongside a
- * {@link LatLonPoint#newDistanceQuery}.
- * @param field field name. must not be null.
- * @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
- * @param radiusMeters maximum distance from the center in meters: must be non-negative and finite.
- * @return query matching points within this distance
- * @throws IllegalArgumentException if {@code field} is null, location has invalid coordinates, or radius is invalid.
- */
- public static Query newSlowDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
- return new LatLonDocValuesDistanceQuery(field, latitude, longitude, radiusMeters);
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
deleted file mode 100644
index 7b45c6b..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPoint.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.document;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.apache.lucene.codecs.lucene60.Lucene60PointsFormat;
-import org.apache.lucene.geo.GeoUtils;
-import org.apache.lucene.geo.Polygon;
-import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.PointValues;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.ConstantScoreQuery;
-import org.apache.lucene.search.FieldDoc;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.search.MatchNoDocsQuery;
-import org.apache.lucene.search.PointRangeQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.TopFieldDocs;
-import org.apache.lucene.util.Bits;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.lucene.util.bkd.BKDReader;
-
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitudeCeil;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitudeCeil;
-
-/**
- * An indexed location field.
- * <p>
- * Finding all documents within a range at search time is
- * efficient. Multiple values for the same field in one document
- * is allowed.
- * <p>
- * This field defines static factory methods for common operations:
- * <ul>
- * <li>{@link #newBoxQuery newBoxQuery()} for matching points within a bounding box.
- * <li>{@link #newDistanceQuery newDistanceQuery()} for matching points within a specified distance.
- * <li>{@link #newPolygonQuery newPolygonQuery()} for matching points within an arbitrary polygon.
- * <li>{@link #nearest nearest()} for finding the k-nearest neighbors by distance.
- * </ul>
- * <p>
- * If you also need per-document operations such as sort by distance, add a separate {@link LatLonDocValuesField} instance.
- * If you also need to store the value, you should add a separate {@link StoredField} instance.
- * <p>
- * <b>WARNING</b>: Values are indexed with some loss of precision from the
- * original {@code double} values (4.190951585769653E-8 for the latitude component
- * and 8.381903171539307E-8 for longitude).
- * @see PointValues
- * @see LatLonDocValuesField
- */
-// TODO ^^^ that is very sandy and hurts the API, usage, and tests tremendously, because what the user passes
-// to the field is not actually what gets indexed. Float would be 1E-5 error vs 1E-7, but it might be
-// a better tradeoff? then it would be completely transparent to the user and lucene would be "lossless".
-public class LatLonPoint extends Field {
- /** LatLonPoint is encoded as integer values so number of bytes is 4 */
- public static final int BYTES = Integer.BYTES;
- /**
- * Type for an indexed LatLonPoint
- * <p>
- * Each point stores two dimensions with 4 bytes per dimension.
- */
- public static final FieldType TYPE = new FieldType();
- static {
- TYPE.setDimensions(2, Integer.BYTES);
- TYPE.freeze();
- }
-
- /**
- * Change the values of this field
- * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
- * @throws IllegalArgumentException if latitude or longitude are out of bounds
- */
- public void setLocationValue(double latitude, double longitude) {
- final byte[] bytes;
-
- if (fieldsData == null) {
- bytes = new byte[8];
- fieldsData = new BytesRef(bytes);
- } else {
- bytes = ((BytesRef) fieldsData).bytes;
- }
-
- int latitudeEncoded = encodeLatitude(latitude);
- int longitudeEncoded = encodeLongitude(longitude);
- NumericUtils.intToSortableBytes(latitudeEncoded, bytes, 0);
- NumericUtils.intToSortableBytes(longitudeEncoded, bytes, Integer.BYTES);
- }
-
- /**
- * Creates a new LatLonPoint with the specified latitude and longitude
- * @param name field name
- * @param latitude latitude value: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude value: must be within standard +/-180 coordinate bounds.
- * @throws IllegalArgumentException if the field name is null or latitude or longitude are out of bounds
- */
- public LatLonPoint(String name, double latitude, double longitude) {
- super(name, TYPE);
- setLocationValue(latitude, longitude);
- }
-
- @Override
- public String toString() {
- StringBuilder result = new StringBuilder();
- result.append(getClass().getSimpleName());
- result.append(" <");
- result.append(name);
- result.append(':');
-
- byte bytes[] = ((BytesRef) fieldsData).bytes;
- result.append(decodeLatitude(bytes, 0));
- result.append(',');
- result.append(decodeLongitude(bytes, Integer.BYTES));
-
- result.append('>');
- return result.toString();
- }
-
- /** sugar encodes a single point as a byte array */
- private static byte[] encode(double latitude, double longitude) {
- byte[] bytes = new byte[2 * Integer.BYTES];
- NumericUtils.intToSortableBytes(encodeLatitude(latitude), bytes, 0);
- NumericUtils.intToSortableBytes(encodeLongitude(longitude), bytes, Integer.BYTES);
- return bytes;
- }
-
- /** sugar encodes a single point as a byte array, rounding values up */
- private static byte[] encodeCeil(double latitude, double longitude) {
- byte[] bytes = new byte[2 * Integer.BYTES];
- NumericUtils.intToSortableBytes(encodeLatitudeCeil(latitude), bytes, 0);
- NumericUtils.intToSortableBytes(encodeLongitudeCeil(longitude), bytes, Integer.BYTES);
- return bytes;
- }
-
- /** helper: checks a fieldinfo and throws exception if its definitely not a LatLonPoint */
- static void checkCompatible(FieldInfo fieldInfo) {
- // point/dv properties could be "unset", if you e.g. used only StoredField with this same name in the segment.
- if (fieldInfo.getPointDimensionCount() != 0 && fieldInfo.getPointDimensionCount() != TYPE.pointDimensionCount()) {
- throw new IllegalArgumentException("field=\"" + fieldInfo.name + "\" was indexed with numDims=" + fieldInfo.getPointDimensionCount() +
- " but this point type has numDims=" + TYPE.pointDimensionCount() +
- ", is the field really a LatLonPoint?");
- }
- if (fieldInfo.getPointNumBytes() != 0 && fieldInfo.getPointNumBytes() != TYPE.pointNumBytes()) {
- throw new IllegalArgumentException("field=\"" + fieldInfo.name + "\" was indexed with bytesPerDim=" + fieldInfo.getPointNumBytes() +
- " but this point type has bytesPerDim=" + TYPE.pointNumBytes() +
- ", is the field really a LatLonPoint?");
- }
- }
-
- // static methods for generating queries
-
- /**
- * Create a query for matching a bounding box.
- * <p>
- * The box may cross over the dateline.
- * @param field field name. must not be null.
- * @param minLatitude latitude lower bound: must be within standard +/-90 coordinate bounds.
- * @param maxLatitude latitude upper bound: must be within standard +/-90 coordinate bounds.
- * @param minLongitude longitude lower bound: must be within standard +/-180 coordinate bounds.
- * @param maxLongitude longitude upper bound: must be within standard +/-180 coordinate bounds.
- * @return query matching points within this box
- * @throws IllegalArgumentException if {@code field} is null, or the box has invalid coordinates.
- */
- public static Query newBoxQuery(String field, double minLatitude, double maxLatitude, double minLongitude, double maxLongitude) {
- // exact double values of lat=90.0D and lon=180.0D must be treated special as they are not represented in the encoding
- // and should not drag in extra bogus junk! TODO: should encodeCeil just throw ArithmeticException to be less trappy here?
- if (minLatitude == 90.0) {
- // range cannot match as 90.0 can never exist
- return new MatchNoDocsQuery("LatLonPoint.newBoxQuery with minLatitude=90.0");
- }
- if (minLongitude == 180.0) {
- if (maxLongitude == 180.0) {
- // range cannot match as 180.0 can never exist
- return new MatchNoDocsQuery("LatLonPoint.newBoxQuery with minLongitude=maxLongitude=180.0");
- } else if (maxLongitude < minLongitude) {
- // encodeCeil() with dateline wrapping!
- minLongitude = -180.0;
- }
- }
- byte[] lower = encodeCeil(minLatitude, minLongitude);
- byte[] upper = encode(maxLatitude, maxLongitude);
- // Crosses date line: we just rewrite into OR of two bboxes, with longitude as an open range:
- if (maxLongitude < minLongitude) {
- // Disable coord here because a multi-valued doc could match both rects and get unfairly boosted:
- BooleanQuery.Builder q = new BooleanQuery.Builder();
-
- // E.g.: maxLon = -179, minLon = 179
- byte[] leftOpen = lower.clone();
- // leave longitude open
- NumericUtils.intToSortableBytes(Integer.MIN_VALUE, leftOpen, Integer.BYTES);
- Query left = newBoxInternal(field, leftOpen, upper);
- q.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
-
- byte[] rightOpen = upper.clone();
- // leave longitude open
- NumericUtils.intToSortableBytes(Integer.MAX_VALUE, rightOpen, Integer.BYTES);
- Query right = newBoxInternal(field, lower, rightOpen);
- q.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
- return new ConstantScoreQuery(q.build());
- } else {
- return newBoxInternal(field, lower, upper);
- }
- }
-
- private static Query newBoxInternal(String field, byte[] min, byte[] max) {
- return new PointRangeQuery(field, min, max, 2) {
- @Override
- protected String toString(int dimension, byte[] value) {
- if (dimension == 0) {
- return Double.toString(decodeLatitude(value, 0));
- } else if (dimension == 1) {
- return Double.toString(decodeLongitude(value, 0));
- } else {
- throw new AssertionError();
- }
- }
- };
- }
-
- /**
- * Create a query for matching points within the specified distance of the supplied location.
- * @param field field name. must not be null.
- * @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
- * @param radiusMeters maximum distance from the center in meters: must be non-negative and finite.
- * @return query matching points within this distance
- * @throws IllegalArgumentException if {@code field} is null, location has invalid coordinates, or radius is invalid.
- */
- public static Query newDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
- return new LatLonPointDistanceQuery(field, latitude, longitude, radiusMeters);
- }
-
- /**
- * Create a query for matching one or more polygons.
- * @param field field name. must not be null.
- * @param polygons array of polygons. must not be null or empty
- * @return query matching points within this polygon
- * @throws IllegalArgumentException if {@code field} is null, {@code polygons} is null or empty
- * @see Polygon
- */
- public static Query newPolygonQuery(String field, Polygon... polygons) {
- return new LatLonPointInPolygonQuery(field, polygons);
- }
-
- /**
- * Finds the {@code n} nearest indexed points to the provided point, according to Haversine distance.
- * <p>
- * This is functionally equivalent to running {@link MatchAllDocsQuery} with a {@link LatLonDocValuesField#newDistanceSort},
- * but is far more efficient since it takes advantage of properties the indexed BKD tree. Currently this
- * only works with {@link Lucene60PointsFormat} (used by the default codec). Multi-valued fields are
- * currently not de-duplicated, so if a document had multiple instances of the specified field that
- * make it into the top n, that document will appear more than once.
- * <p>
- * Documents are ordered by ascending distance from the location. The value returned in {@link FieldDoc} for
- * the hits contains a Double instance with the distance in meters.
- *
- * @param searcher IndexSearcher to find nearest points from.
- * @param field field name. must not be null.
- * @param latitude latitude at the center: must be within standard +/-90 coordinate bounds.
- * @param longitude longitude at the center: must be within standard +/-180 coordinate bounds.
- * @param n the number of nearest neighbors to retrieve.
- * @return TopFieldDocs containing documents ordered by distance, where the field value for each {@link FieldDoc} is the distance in meters
- * @throws IllegalArgumentException if the underlying PointValues is not a {@code Lucene60PointsReader} (this is a current limitation), or
- * if {@code field} or {@code searcher} is null, or if {@code latitude}, {@code longitude} or {@code n} are out-of-bounds
- * @throws IOException if an IOException occurs while finding the points.
- */
- // TODO: what about multi-valued documents? what happens?
- public static TopFieldDocs nearest(IndexSearcher searcher, String field, double latitude, double longitude, int n) throws IOException {
- GeoUtils.checkLatitude(latitude);
- GeoUtils.checkLongitude(longitude);
- if (n < 1) {
- throw new IllegalArgumentException("n must be at least 1; got " + n);
- }
- if (field == null) {
- throw new IllegalArgumentException("field must not be null");
- }
- if (searcher == null) {
- throw new IllegalArgumentException("searcher must not be null");
- }
- List<BKDReader> readers = new ArrayList<>();
- List<Integer> docBases = new ArrayList<>();
- List<Bits> liveDocs = new ArrayList<>();
- int totalHits = 0;
- for(LeafReaderContext leaf : searcher.getIndexReader().leaves()) {
- PointValues points = leaf.reader().getPointValues(field);
- if (points != null) {
- if (points instanceof BKDReader == false) {
- throw new IllegalArgumentException("can only run on Lucene60PointsReader points implementation, but got " + points);
- }
- totalHits += points.getDocCount();
- BKDReader reader = (BKDReader) points;
- if (reader != null) {
- readers.add(reader);
- docBases.add(leaf.docBase);
- liveDocs.add(leaf.reader().getLiveDocs());
- }
- }
- }
-
- NearestNeighbor.NearestHit[] hits = NearestNeighbor.nearest(latitude, longitude, readers, liveDocs, docBases, n);
-
- // Convert to TopFieldDocs:
- ScoreDoc[] scoreDocs = new ScoreDoc[hits.length];
- for(int i=0;i<hits.length;i++) {
- NearestNeighbor.NearestHit hit = hits[i];
- scoreDocs[i] = new FieldDoc(hit.docID, 0.0f, new Object[] {Double.valueOf(hit.distanceMeters)});
- }
- return new TopFieldDocs(totalHits, scoreDocs, null);
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceComparator.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceComparator.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceComparator.java
deleted file mode 100644
index 40e8dee..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceComparator.java
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.document;
-
-import java.io.IOException;
-
-import org.apache.lucene.geo.Rectangle;
-import org.apache.lucene.index.DocValues;
-import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.SortedNumericDocValues;
-import org.apache.lucene.search.FieldComparator;
-import org.apache.lucene.search.LeafFieldComparator;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.util.ArrayUtil;
-import org.apache.lucene.util.RamUsageEstimator;
-import org.apache.lucene.util.SloppyMath;
-
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
-
-/**
- * Compares documents by distance from an origin point
- * <p>
- * When the least competitive item on the priority queue changes (setBottom), we recompute
- * a bounding box representing competitive distance to the top-N. Then in compareBottom, we can
- * quickly reject hits based on bounding box alone without computing distance for every element.
- */
-class LatLonPointDistanceComparator extends FieldComparator<Double> implements LeafFieldComparator {
- final String field;
- final double latitude;
- final double longitude;
-
- final double[] values;
- double bottom;
- double topValue;
- SortedNumericDocValues currentDocs;
-
- // current bounding box(es) for the bottom distance on the PQ.
- // these are pre-encoded with LatLonPoint's encoding and
- // used to exclude uncompetitive hits faster.
- int minLon = Integer.MIN_VALUE;
- int maxLon = Integer.MAX_VALUE;
- int minLat = Integer.MIN_VALUE;
- int maxLat = Integer.MAX_VALUE;
-
- // second set of longitude ranges to check (for cross-dateline case)
- int minLon2 = Integer.MAX_VALUE;
-
- // the number of times setBottom has been called (adversary protection)
- int setBottomCounter = 0;
-
- private long[] currentValues = new long[4];
- private int valuesDocID = -1;
-
- public LatLonPointDistanceComparator(String field, double latitude, double longitude, int numHits) {
- this.field = field;
- this.latitude = latitude;
- this.longitude = longitude;
- this.values = new double[numHits];
- }
-
- @Override
- public void setScorer(Scorer scorer) {}
-
- @Override
- public int compare(int slot1, int slot2) {
- return Double.compare(values[slot1], values[slot2]);
- }
-
- @Override
- public void setBottom(int slot) {
- bottom = values[slot];
- // make bounding box(es) to exclude non-competitive hits, but start
- // sampling if we get called way too much: don't make gobs of bounding
- // boxes if comparator hits a worst case order (e.g. backwards distance order)
- if (setBottomCounter < 1024 || (setBottomCounter & 0x3F) == 0x3F) {
- Rectangle box = Rectangle.fromPointDistance(latitude, longitude, haversin2(bottom));
- // pre-encode our box to our integer encoding, so we don't have to decode
- // to double values for uncompetitive hits. This has some cost!
- minLat = encodeLatitude(box.minLat);
- maxLat = encodeLatitude(box.maxLat);
- if (box.crossesDateline()) {
- // box1
- minLon = Integer.MIN_VALUE;
- maxLon = encodeLongitude(box.maxLon);
- // box2
- minLon2 = encodeLongitude(box.minLon);
- } else {
- minLon = encodeLongitude(box.minLon);
- maxLon = encodeLongitude(box.maxLon);
- // disable box2
- minLon2 = Integer.MAX_VALUE;
- }
- }
- setBottomCounter++;
- }
-
- @Override
- public void setTopValue(Double value) {
- topValue = value.doubleValue();
- }
-
- private void setValues() throws IOException {
- if (valuesDocID != currentDocs.docID()) {
- assert valuesDocID < currentDocs.docID(): " valuesDocID=" + valuesDocID + " vs " + currentDocs.docID();
- valuesDocID = currentDocs.docID();
- int count = currentDocs.docValueCount();
- if (count > currentValues.length) {
- currentValues = new long[ArrayUtil.oversize(count, RamUsageEstimator.NUM_BYTES_LONG)];
- }
- for(int i=0;i<count;i++) {
- currentValues[i] = currentDocs.nextValue();
- }
- }
- }
-
- @Override
- public int compareBottom(int doc) throws IOException {
- if (doc > currentDocs.docID()) {
- currentDocs.advance(doc);
- }
- if (doc < currentDocs.docID()) {
- return Double.compare(bottom, Double.POSITIVE_INFINITY);
- }
-
- setValues();
-
- int numValues = currentDocs.docValueCount();
-
- int cmp = -1;
- for (int i = 0; i < numValues; i++) {
- long encoded = currentValues[i];
-
- // test bounding box
- int latitudeBits = (int)(encoded >> 32);
- if (latitudeBits < minLat || latitudeBits > maxLat) {
- continue;
- }
- int longitudeBits = (int)(encoded & 0xFFFFFFFF);
- if ((longitudeBits < minLon || longitudeBits > maxLon) && (longitudeBits < minLon2)) {
- continue;
- }
-
- // only compute actual distance if its inside "competitive bounding box"
- double docLatitude = decodeLatitude(latitudeBits);
- double docLongitude = decodeLongitude(longitudeBits);
- cmp = Math.max(cmp, Double.compare(bottom, SloppyMath.haversinSortKey(latitude, longitude, docLatitude, docLongitude)));
- // once we compete in the PQ, no need to continue.
- if (cmp > 0) {
- return cmp;
- }
- }
- return cmp;
- }
-
- @Override
- public void copy(int slot, int doc) throws IOException {
- values[slot] = sortKey(doc);
- }
-
- @Override
- public LeafFieldComparator getLeafComparator(LeafReaderContext context) throws IOException {
- LeafReader reader = context.reader();
- FieldInfo info = reader.getFieldInfos().fieldInfo(field);
- if (info != null) {
- LatLonDocValuesField.checkCompatible(info);
- }
- currentDocs = DocValues.getSortedNumeric(reader, field);
- valuesDocID = -1;
- return this;
- }
-
- @Override
- public Double value(int slot) {
- return Double.valueOf(haversin2(values[slot]));
- }
-
- @Override
- public int compareTop(int doc) throws IOException {
- return Double.compare(topValue, haversin2(sortKey(doc)));
- }
-
- // TODO: optimize for single-valued case?
- // TODO: do all kinds of other optimizations!
- double sortKey(int doc) throws IOException {
- if (doc > currentDocs.docID()) {
- currentDocs.advance(doc);
- }
- double minValue = Double.POSITIVE_INFINITY;
- if (doc == currentDocs.docID()) {
- setValues();
- int numValues = currentDocs.docValueCount();
- for (int i = 0; i < numValues; i++) {
- long encoded = currentValues[i];
- double docLatitude = decodeLatitude((int)(encoded >> 32));
- double docLongitude = decodeLongitude((int)(encoded & 0xFFFFFFFF));
- minValue = Math.min(minValue, SloppyMath.haversinSortKey(latitude, longitude, docLatitude, docLongitude));
- }
- }
- return minValue;
- }
-
- // second half of the haversin calculation, used to convert results from haversin1 (used internally
- // for sorting) for display purposes.
- static double haversin2(double partial) {
- if (Double.isInfinite(partial)) {
- return partial;
- }
- return SloppyMath.haversinMeters(partial);
- }
-}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6ab3ff83/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java b/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java
deleted file mode 100644
index a72d458..0000000
--- a/lucene/sandbox/src/java/org/apache/lucene/document/LatLonPointDistanceQuery.java
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.lucene.document;
-
-import java.io.IOException;
-
-import org.apache.lucene.geo.GeoEncodingUtils;
-import org.apache.lucene.geo.GeoUtils;
-import org.apache.lucene.geo.Rectangle;
-import org.apache.lucene.index.FieldInfo;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.PointValues;
-import org.apache.lucene.index.PointValues.IntersectVisitor;
-import org.apache.lucene.index.PointValues.Relation;
-import org.apache.lucene.search.ConstantScoreScorer;
-import org.apache.lucene.search.ConstantScoreWeight;
-import org.apache.lucene.search.DocIdSetIterator;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreMode;
-import org.apache.lucene.search.Scorer;
-import org.apache.lucene.search.ScorerSupplier;
-import org.apache.lucene.search.Weight;
-import org.apache.lucene.util.BitSetIterator;
-import org.apache.lucene.util.DocIdSetBuilder;
-import org.apache.lucene.util.FixedBitSet;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.lucene.util.StringHelper;
-
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLatitude;
-import static org.apache.lucene.geo.GeoEncodingUtils.encodeLongitude;
-
-/**
- * Distance query for {@link LatLonPoint}.
- */
-final class LatLonPointDistanceQuery extends Query {
- final String field;
- final double latitude;
- final double longitude;
- final double radiusMeters;
-
- public LatLonPointDistanceQuery(String field, double latitude, double longitude, double radiusMeters) {
- if (field == null) {
- throw new IllegalArgumentException("field must not be null");
- }
- if (Double.isFinite(radiusMeters) == false || radiusMeters < 0) {
- throw new IllegalArgumentException("radiusMeters: '" + radiusMeters + "' is invalid");
- }
- GeoUtils.checkLatitude(latitude);
- GeoUtils.checkLongitude(longitude);
- this.field = field;
- this.latitude = latitude;
- this.longitude = longitude;
- this.radiusMeters = radiusMeters;
- }
-
- @Override
- public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost) throws IOException {
- Rectangle box = Rectangle.fromPointDistance(latitude, longitude, radiusMeters);
- // create bounding box(es) for the distance range
- // these are pre-encoded with LatLonPoint's encoding
- final byte minLat[] = new byte[Integer.BYTES];
- final byte maxLat[] = new byte[Integer.BYTES];
- final byte minLon[] = new byte[Integer.BYTES];
- final byte maxLon[] = new byte[Integer.BYTES];
- // second set of longitude ranges to check (for cross-dateline case)
- final byte minLon2[] = new byte[Integer.BYTES];
-
- NumericUtils.intToSortableBytes(encodeLatitude(box.minLat), minLat, 0);
- NumericUtils.intToSortableBytes(encodeLatitude(box.maxLat), maxLat, 0);
-
- // crosses dateline: split
- if (box.crossesDateline()) {
- // box1
- NumericUtils.intToSortableBytes(Integer.MIN_VALUE, minLon, 0);
- NumericUtils.intToSortableBytes(encodeLongitude(box.maxLon), maxLon, 0);
- // box2
- NumericUtils.intToSortableBytes(encodeLongitude(box.minLon), minLon2, 0);
- } else {
- NumericUtils.intToSortableBytes(encodeLongitude(box.minLon), minLon, 0);
- NumericUtils.intToSortableBytes(encodeLongitude(box.maxLon), maxLon, 0);
- // disable box2
- NumericUtils.intToSortableBytes(Integer.MAX_VALUE, minLon2, 0);
- }
-
- // compute exact sort key: avoid any asin() computations
- final double sortKey = GeoUtils.distanceQuerySortKey(radiusMeters);
-
- final double axisLat = Rectangle.axisLat(latitude, radiusMeters);
-
- return new ConstantScoreWeight(this, boost) {
-
- final GeoEncodingUtils.DistancePredicate distancePredicate = GeoEncodingUtils.createDistancePredicate(latitude, longitude, radiusMeters);
-
- @Override
- public Scorer scorer(LeafReaderContext context) throws IOException {
- ScorerSupplier scorerSupplier = scorerSupplier(context);
- if (scorerSupplier == null) {
- return null;
- }
- return scorerSupplier.get(Long.MAX_VALUE);
- }
-
- @Override
- public boolean isCacheable(LeafReaderContext ctx) {
- return true;
- }
-
- @Override
- public ScorerSupplier scorerSupplier(LeafReaderContext context) throws IOException {
- LeafReader reader = context.reader();
- PointValues values = reader.getPointValues(field);
- if (values == null) {
- // No docs in this segment had any points fields
- return null;
- }
- FieldInfo fieldInfo = reader.getFieldInfos().fieldInfo(field);
- if (fieldInfo == null) {
- // No docs in this segment indexed this field at all
- return null;
- }
- LatLonPoint.checkCompatible(fieldInfo);
-
- // matching docids
- DocIdSetBuilder result = new DocIdSetBuilder(reader.maxDoc(), values, field);
- final IntersectVisitor visitor = getIntersectVisitor(result);
-
- final Weight weight = this;
- return new ScorerSupplier() {
-
- long cost = -1;
-
- @Override
- public Scorer get(long leadCost) throws IOException {
- if (values.getDocCount() == reader.maxDoc()
- && values.getDocCount() == values.size()
- && cost() > reader.maxDoc() / 2) {
- // If all docs have exactly one value and the cost is greater
- // than half the leaf size then maybe we can make things faster
- // by computing the set of documents that do NOT match the range
- final FixedBitSet result = new FixedBitSet(reader.maxDoc());
- result.set(0, reader.maxDoc());
- int[] cost = new int[]{reader.maxDoc()};
- values.intersect(getInverseIntersectVisitor(result, cost));
- final DocIdSetIterator iterator = new BitSetIterator(result, cost[0]);
- return new ConstantScoreScorer(weight, score(), iterator);
- }
- values.intersect(visitor);
- return new ConstantScoreScorer(weight, score(), result.build().iterator());
- }
-
- @Override
- public long cost() {
- if (cost == -1) {
- cost = values.estimatePointCount(visitor);
- }
- assert cost >= 0;
- return cost;
- }
- };
-
- }
-
- /**
- * Create a visitor that collects documents matching the range.
- */
- private IntersectVisitor getIntersectVisitor(DocIdSetBuilder result) {
- return new IntersectVisitor() {
-
- DocIdSetBuilder.BulkAdder adder;
-
- @Override
- public void grow(int count) {
- adder = result.grow(count);
- }
-
- @Override
- public void visit(int docID) {
- adder.add(docID);
- }
-
- @Override
- public void visit(int docID, byte[] packedValue) {
- // bounding box check
- if (StringHelper.compare(Integer.BYTES, packedValue, 0, maxLat, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, packedValue, 0, minLat, 0) < 0) {
- // latitude out of bounding box range
- return;
- }
-
- if ((StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, maxLon, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon, 0) < 0)
- && StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon2, 0) < 0) {
- // longitude out of bounding box range
- return;
- }
-
- int docLatitude = NumericUtils.sortableBytesToInt(packedValue, 0);
- int docLongitude = NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES);
- if (distancePredicate.test(docLatitude, docLongitude)) {
- adder.add(docID);
- }
- }
-
- // algorithm: we create a bounding box (two bounding boxes if we cross the dateline).
- // 1. check our bounding box(es) first. if the subtree is entirely outside of those, bail.
- // 2. check if the subtree is disjoint. it may cross the bounding box but not intersect with circle
- // 3. see if the subtree is fully contained. if the subtree is enormous along the x axis, wrapping half way around the world, etc: then this can't work, just go to step 4.
- // 4. recurse naively (subtrees crossing over circle edge)
- @Override
- public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
- if (StringHelper.compare(Integer.BYTES, minPackedValue, 0, maxLat, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, maxPackedValue, 0, minLat, 0) < 0) {
- // latitude out of bounding box range
- return Relation.CELL_OUTSIDE_QUERY;
- }
-
- if ((StringHelper.compare(Integer.BYTES, minPackedValue, Integer.BYTES, maxLon, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon, 0) < 0)
- && StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon2, 0) < 0) {
- // longitude out of bounding box range
- return Relation.CELL_OUTSIDE_QUERY;
- }
-
- double latMin = decodeLatitude(minPackedValue, 0);
- double lonMin = decodeLongitude(minPackedValue, Integer.BYTES);
- double latMax = decodeLatitude(maxPackedValue, 0);
- double lonMax = decodeLongitude(maxPackedValue, Integer.BYTES);
-
- return GeoUtils.relate(latMin, latMax, lonMin, lonMax, latitude, longitude, sortKey, axisLat);
- }
- };
- }
-
- /**
- * Create a visitor that clears documents that do NOT match the range.
- */
- private IntersectVisitor getInverseIntersectVisitor(FixedBitSet result, int[] cost) {
- return new IntersectVisitor() {
-
- @Override
- public void visit(int docID) {
- result.clear(docID);
- cost[0]--;
- }
-
- @Override
- public void visit(int docID, byte[] packedValue) {
- // bounding box check
- if (StringHelper.compare(Integer.BYTES, packedValue, 0, maxLat, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, packedValue, 0, minLat, 0) < 0) {
- // latitude out of bounding box range
- result.clear(docID);
- cost[0]--;
- return;
- }
-
- if ((StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, maxLon, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon, 0) < 0)
- && StringHelper.compare(Integer.BYTES, packedValue, Integer.BYTES, minLon2, 0) < 0) {
- // longitude out of bounding box range
- result.clear(docID);
- cost[0]--;
- return;
- }
-
- int docLatitude = NumericUtils.sortableBytesToInt(packedValue, 0);
- int docLongitude = NumericUtils.sortableBytesToInt(packedValue, Integer.BYTES);
- if (!distancePredicate.test(docLatitude, docLongitude)) {
- result.clear(docID);
- cost[0]--;
- }
- }
-
- @Override
- public Relation compare(byte[] minPackedValue, byte[] maxPackedValue) {
- if (StringHelper.compare(Integer.BYTES, minPackedValue, 0, maxLat, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, maxPackedValue, 0, minLat, 0) < 0) {
- // latitude out of bounding box range
- return Relation.CELL_INSIDE_QUERY;
- }
-
- if ((StringHelper.compare(Integer.BYTES, minPackedValue, Integer.BYTES, maxLon, 0) > 0 ||
- StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon, 0) < 0)
- && StringHelper.compare(Integer.BYTES, maxPackedValue, Integer.BYTES, minLon2, 0) < 0) {
- // latitude out of bounding box range
- return Relation.CELL_INSIDE_QUERY;
- }
-
- double latMin = decodeLatitude(minPackedValue, 0);
- double lonMin = decodeLongitude(minPackedValue, Integer.BYTES);
- double latMax = decodeLatitude(maxPackedValue, 0);
- double lonMax = decodeLongitude(maxPackedValue, Integer.BYTES);
-
- Relation relation = GeoUtils.relate(latMin, latMax, lonMin, lonMax, latitude, longitude, sortKey, axisLat);
- switch (relation) {
- case CELL_INSIDE_QUERY:
- // all points match, skip this subtree
- return Relation.CELL_OUTSIDE_QUERY;
- case CELL_OUTSIDE_QUERY:
- // none of the points match, clear all documents
- return Relation.CELL_INSIDE_QUERY;
- default:
- return relation;
- }
- }
-
- };
- }
-
- };
- }
-
- public String getField() {
- return field;
- }
-
- public double getLatitude() {
- return latitude;
- }
-
- public double getLongitude() {
- return longitude;
- }
-
- public double getRadiusMeters() {
- return radiusMeters;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = classHash();
- result = prime * result + field.hashCode();
- long temp;
- temp = Double.doubleToLongBits(latitude);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(longitude);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(radiusMeters);
- result = prime * result + (int) (temp ^ (temp >>> 32));
- return result;
- }
-
- @Override
- public boolean equals(Object other) {
- return sameClassAs(other) &&
- equalsTo(getClass().cast(other));
- }
-
- private boolean equalsTo(LatLonPointDistanceQuery other) {
- return field.equals(other.field) &&
- Double.doubleToLongBits(latitude) == Double.doubleToLongBits(other.latitude) &&
- Double.doubleToLongBits(longitude) == Double.doubleToLongBits(other.longitude) &&
- Double.doubleToLongBits(radiusMeters) == Double.doubleToLongBits(other.radiusMeters);
- }
-
- @Override
- public String toString(String field) {
- StringBuilder sb = new StringBuilder();
- if (!this.field.equals(field)) {
- sb.append(this.field);
- sb.append(':');
- }
- sb.append(latitude);
- sb.append(",");
- sb.append(longitude);
- sb.append(" +/- ");
- sb.append(radiusMeters);
- sb.append(" meters");
- return sb.toString();
- }
-}