Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/LatLongLocationDataSetFactory.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/LatLongLocationDataSetFactory.java	Fri Dec 11 15:23:51 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/LatLongLocationDataSetFactory.java	Fri Dec 11 15:23:51 CET 2009
@@ -0,0 +1,88 @@
+/**
+ * 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.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.spatial.geometry.shape.Point2D;
+
+import java.io.IOException;
+
+/**
+ * Implementation of {@link LocationDataSetFactory} that builds LocationDataSet based on 2 fields representing latitude
+ * and longitude.
+ *
+ * <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 LatLongLocationDataSetFactory implements LocationDataSetFactory {
+
+  private final String latField;
+  private final String lngField;
+
+  /**
+   * Creates a new LatLongLocationDataSetFactory that will use the latitude and longitude data read from the fields
+   * with the given names
+   *
+   * @param latField Name of the latitude field
+   * @param lngField Name of the longitude field
+   */
+  public LatLongLocationDataSetFactory(String latField, String lngField) {
+    this.latField = latField;
+    this.lngField = lngField;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public LocationDataSet buildLocationDataSet(IndexReader indexReader) throws IOException {
+    return new LatLongLocationDataSet(
+        FieldCache.DEFAULT.getDoubles(indexReader, latField, FieldCache.NUMERIC_UTILS_DOUBLE_PARSER),
+        FieldCache.DEFAULT.getDoubles(indexReader, lngField, FieldCache.NUMERIC_UTILS_DOUBLE_PARSER));
+  }
+
+  // ================================================= Inner Classes =================================================
+
+  /**
+   * Implementation of LocationDataSet that uses fields that represent latitude and longitude to construct the Point
+   * for a document.
+   */
+  private class LatLongLocationDataSet implements LocationDataSet {
+
+    private double[] latIndex;
+    private double[] lngIndex;
+
+    /**
+     * Creates a new LatLongLocationDataSet which uses the values in the given latitude and longitude indexes to create
+     * Points for documents
+     *
+     * @param latIndex Array containing the latitude field values taken from the index
+     * @param lngIndex Array containing the longitude field values taken from the index
+     */
+    private LatLongLocationDataSet(double[] latIndex, double[] lngIndex) {
+      this.latIndex = latIndex;
+      this.lngIndex = lngIndex;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Point2D getPoint(int docId) {
+      return new Point2D(latIndex[docId], lngIndex[docId]);
+    }
+  }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/DistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/DistanceFilter.java	Fri Dec 11 15:22:10 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/DistanceFilter.java	Fri Dec 11 15:22:10 CET 2009
@@ -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.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * DistanceFilter is responsible for filtering out documents from an existing BitSet, based on their calculated distance
+ * from the central point.  Because the costing of calculating distances for documents is relatively high, this filter
+ * uses an existing BitSet, which will have been created another filter previously.  As such, this is technicall not
+ * a Lucene Filter.
+ * <p/>
+ * In addition to filtering out documents, the filter also holds onto the calculated distances so they can be used after
+ * the filtering process.
+ *
+ * <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 interface DistanceFilter {
+
+  /**
+   * Returns a map of calculated distances by document ids
+   *
+   * @return Map of calculated distances by document ids
+   */
+  Map<Integer, Double> getDistances();
+
+  /**
+   * Returns the calculated distance for a document with the given id
+   *
+   * @param docId ID of the document whose distance is to be returned
+   * @return Calculated distance of the document with the id
+   */
+  Double getDistance(int docId);
+
+  /**
+   * Filters the documents from the given IndexReader who have bits set in the given BitSet.
+   *
+   * @param reader IndexReader from where the documents will be read from
+   * @param bits BitSet containing bits indicating which documents should be considered to be filtered out
+   * @return BitSet with bits set representing those documents that passed the filter
+   * @throws java.io.IOException Can be thrown while reading from the IndexReader
+   */
+  BitSet bits(IndexReader reader, BitSet bits) throws IOException;
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/ThreadedDistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/ThreadedDistanceFilter.java	Fri Dec 11 15:26:24 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/ThreadedDistanceFilter.java	Fri Dec 11 15:26:24 CET 2009
@@ -0,0 +1,235 @@
+/**
+ * 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.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.GeoDistanceCalculator;
+import org.apache.lucene.spatial.geometry.shape.Point2D;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+/**
+ * Implementation of {@link DistanceFilter} that uses multiple threads to iterate in
+ * parallel, over the BitSet to be filtered.
+ * <p/>
+ * To manage the threads, an ExecutorService is used, which allows users of this class to have fine grained control over
+ * how many threads should be created, and what to do when there isn't any threads left in the pool.
+ *
+ * <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 ThreadedDistanceFilter implements DistanceFilter {
+
+  private final List<Map<Integer, Double>> distanceMaps = new ArrayList<Map<Integer, Double>>();
+  private final double lat;
+  private final double lng;
+  private final double radius;
+  private final DistanceUnits unit;
+
+  private final GeoDistanceCalculator distanceCalculator;
+  private final LocationDataSetFactory dataSetFactory;
+
+  private final ExecutorService executorService;
+  private final int threadCount;
+
+  private int nextOffset = 0;
+
+  /**
+   * Creates a new ThreadedDistanceFilter that will filter out documents that are outside of the given radius of the
+   * central point defined by the given latitude and longitude
+   *
+   * @param lat Latitude of the central point
+   * @param lng Longitude of the central point
+   * @param radius Radius that documents must be within from the central point to pass the filter
+   * @param unit Unit of distance the radius is in
+   * @param dataSetFactory LocationDataSetFactory which can be used to create LocationDataSets from an IndexReader
+   * @param distanceCalculator GeoDistanceCalculator that will be used to calculate the distances between points
+   * @param executorService ExecutorService which will manage the execution of the threads
+   * @param threadCount Number of threads that the filter should try to split its work across
+   */
+  public ThreadedDistanceFilter(
+      double lat,
+      double lng,
+      double radius,
+      DistanceUnits unit,
+      LocationDataSetFactory dataSetFactory,
+      GeoDistanceCalculator distanceCalculator,
+      ExecutorService executorService,
+      int threadCount) {
+
+    this.lat = lat;
+    this.lng = lng;
+    this.radius = radius;
+    this.unit = unit;
+    this.distanceCalculator = distanceCalculator;
+    this.dataSetFactory = dataSetFactory;
+    this.executorService = executorService;
+    this.threadCount = threadCount;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Map<Integer, Double> getDistances() {
+    Map<Integer, Double> combinedDistances = new HashMap<Integer, Double>();
+    for (Map<Integer, Double> distanceMap : distanceMaps) {
+      combinedDistances.putAll(distanceMap);
+    }
+    return combinedDistances;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Double getDistance(int docId) {
+    for (Map<Integer, Double> distanceMap : distanceMaps) {
+      Double distance = distanceMap.get(docId);
+      if (distance != null) {
+        return distance;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public BitSet bits(final IndexReader reader, final BitSet bits) throws IOException {
+    final LocationDataSet dataSet = dataSetFactory.buildLocationDataSet(reader);
+
+    int maxLength = bits.length();
+    int threadSize = maxLength / threadCount;
+    List<Callable<IterationResult>> tasks = new ArrayList<Callable<IterationResult>>();
+
+    for (int i = 0; i < threadCount; i++) {
+      final int start = i * threadSize;
+      // if the last batch of documents has been reached, then maxLength should be end
+      final int end = (i == threadCount - 1) ? maxLength : Math.min((i + 1) * threadSize, maxLength);
+      tasks.add(new Callable<IterationResult>() {
+        public IterationResult call() throws Exception {
+          return iterate(dataSet, bits, start, end, end - start, reader);
+        }
+      });
+    }
+
+    BitSet result = new BitSet(bits.cardinality());
+
+    try {
+      List<Future<IterationResult>> results = executorService.invokeAll(tasks);
+      for (Future<IterationResult> resultFuture : results) {
+        IterationResult iterationResult = resultFuture.get();
+        result.or(iterationResult.getBitSet());
+        distanceMaps.add(iterationResult.getDistanceById());
+      }
+    } catch (InterruptedException ie) {
+      throw new RuntimeException("InterruptedException thrown while executing tasks", ie);
+    } catch (ExecutionException ee) {
+      throw new RuntimeException("ExecutionException thrown while retrieving results of tasks", ee);
+    }
+
+    nextOffset += reader.maxDoc();
+
+    return result;
+  }
+
+  // ================================================ Helper Methods =================================================
+
+  /**
+   * Iterates over the set bits in the given BitSet from the given start to end range, calculating the distance of the
+   * documents and determining which are within the distance radius of the central point.
+   *
+   * @param dataSet LocationDataSet containing the document locations that can be used to calculate the distance each
+   *                document is from the central point
+   * @param originalBitSet BitSet which has bits set identifying which documents should be checked to see if their
+   *        distance falls within the radius
+   * @param start Index in the BitSet that the method will start at
+   * @param end Index in the BitSet that the method will stop at
+   * @param size Size the the resulting BitSet should be created at (most likely end - start)
+   * @param reader IndexReader for checking if the document has been deleted
+   * @return IterationResult containing all the results of the method.
+   */
+  protected IterationResult iterate(LocationDataSet dataSet, BitSet originalBitSet, int start, int end, int size, IndexReader reader) {
+    BitSet bitSet = new BitSet(size);
+
+    Map<Integer, Double> distanceById = new HashMap<Integer, Double>();
+
+    int docId = originalBitSet.nextSetBit(start);
+    while (docId != -1 && docId < end) {
+      if (reader.isDeleted(docId)) {
+        docId = originalBitSet.nextSetBit(docId + 1);
+        continue;
+      }
+
+      Point2D point = dataSet.getPoint(docId);
+      double distance = distanceCalculator.calculate(lat, lng, point.getX(), point.getY(), unit);
+      if (distance < radius) {
+        bitSet.set(docId);
+        distanceById.put(docId + nextOffset, distance);
+      }
+
+      docId = originalBitSet.nextSetBit(docId + 1);
+    }
+    return new IterationResult(bitSet, distanceById);
+  }
+
+  // ================================================= Inner Classes =================================================
+
+  /**
+   * Wrapper of the results from {@link ThreadedDistanceFilter#iterate(LocationDataSet, BitSet, int, int, int, IndexReader)}.
+   * This allows the method to operate in almost total isolation in separate threads.
+   */
+  protected class IterationResult {
+
+    private BitSet bitSet;
+    private Map<Integer, Double> distanceById;
+
+    /**
+     * Creates a new IterationResult that wraps the given BitSet
+     *
+     * @param bitSet BitSet to wrap
+     * @param distanceById Document IDs with their calculated distances
+     */
+    public IterationResult(BitSet bitSet, Map<Integer, Double> distanceById) {
+      this.bitSet = bitSet;
+      this.distanceById = distanceById;
+    }
+
+    /**
+     * Returns the wrapped BitSet
+     *
+     * @return Wrapped BitSet
+     */
+    public BitSet getBitSet() {
+      return bitSet;
+    }
+
+    /**
+     * Returns the document IDs and calculated distances contained in this result
+     *
+     * @return Document IDs and calculated distances contained in this result
+     */
+    public Map<Integer, Double> getDistanceById() {
+      return distanceById;
+    }
+  }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/NoOpDistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/NoOpDistanceFilter.java	Fri Dec 11 15:24:48 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/NoOpDistanceFilter.java	Fri Dec 11 15:24:48 CET 2009
@@ -0,0 +1,60 @@
+/**
+ * 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.distance;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Implementation of {@link DistanceFilter} that does no actual filtering.  This means that there can always be a DistanceFilter
+ * instantiated but that the actual process of filtering documents by their distance, which is a computationally expensive
+ * process, doesn't always have to occur.
+ *
+ * <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 NoOpDistanceFilter implements DistanceFilter {
+
+  private final Map<Integer, Double> distancesById = Collections.EMPTY_MAP;
+
+  /**
+   * {@inheritDoc}
+   */
+  public Map<Integer, Double> getDistances() {
+    return distancesById;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public Double getDistance(int docId) {
+    return distancesById.get(docId);
+  }
+
+  /**
+   * Executes no filtering.  Simply returns the given BitSet
+   * <p/>
+   * {@inheritDoc}
+   */
+  public BitSet bits(IndexReader reader, BitSet bits) throws IOException {
+    return bits;
+  }
+}
Index: contrib/spatial/src/test/org/apache/lucene/spatial/distance/TestThreadedDistanceFilter.java
===================================================================
--- contrib/spatial/src/test/org/apache/lucene/spatial/distance/TestThreadedDistanceFilter.java	Fri Dec 11 15:57:47 CET 2009
+++ contrib/spatial/src/test/org/apache/lucene/spatial/distance/TestThreadedDistanceFilter.java	Fri Dec 11 15:57:47 CET 2009
@@ -0,0 +1,279 @@
+/**
+ * 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.distance;
+
+import junit.framework.TestCase;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.spatial.geometry.ArcGeoDistanceCalculator;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.GeoDistanceCalculator;
+import org.apache.lucene.spatial.geometry.shape.Point2D;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.Version;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * Tests for {@link TestThreadedDistanceFilter}
+ */
+public class TestThreadedDistanceFilter extends TestCase {
+
+  private final LocationDataSet locationDataSet = new LocationDataSet() {
+
+    private List<Point2D> points = Arrays.asList(new Point2D(4.53, 30.61), new Point2D(4.51, 31.01), new Point2D(5.69, 40.89));
+
+    public Point2D getPoint(int docId) {
+      return points.get(docId);
+    }
+  };
+
+  private Directory directory;
+
+  /**
+   * Creates a RAM directory with an index containing 3 documents
+   *
+   * @throws Exception Can be thrown creating the index
+   */
+  @Override
+  protected void setUp() throws Exception {
+    directory = new RAMDirectory();
+    IndexWriter indexWriter = new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_CURRENT), IndexWriter.MaxFieldLength.UNLIMITED);
+
+    Document document = new Document();
+    document.add(new Field("id", "1", Field.Store.YES, Field.Index.ANALYZED));
+
+    Document document1 = new Document();
+    document1.add(new Field("id", "2", Field.Store.YES, Field.Index.ANALYZED));
+
+    Document document2 = new Document();
+    document2.add(new Field("id", "3", Field.Store.YES, Field.Index.ANALYZED));
+
+    indexWriter.addDocument(document);
+    indexWriter.addDocument(document1);
+    indexWriter.addDocument(document2);
+    
+    indexWriter.commit();
+    indexWriter.close();
+  }
+
+  /**
+   * Closes the directory
+   *
+   * @throws Exception Can be thrown closely the directory
+   */
+  @Override
+  protected void tearDown() throws Exception {
+    directory.close();  
+  }
+
+  public void testBits_correctArgumentPassing() throws Exception {
+    double sourceLatitude = 1.0;
+    double sourceLongitude = 2.0;
+    final double targetLatitude = 3.0;
+    final double targetLongitude = 4.0;
+
+    LocationDataSetFactory dataSetFactory = new LocationDataSetFactory() {
+
+      public LocationDataSet buildLocationDataSet(IndexReader indexReader) throws IOException {
+        return new LocationDataSet() {
+
+          private List<Point2D> points = Arrays.asList(new Point2D(targetLatitude, targetLongitude));
+
+          public Point2D getPoint(int docId) {
+            return points.get(docId);
+          }
+        };
+      }
+    };
+
+    StubGeoDistanceCalculator distanceCalculator = new StubGeoDistanceCalculator();
+    ExecutorService executorService = Executors.newFixedThreadPool(1);
+
+    ThreadedDistanceFilter filter =
+        new ThreadedDistanceFilter(sourceLatitude, sourceLongitude, 10, DistanceUnits.KILOMETERS, dataSetFactory, distanceCalculator, executorService, 1);
+
+    IndexReader indexReader = IndexReader.open(directory);
+
+    BitSet bitSet = new BitSet(1);
+    bitSet.set(0);
+    filter.bits(indexReader, bitSet);
+
+    assertEquals(sourceLatitude, distanceCalculator.getSourceLatitude(), 0);
+    assertEquals(sourceLongitude, distanceCalculator.getSourceLongitude(), 0);
+    assertEquals(targetLatitude, distanceCalculator.getTargetLatitude(), 0);
+    assertEquals(targetLongitude, distanceCalculator.getTargetLongitude(), 0);
+
+    indexReader.close();
+  }
+
+  /**
+   * Pass condition: 2 threads with the correct start and end indexes are created based on the BitSet given in the method
+   * call, and the number of threads that were requested.
+   *
+   * @throws java.io.IOException Can be thrown by the ParallelReader which is used as the IndexReader for the test
+   */
+  public void testBits() throws IOException {
+    LocationDataSetFactory dataSetFactory = new LocationDataSetFactory() {
+      public LocationDataSet buildLocationDataSet(IndexReader indexReader) throws IOException {
+        return locationDataSet;
+      }
+    };
+
+    ExecutorService executorService = new ThreadPoolExecutor(2, 6, 8, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+
+    BitSet bitSet = new BitSet(13);
+    bitSet.set(2, 6);
+    bitSet.set(8, 9);
+    bitSet.set(11);
+
+    final List<IterateCallInfo> infoList = Collections.synchronizedList(new ArrayList<IterateCallInfo>());
+    ThreadedDistanceFilter distanceFilter = new ThreadedDistanceFilter(4.52, 30.81, 30, DistanceUnits.MILES, dataSetFactory, new ArcGeoDistanceCalculator(), executorService, 2) {
+
+      @Override
+      protected IterationResult iterate(LocationDataSet dataSet, BitSet originalBitSet, int start, int end, int size, IndexReader reader) {
+        infoList.add(new IterateCallInfo(start, end, size));
+        return new IterationResult(new BitSet(), Collections.EMPTY_MAP);
+      }
+    };
+
+    IndexReader indexReader = IndexReader.open(directory);
+
+    distanceFilter.bits(indexReader, bitSet);
+
+    assertEquals(2, infoList.size());
+
+    Collections.sort(infoList, new Comparator<IterateCallInfo>() {
+      public int compare(IterateCallInfo info1, IterateCallInfo info2) {
+        return info1.getStart() - info2.getStart();
+      }
+    });
+
+    IterateCallInfo callInfo = infoList.get(0);
+    assertEquals(0, callInfo.getStart());
+    assertEquals(6, callInfo.getEnd());
+    assertEquals(6, callInfo.getSize());
+
+    callInfo = infoList.get(1);
+    assertEquals(6, callInfo.getStart());
+    assertEquals(12, callInfo.getEnd());
+    assertEquals(6, callInfo.getSize());
+
+    indexReader.close();
+  }
+
+  /**
+   * Pass condition: Of the 3 documents with points, 1 is outside of the radius and should be filtered out, 1 has been
+   * deleted so it should also be filtered out, leaving just 1 that passes the filter.  Its distance
+   * should be recorded in the calculated distances of the filter
+   *
+   * @throws IOException Can be thrown by the ParallelReader which is used as the IndexReader for the test
+   */
+  public void testIterate() throws IOException {
+    IndexReader indexReader = IndexReader.open(directory, false);
+    indexReader.deleteDocument(1);
+
+    BitSet bitSet = new BitSet(2);
+    bitSet.set(0);
+    bitSet.set(1);
+    bitSet.set(2);
+
+    ThreadedDistanceFilter distanceFilter = new ThreadedDistanceFilter(4.52, 30.81, 30, DistanceUnits.MILES, null, new ArcGeoDistanceCalculator(), null, 0);
+
+    ThreadedDistanceFilter.IterationResult result = distanceFilter.iterate(locationDataSet, bitSet, 0, 3, 3, indexReader);
+
+    assertNotNull(result);
+
+    BitSet resultingBitSet = result.getBitSet();
+    assertNotNull(resultingBitSet);
+    assertTrue(resultingBitSet.get(0));
+    assertFalse(resultingBitSet.get(1));
+    assertFalse(resultingBitSet.get(2));
+
+    assertNotNull(result.getDistanceById());
+    assertEquals(1, result.getDistanceById().size());
+  }
+
+  // ================================================= Inner Classes =================================================
+
+  private static class IterateCallInfo {
+
+    private final int start;
+    private final int end;
+    private final int size;
+
+    private IterateCallInfo(int start, int end, int size) {
+      this.start = start;
+      this.end = end;
+      this.size = size;
+    }
+
+    public int getStart() {
+      return start;
+    }
+
+    public int getEnd() {
+      return end;
+    }
+
+    public int getSize() {
+      return size;
+    }
+  }
+
+  /**
+   * Dummy implementation. Used for unit test to assure that the given arguments to a distance calculator are given correctly .
+   */
+  class StubGeoDistanceCalculator implements GeoDistanceCalculator {
+
+    private double sourceLatitude;
+    private double sourceLongitude;
+    private double targetLatitude;
+    private double targetLongitude;
+
+    public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnits unit) {
+      this.sourceLatitude = sourceLatitude;
+      this.sourceLongitude = sourceLongitude;
+      this.targetLatitude = targetLatitude;
+      this.targetLongitude = targetLongitude;
+      return 0.0;
+    }
+
+    public double getSourceLatitude() {
+      return sourceLatitude;
+    }
+
+    public double getSourceLongitude() {
+      return sourceLongitude;
+    }
+
+    public double getTargetLatitude() {
+      return targetLatitude;
+    }
+
+    public double getTargetLongitude() {
+      return targetLongitude;
+    }
+  }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/LocationDataSet.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/LocationDataSet.java	Fri Dec 11 15:22:22 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/LocationDataSet.java	Fri Dec 11 15:22:22 CET 2009
@@ -0,0 +1,38 @@
+/**
+ * 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.distance;
+
+import org.apache.lucene.spatial.geometry.shape.Point2D;
+
+/**
+ * LocationDataSet is an abstracts away the format of the data that defines the location of a document.  It means that
+ * different formats can be used, whether they be 2 fields for latitude and longitude, or 1 field with a geohash, without
+ * having to change the code that uses the data.
+ *
+ * <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 interface LocationDataSet {
+
+  /**
+   * Returns the point (defined by an x/y coordinate) of the document with the given id
+   *
+   * @param docId ID of the document whose point is to be returned
+   * @return Point (defined by an x/y coordinate) of the document with the given id
+   */
+  Point2D getPoint(int docId);
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/LocationDataSetFactory.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/LocationDataSetFactory.java	Fri Dec 11 15:22:32 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/LocationDataSetFactory.java	Fri Dec 11 15:22:32 CET 2009
@@ -0,0 +1,39 @@
+/**
+ * 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.distance;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+/**
+ * Factory for instances of {@link LocationDataSet}
+ *
+ * <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 interface LocationDataSetFactory {
+
+  /**
+   * Builds a LocationDataSet based on the data read from the given IndexReader
+   *
+   * @param indexReader IndexReader from where the location data will be read
+   * @return LocationDataSet representing the location data of the documents in the index
+   * @throws java.io.IOException Can be thrown while reading from the IndexReader
+   */
+  LocationDataSet buildLocationDataSet(IndexReader indexReader) throws IOException;
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/GeoHashLocationDataSetFactory.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/GeoHashLocationDataSetFactory.java	Fri Dec 11 15:23:20 CET 2009
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/GeoHashLocationDataSetFactory.java	Fri Dec 11 15:23:20 CET 2009
@@ -0,0 +1,82 @@
+/**
+ * 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.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.spatial.geohash.GeoHashUtils;
+import org.apache.lucene.spatial.geometry.shape.Point2D;
+
+import java.io.IOException;
+
+/**
+ * Implementation of {@link LocationDataSetFactory} that supports location information
+ * being contained in a single geohashed field.
+ *
+ * @see org.apache.lucene.spatial.geohash.GeoHashUtils
+ *
+ * <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 GeoHashLocationDataSetFactory implements LocationDataSetFactory {
+
+  private final String geoHashField;
+
+  /**
+   * Creates a new GeoHashLocationDataSetFactory that will read from the field with the given name
+   *
+   * @param geoHashField Name of the field containing the geohashes
+   */
+  public GeoHashLocationDataSetFactory(String geoHashField) {
+    this.geoHashField = geoHashField;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public LocationDataSet buildLocationDataSet(IndexReader indexReader) throws IOException {
+    return new GeoHashLocationDataSet(FieldCache.DEFAULT.getStringIndex(indexReader, geoHashField));
+  }
+
+  // ================================================= Inner Classes =================================================
+
+  /**
+   * Implementation of LocationDataSet which uses a geohash stored in a single index field
+   */
+  private class GeoHashLocationDataSet implements LocationDataSet {
+
+    private FieldCache.StringIndex index;
+
+    /**
+     * Creates a new GeoHashLocationDataSet which uses the values in the given index
+     *
+     * @param index A StringIndex containing the values of the geohash field taken from the index
+     */
+    private GeoHashLocationDataSet(FieldCache.StringIndex index) {
+      this.index = index;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Point2D getPoint(int docId) {
+      String fieldValue = index.lookup[index.order[docId]];
+      double[] coords = GeoHashUtils.decode(fieldValue);
+      return new Point2D(coords[0], coords[1]);
+    }
+  }
+}
