--- /dev/null
+/**
+ * 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.tier;
+
+import java.io.IOException;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FilteredDocIdSet;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.Filter;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.spatial.DistanceUtils;
+
+
+/**
+ * <p><font color="red"><b>NOTE:</b> This API is still in
+ * flux and might change in incompatible ways in the next
+ * release.</font>
+ */
+public class LatLongDistanceFilter extends DistanceFilter {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ double lat;
+ double lng;
+ String latField;
+ String lngField;
+
+ int nextOffset = 0;
+
+ /**
+ * Provide a distance filter based from a center point with a radius
+ * in miles.
+ * @param startingFilter Filter to start from
+ * @param lat
+ * @param lng
+ * @param miles
+ * @param latField
+ * @param lngField
+ */
+ public LatLongDistanceFilter(Filter startingFilter, double lat, double lng, double miles, String latField, String lngField) {
+ super(startingFilter, miles);
+ this.lat = lat;
+ this.lng = lng;
+ this.latField = latField;
+ this.lngField = lngField;
+ }
+
+ @Override
+ public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
+
+ final double[] latIndex = FieldCache.DEFAULT.getDoubles(reader, latField);
+ final double[] lngIndex = FieldCache.DEFAULT.getDoubles(reader, lngField);
+
+ final int docBase = nextDocBase;
+ nextDocBase += reader.maxDoc();
+
+ return new FilteredDocIdSet(startingFilter.getDocIdSet(reader)) {
+ @Override
+ protected boolean match(int doc) {
+ double x = latIndex[doc];
+ double y = lngIndex[doc];
+
+ // round off lat / longs if necessary
+ // x = DistanceHandler.getPrecision(x, precise);
+ // y = DistanceHandler.getPrecision(y, precise);
+
+ String ck = Double.toString(x)+","+Double.toString(y);
+ Double cachedDistance = distanceLookupCache.get(ck);
+
+ double d;
+ if (cachedDistance != null){
+ d = cachedDistance.doubleValue();
+ } else {
+ d = DistanceUtils.getDistanceMi(lat, lng, x, y);
+ distanceLookupCache.put(ck, d);
+ }
+
+ if (d < distance) {
+ // Save distances, so they can be pulled for
+ // sorting after filtering is done:
+ distances.put(doc+docBase, d);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ };
+ }
+
+ /** Returns true if <code>o</code> is equal to this. */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof LatLongDistanceFilter)) return false;
+ LatLongDistanceFilter other = (LatLongDistanceFilter) o;
+
+ if (!this.startingFilter.equals(other.startingFilter) ||
+ this.distance != other.distance ||
+ this.lat != other.lat ||
+ this.lng != other.lng ||
+ !this.latField.equals(other.latField) ||
+ !this.lngField.equals(other.lngField)) {
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns a hash code value for this object.*/
+ @Override
+ public int hashCode() {
+ int h = Double.valueOf(distance).hashCode();
+ h ^= startingFilter.hashCode();
+ h ^= Double.valueOf(lat).hashCode();
+ h ^= Double.valueOf(lng).hashCode();
+ h ^= latField.hashCode();
+ h ^= lngField.hashCode();
+ return h;
+ }
+}