content: /**
   * 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;
    }
  }
  