lucene-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From nkn...@apache.org
Subject [2/3] lucene-solr git commit: LUCENE-6997: refactor GeoPointField and query classes from lucene.spatial to lucene.spatial.geopoint package
Date Wed, 10 Feb 2016 03:51:55 GMT
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQueryImpl.java
new file mode 100644
index 0000000..35a3950
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQueryImpl.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.geopoint.search;
+
+import org.apache.lucene.search.MultiTermQuery;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.util.GeoRelationUtils;
+
+/** Package private implementation for the public facing GeoPointInPolygonQuery delegate class.
+ *
+ *    @lucene.experimental
+ */
+final class GeoPointInPolygonQueryImpl extends GeoPointInBBoxQueryImpl {
+  private final GeoPointInPolygonQuery polygonQuery;
+
+  GeoPointInPolygonQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointInPolygonQuery q,
+                             final double minLon, final double minLat, final double maxLon, final double maxLat) {
+    super(field, termEncoding, minLon, minLat, maxLon, maxLat);
+    polygonQuery = q;
+  }
+
+  @Override
+  public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
+    throw new UnsupportedOperationException("cannot change rewrite method");
+  }
+
+  @Override
+  protected CellComparator newCellComparator() {
+    return new GeoPolygonCellComparator(this);
+  }
+
+  /**
+   * Custom {@code org.apache.lucene.spatial.geopoint.search.GeoPointMultiTermQuery.CellComparator} that computes morton hash
+   * ranges based on the defined edges of the provided polygon.
+   */
+  private final class GeoPolygonCellComparator extends CellComparator {
+    GeoPolygonCellComparator(GeoPointMultiTermQuery query) {
+      super(query);
+    }
+
+    @Override
+    protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+      return GeoRelationUtils.rectCrossesPolyApprox(minLon, minLat, maxLon, maxLat, polygonQuery.x, polygonQuery.y,
+          polygonQuery.minLon, polygonQuery.minLat, polygonQuery.maxLon, polygonQuery.maxLat);
+    }
+
+    @Override
+    protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+      return GeoRelationUtils.rectWithinPolyApprox(minLon, minLat, maxLon, maxLat, polygonQuery.x, polygonQuery.y,
+          polygonQuery.minLon, polygonQuery.minLat, polygonQuery.maxLon, polygonQuery.maxLat);
+    }
+
+    @Override
+    protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+      return cellContains(minLon, minLat, maxLon, maxLat) || cellWithin(minLon, minLat, maxLon, maxLat)
+          || cellCrosses(minLon, minLat, maxLon, maxLat);
+    }
+
+    /**
+     * The two-phase query approach. The parent
+     * {@link org.apache.lucene.spatial.geopoint.search.GeoPointTermsEnum#accept} method is called to match
+     * encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
+     * bounding box filter are then compared to the provided polygon using the
+     * {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
+     */
+    @Override
+    protected boolean postFilter(final double lon, final double lat) {
+      return GeoRelationUtils.pointInPolygon(polygonQuery.x, polygonQuery.y, lat, lon);
+    }
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (o == null || getClass() != o.getClass()) return false;
+    if (!super.equals(o)) return false;
+
+    GeoPointInPolygonQueryImpl that = (GeoPointInPolygonQueryImpl) o;
+
+    return !(polygonQuery != null ? !polygonQuery.equals(that.polygonQuery) : that.polygonQuery != null);
+  }
+
+  @Override
+  public int hashCode() {
+    int result = super.hashCode();
+    result = 31 * result + (polygonQuery != null ? polygonQuery.hashCode() : 0);
+    return result;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointMultiTermQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointMultiTermQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointMultiTermQuery.java
new file mode 100644
index 0000000..b83d680
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointMultiTermQuery.java
@@ -0,0 +1,166 @@
+package org.apache.lucene.spatial.geopoint.search;
+
+/*
+ * 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.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.search.MultiTermQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.util.AttributeSource;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+import org.apache.lucene.spatial.util.GeoRelationUtils;
+import org.apache.lucene.spatial.util.GeoUtils;
+import org.apache.lucene.util.SloppyMath;
+
+/**
+ * TermQuery for GeoPointField for overriding {@link org.apache.lucene.search.MultiTermQuery} methods specific to
+ * Geospatial operations
+ *
+ * @lucene.experimental
+ */
+abstract class GeoPointMultiTermQuery extends MultiTermQuery {
+  // simple bounding box optimization - no objects used to avoid dependencies
+  protected final double minLon;
+  protected final double minLat;
+  protected final double maxLon;
+  protected final double maxLat;
+  protected final short maxShift;
+  protected final TermEncoding termEncoding;
+  protected final CellComparator cellComparator;
+
+  /**
+   * Constructs a query matching terms that cannot be represented with a single
+   * Term.
+   */
+  public GeoPointMultiTermQuery(String field, final TermEncoding termEncoding, final double minLon, final double minLat, final double maxLon, final double maxLat) {
+    super(field);
+
+    if (GeoUtils.isValidLon(minLon) == false) {
+      throw new IllegalArgumentException("invalid minLon " + minLon);
+    }
+    if (GeoUtils.isValidLon(maxLon) == false) {
+      throw new IllegalArgumentException("invalid maxLon " + maxLon);
+    }
+    if (GeoUtils.isValidLat(minLat) == false) {
+      throw new IllegalArgumentException("invalid minLat " + minLat);
+    }
+    if (GeoUtils.isValidLat(maxLat) == false) {
+      throw new IllegalArgumentException("invalid maxLat " + maxLat);
+    }
+
+    final long minHash = GeoEncodingUtils.mortonHash(minLon, minLat);
+    final long maxHash = GeoEncodingUtils.mortonHash(maxLon, maxLat);
+    this.minLon = GeoEncodingUtils.mortonUnhashLon(minHash);
+    this.minLat = GeoEncodingUtils.mortonUnhashLat(minHash);
+    this.maxLon = GeoEncodingUtils.mortonUnhashLon(maxHash);
+    this.maxLat = GeoEncodingUtils.mortonUnhashLat(maxHash);
+
+    this.maxShift = computeMaxShift();
+    this.termEncoding = termEncoding;
+    this.cellComparator = newCellComparator();
+
+    this.rewriteMethod = GEO_CONSTANT_SCORE_REWRITE;
+  }
+
+  public static final RewriteMethod GEO_CONSTANT_SCORE_REWRITE = new RewriteMethod() {
+    @Override
+    public Query rewrite(IndexReader reader, MultiTermQuery query) {
+      return new GeoPointTermQueryConstantScoreWrapper<>((GeoPointMultiTermQuery)query);
+    }
+  };
+
+  @Override @SuppressWarnings("unchecked")
+  protected TermsEnum getTermsEnum(final Terms terms, AttributeSource atts) throws IOException {
+    return GeoPointTermsEnum.newInstance(terms.iterator(), this);
+  }
+
+  /**
+   * Computes the maximum shift based on the diagonal distance of the bounding box
+   */
+  protected short computeMaxShift() {
+    // in this case a factor of 4 brings the detail level to ~0.002/0.001 degrees lon/lat respectively (or ~222m/111m)
+    final short shiftFactor;
+
+    // compute diagonal distance
+    double midLon = (minLon + maxLon) * 0.5;
+    double midLat = (minLat + maxLat) * 0.5;
+
+    if (SloppyMath.haversin(minLat, minLon, midLat, midLon)*1000 > 1000000) {
+      shiftFactor = 5;
+    } else {
+      shiftFactor = 4;
+    }
+
+    return (short)(GeoPointField.PRECISION_STEP * shiftFactor);
+  }
+
+  /**
+   * Abstract method to construct the class that handles all geo point relations
+   * (e.g., GeoPointInPolygon)
+   */
+  abstract protected CellComparator newCellComparator();
+
+  /**
+   * Base class for all geo point relation comparators
+   */
+  static abstract class CellComparator {
+    protected final GeoPointMultiTermQuery geoPointQuery;
+
+    CellComparator(GeoPointMultiTermQuery query) {
+      this.geoPointQuery = query;
+    }
+
+    /**
+     * Primary driver for cells intersecting shape boundaries
+     */
+    protected boolean cellIntersectsMBR(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+      return GeoRelationUtils.rectIntersects(minLon, minLat, maxLon, maxLat, geoPointQuery.minLon, geoPointQuery.minLat,
+          geoPointQuery.maxLon, geoPointQuery.maxLat);
+    }
+
+    /**
+     * Return whether quad-cell contains the bounding box of this shape
+     */
+    protected boolean cellContains(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+      return GeoRelationUtils.rectWithin(geoPointQuery.minLon, geoPointQuery.minLat, geoPointQuery.maxLon,
+          geoPointQuery.maxLat, minLon, minLat, maxLon, maxLat);
+    }
+
+    /**
+     * Determine whether the quad-cell crosses the shape
+     */
+    abstract protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat);
+
+    /**
+     * Determine whether quad-cell is within the shape
+     */
+    abstract protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat);
+
+    /**
+     * Default shape is a rectangle, so this returns the same as {@code cellIntersectsMBR}
+     */
+    abstract protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat);
+
+    abstract protected boolean postFilter(final double lon, final double lat);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointNumericTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointNumericTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointNumericTermsEnum.java
new file mode 100644
index 0000000..120df7d
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointNumericTermsEnum.java
@@ -0,0 +1,161 @@
+package org.apache.lucene.spatial.geopoint.search;
+
+/*
+ * 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.
+ */
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.util.LegacyNumericUtils;
+
+/**
+ * Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
+ * {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding#NUMERIC} method defined by
+ * {@link org.apache.lucene.analysis.LegacyNumericTokenStream}. The terms are then enumerated by the
+ * {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or
+ * pass the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the resulting DocIdSet.
+ *
+ *  @lucene.experimental
+ */
+@Deprecated
+final class GeoPointNumericTermsEnum extends GeoPointTermsEnum {
+  private final List<Range> rangeBounds = new LinkedList<>();
+
+  // detail level should be a factor of PRECISION_STEP limiting the depth of recursion (and number of ranges)
+  private final short DETAIL_LEVEL;
+
+  GeoPointNumericTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
+    super(tenum, query);
+    DETAIL_LEVEL = (short)(((GeoEncodingUtils.BITS<<1)-this.maxShift)/2);
+    computeRange(0L, (short) (((GeoEncodingUtils.BITS) << 1) - 1));
+    assert rangeBounds.isEmpty() == false;
+    Collections.sort(rangeBounds);
+  }
+
+  /**
+   * entry point for recursively computing ranges
+   */
+  private final void computeRange(long term, final short shift) {
+    final long split = term | (0x1L<<shift);
+    assert shift < 64;
+    final long upperMax;
+    if (shift < 63) {
+      upperMax = term | ((1L << (shift+1))-1);
+    } else {
+      upperMax = 0xffffffffffffffffL;
+    }
+    final long lowerMax = split-1;
+
+    relateAndRecurse(term, lowerMax, shift);
+    relateAndRecurse(split, upperMax, shift);
+  }
+
+  /**
+   * recurse to higher level precision cells to find ranges along the space-filling curve that fall within the
+   * query box
+   *
+   * @param start starting value on the space-filling curve for a cell at a given res
+   * @param end ending value on the space-filling curve for a cell at a given res
+   * @param res spatial res represented as a bit shift (MSB is lower res)
+   */
+  private void relateAndRecurse(final long start, final long end, final short res) {
+    final double minLon = GeoEncodingUtils.mortonUnhashLon(start);
+    final double minLat = GeoEncodingUtils.mortonUnhashLat(start);
+    final double maxLon = GeoEncodingUtils.mortonUnhashLon(end);
+    final double maxLat = GeoEncodingUtils.mortonUnhashLat(end);
+
+    final short level = (short)((GeoEncodingUtils.BITS<<1)-res>>>1);
+
+    // if cell is within and a factor of the precision step, or it crosses the edge of the shape add the range
+    final boolean within = res % GeoPointField.PRECISION_STEP == 0 && relationImpl.cellWithin(minLon, minLat, maxLon, maxLat);
+    if (within || (level == DETAIL_LEVEL && relationImpl.cellIntersectsShape(minLon, minLat, maxLon, maxLat))) {
+      final short nextRes = (short)(res-1);
+      if (nextRes % GeoPointField.PRECISION_STEP == 0) {
+        rangeBounds.add(new Range(start, nextRes, !within));
+        rangeBounds.add(new Range(start|(1L<<nextRes), nextRes, !within));
+      } else {
+        rangeBounds.add(new Range(start, res, !within));
+      }
+    } else if (level < DETAIL_LEVEL && relationImpl.cellIntersectsMBR(minLon, minLat, maxLon, maxLat)) {
+      computeRange(start, (short) (res - 1));
+    }
+  }
+
+  @Override
+  protected final BytesRef peek() {
+    rangeBounds.get(0).fillBytesRef(this.nextSubRangeBRB);
+    return nextSubRangeBRB.get();
+  }
+
+  @Override
+  protected void nextRange() {
+    currentRange = rangeBounds.remove(0);
+    super.nextRange();
+  }
+
+  @Override
+  protected final BytesRef nextSeekTerm(BytesRef term) {
+    while (hasNext()) {
+      if (currentRange == null) {
+        nextRange();
+      }
+      // if the new upper bound is before the term parameter, the sub-range is never a hit
+      if (term != null && term.compareTo(currentCell) > 0) {
+        nextRange();
+        if (!rangeBounds.isEmpty()) {
+          continue;
+        }
+      }
+      // never seek backwards, so use current term if lower bound is smaller
+      return (term != null && term.compareTo(currentCell) > 0) ? term : currentCell;
+    }
+
+    // no more sub-range enums available
+    assert rangeBounds.isEmpty();
+    return null;
+  }
+
+  @Override
+  protected final boolean hasNext() {
+    return rangeBounds.isEmpty() == false;
+  }
+
+  /**
+   * Internal class to represent a range along the space filling curve
+   */
+  protected final class Range extends BaseRange {
+    Range(final long lower, final short shift, boolean boundary) {
+      super(lower, shift, boundary);
+    }
+
+    /**
+     * Encode as a BytesRef using a reusable object. This allows us to lazily create the BytesRef (which is
+     * quite expensive), only when we need it.
+     */
+    @Override
+    protected void fillBytesRef(BytesRefBuilder result) {
+      assert result != null;
+      LegacyNumericUtils.longToPrefixCoded(start, shift, result);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointPrefixTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointPrefixTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointPrefixTermsEnum.java
new file mode 100644
index 0000000..fbaf68e
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointPrefixTermsEnum.java
@@ -0,0 +1,237 @@
+package org.apache.lucene.spatial.geopoint.search;
+
+/*
+ * 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.
+ */
+
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonHash;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLat;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLon;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.geoCodedToPrefixCoded;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.prefixCodedToGeoCoded;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.getPrefixCodedShift;
+
+/**
+ * Decomposes a given {@link GeoPointMultiTermQuery} into a set of terms that represent the query criteria using
+ * {@link org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding#PREFIX} method defined by
+ * {@link GeoPointField}. The terms are then enumerated by the
+ * {@link GeoPointTermQueryConstantScoreWrapper} and all docs whose GeoPoint fields match the prefix terms or pass
+ * the {@link GeoPointMultiTermQuery.CellComparator#postFilter} criteria are returned in the
+ * resulting DocIdSet.
+ *
+ *  @lucene.experimental
+ */
+final class GeoPointPrefixTermsEnum extends GeoPointTermsEnum {
+  private final long start;
+
+  private short shift;
+
+  // current range as long
+  private long currStart;
+  private long currEnd;
+
+  private final Range nextRange = new Range(-1, shift, true);
+
+  private boolean hasNext = false;
+
+  private boolean withinOnly = false;
+  private long lastWithin;
+
+  public GeoPointPrefixTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
+    super(tenum, query);
+    this.start = mortonHash(query.minLon, query.minLat);
+    this.currentRange = new Range(0, shift, true);
+    // start shift at maxShift value (from computeMaxShift)
+    this.shift = maxShift;
+    final long mask = (1L << shift) - 1;
+    this.currStart = start & ~mask;
+    this.currEnd = currStart | mask;
+  }
+
+  private boolean within(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+    return relationImpl.cellWithin(minLon, minLat, maxLon, maxLat);
+  }
+
+  private boolean boundary(final double minLon, final double minLat, final double maxLon, final double maxLat) {
+    return shift == maxShift && relationImpl.cellIntersectsShape(minLon, minLat, maxLon, maxLat);
+  }
+
+  private boolean nextWithin() {
+    if (withinOnly == false) {
+      return false;
+    }
+    currStart += (1L << shift);
+    setNextRange(false);
+    currentRange.set(nextRange);
+    hasNext = true;
+
+    withinOnly = lastWithin != currStart;
+    if (withinOnly == false) advanceVariables();
+    return true;
+  }
+
+  private void nextRelation() {
+    double minLon = mortonUnhashLon(currStart);
+    double minLat = mortonUnhashLat(currStart);
+    double maxLon;
+    double maxLat;
+    boolean isWithin;
+    do {
+      maxLon = mortonUnhashLon(currEnd);
+      maxLat = mortonUnhashLat(currEnd);
+
+      // within or a boundary
+      if ((isWithin = within(minLon, minLat, maxLon, maxLat) == true) || boundary(minLon, minLat, maxLon, maxLat) == true) {
+        final int m;
+        if (isWithin == false || (m = shift % GeoPointField.PRECISION_STEP) == 0) {
+          setNextRange(isWithin == false);
+          advanceVariables();
+          break;
+        } else if (shift < 54) {
+          withinOnly = true;
+          shift = (short)(shift - m);
+          lastWithin = currEnd & ~((1L << shift) - 1);
+          setNextRange(false);
+          break;
+        }
+      }
+
+      // within cell but not at a depth factor of PRECISION_STEP
+      if (isWithin == true || (relationImpl.cellIntersectsMBR(minLon, minLat, maxLon , maxLat) == true && shift != maxShift)) {
+        // descend: currStart need not change since shift handles end of range
+        currEnd = currStart | (1L<<--shift) - 1;
+      } else {
+        advanceVariables();
+        minLon = mortonUnhashLon(currStart);
+        minLat = mortonUnhashLat(currStart);
+      }
+    } while(shift < 63);
+  }
+
+  private void setNextRange(final boolean boundary) {
+    nextRange.start = currStart;
+    nextRange.shift = shift;
+    nextRange.boundary = boundary;
+  }
+
+  private void advanceVariables() {
+    /** set next variables */
+    long shiftMask = 1L << shift;
+    // pop-up if shift bit is set
+    while ( (currStart & shiftMask) == shiftMask) {
+      shiftMask = 1L << ++shift;
+    }
+    final long shiftMOne = shiftMask - 1;
+    currStart = currStart & ~shiftMOne | shiftMask;
+    currEnd = currStart | shiftMOne;
+  }
+
+  @Override
+  protected final BytesRef peek() {
+    nextRange.fillBytesRef(nextSubRangeBRB);
+    return super.peek();
+  }
+
+  protected void seek(long term, short res) {
+    if (term < currStart && res < maxShift) {
+      throw new IllegalArgumentException("trying to seek backwards");
+    } else if (term == currStart) {
+      return;
+    }
+    shift = res;
+    currStart = term;
+    currEnd = currStart | ((1L<<shift)-1);
+    withinOnly = false;
+  }
+
+  @Override
+  protected void nextRange() {
+    hasNext = false;
+    super.nextRange();
+  }
+
+  @Override
+  protected final boolean hasNext() {
+    if (hasNext == true || nextWithin()) {
+      return true;
+    }
+    nextRelation();
+    if (currentRange.compareTo(nextRange) != 0) {
+      currentRange.set(nextRange);
+      return (hasNext = true);
+    }
+    return false;
+  }
+
+  @Override
+  protected final BytesRef nextSeekTerm(BytesRef term) {
+    while (hasNext()) {
+      nextRange();
+      if (term == null) {
+        return currentCell;
+      }
+
+      final int comparison = term.compareTo(currentCell);
+      if (comparison > 0) {
+        seek(GeoEncodingUtils.prefixCodedToGeoCoded(term), (short)(64-GeoEncodingUtils.getPrefixCodedShift(term)));
+        continue;
+      }
+      return currentCell;
+    }
+
+    // no more sub-range enums available
+    return null;
+  }
+
+  @Override
+  protected AcceptStatus accept(BytesRef term) {
+    // range < term or range is null
+    while (currentCell == null || term.compareTo(currentCell) > 0) {
+      // no more ranges, be gone
+      if (hasNext() == false) {
+        return AcceptStatus.END;
+      }
+
+      // peek next range, if the range > term then seek
+      final int peekCompare = term.compareTo(peek());
+      if (peekCompare < 0) {
+        return AcceptStatus.NO_AND_SEEK;
+      } else if (peekCompare > 0) {
+        seek(prefixCodedToGeoCoded(term), (short)(64 - getPrefixCodedShift(term)));
+      }
+      nextRange();
+    }
+    return AcceptStatus.YES;
+  }
+
+  protected final class Range extends BaseRange {
+    public Range(final long start, final short res, final boolean boundary) {
+      super(start, res, boundary);
+    }
+
+    @Override
+    protected void fillBytesRef(BytesRefBuilder result) {
+      assert result != null;
+      geoCodedToPrefixCoded(start, shift, result);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermQueryConstantScoreWrapper.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermQueryConstantScoreWrapper.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermQueryConstantScoreWrapper.java
new file mode 100644
index 0000000..c8d3e55
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermQueryConstantScoreWrapper.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.geopoint.search;
+
+import java.io.IOException;
+
+import org.apache.lucene.index.LeafReader;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.PostingsEnum;
+import org.apache.lucene.index.SortedNumericDocValues;
+import org.apache.lucene.index.Terms;
+import org.apache.lucene.search.ConstantScoreScorer;
+import org.apache.lucene.search.ConstantScoreWeight;
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.TwoPhaseIterator;
+import org.apache.lucene.search.Weight;
+import org.apache.lucene.util.BitSet;
+import org.apache.lucene.util.DocIdSetBuilder;
+import org.apache.lucene.util.FixedBitSet;
+import org.apache.lucene.util.SparseFixedBitSet;
+
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLat;
+import static org.apache.lucene.spatial.util.GeoEncodingUtils.mortonUnhashLon;
+
+
+/**
+ * Custom ConstantScoreWrapper for {@code GeoPointMultiTermQuery} that cuts over to DocValues
+ * for post filtering boundary ranges. Multi-valued GeoPoint documents are supported.
+ *
+ * @lucene.experimental
+ */
+final class GeoPointTermQueryConstantScoreWrapper <Q extends GeoPointMultiTermQuery> extends Query {
+  protected final Q query;
+
+  protected GeoPointTermQueryConstantScoreWrapper(Q query) {
+    this.query = query;
+  }
+
+  @Override
+  public String toString(String field) {
+    return query.toString();
+  }
+
+  @Override
+  public final boolean equals(final Object o) {
+    if (super.equals(o) == false) {
+      return false;
+    }
+    final GeoPointTermQueryConstantScoreWrapper<?> that = (GeoPointTermQueryConstantScoreWrapper<?>) o;
+    return this.query.equals(that.query);
+  }
+
+  @Override
+  public final int hashCode() {
+    return 31 * super.hashCode() + query.hashCode();
+  }
+
+  @Override
+  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+    return new ConstantScoreWeight(this) {
+
+      @Override
+      public Scorer scorer(LeafReaderContext context) throws IOException {
+        final Terms terms = context.reader().terms(query.getField());
+        if (terms == null) {
+          return null;
+        }
+
+        final GeoPointTermsEnum termsEnum = (GeoPointTermsEnum)(query.getTermsEnum(terms, null));
+        assert termsEnum != null;
+
+        LeafReader reader = context.reader();
+        // approximation (postfiltering has not yet been applied)
+        DocIdSetBuilder builder = new DocIdSetBuilder(reader.maxDoc());
+        // subset of documents that need no postfiltering, this is purely an optimization
+        final BitSet preApproved;
+        // dumb heuristic: if the field is really sparse, use a sparse impl
+        if (terms.getDocCount() * 100L < reader.maxDoc()) {
+          preApproved = new SparseFixedBitSet(reader.maxDoc());
+        } else {
+          preApproved = new FixedBitSet(reader.maxDoc());
+        }
+        PostingsEnum docs = null;
+
+        while (termsEnum.next() != null) {
+          docs = termsEnum.postings(docs, PostingsEnum.NONE);
+          // boundary terms need post filtering
+          if (termsEnum.boundaryTerm()) {
+            builder.add(docs);
+          } else {
+            int docId;
+            while ((docId = docs.nextDoc()) != DocIdSetIterator.NO_MORE_DOCS) {
+              builder.add(docId);
+              preApproved.set(docId);
+            }
+          }
+        }
+
+        DocIdSet set = builder.build();
+        final DocIdSetIterator disi = set.iterator();
+        if (disi == null) {
+          return null;
+        }
+
+        // return two-phase iterator using docvalues to postfilter candidates
+        SortedNumericDocValues sdv = reader.getSortedNumericDocValues(query.getField());
+        TwoPhaseIterator iterator = new TwoPhaseIterator(disi) {
+          @Override
+          public boolean matches() throws IOException {
+            int docId = disi.docID();
+            if (preApproved.get(docId)) {
+              return true;
+            } else {
+              sdv.setDocument(docId);
+              int count = sdv.count();
+              for (int i = 0; i < count; i++) {
+                long hash = sdv.valueAt(i);
+                if (termsEnum.postFilter(mortonUnhashLon(hash), mortonUnhashLat(hash))) {
+                  return true;
+                }
+              }
+              return false;
+            }
+          }
+
+          @Override
+          public float matchCost() {
+            return 20; // TODO: make this fancier
+          }
+        };
+        return new ConstantScoreScorer(this, score(), iterator);
+      }
+    };
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermsEnum.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermsEnum.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermsEnum.java
new file mode 100644
index 0000000..9f9e251
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointTermsEnum.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.lucene.spatial.geopoint.search;
+
+import org.apache.lucene.index.FilteredTermsEnum;
+import org.apache.lucene.index.TermsEnum;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
+import org.apache.lucene.spatial.geopoint.search.GeoPointMultiTermQuery.CellComparator;
+
+/**
+ * Base class for {@link GeoPointNumericTermsEnum} and {@link GeoPointPrefixTermsEnum} which compares
+ * candidate GeoPointField encoded terms against terms matching the defined query criteria.
+ *
+ *  @lucene.experimental
+ */
+abstract class GeoPointTermsEnum extends FilteredTermsEnum {
+  protected final short maxShift;
+
+  protected BaseRange currentRange;
+  protected BytesRef currentCell;
+  protected final BytesRefBuilder currentCellBRB = new BytesRefBuilder();
+  protected final BytesRefBuilder nextSubRangeBRB = new BytesRefBuilder();
+
+  protected final CellComparator relationImpl;
+
+  GeoPointTermsEnum(final TermsEnum tenum, final GeoPointMultiTermQuery query) {
+    super(tenum);
+    this.maxShift = query.maxShift;
+    this.relationImpl = query.cellComparator;
+  }
+
+  static GeoPointTermsEnum newInstance(final TermsEnum terms, final GeoPointMultiTermQuery query) {
+    if (query.termEncoding == TermEncoding.PREFIX) {
+      return new GeoPointPrefixTermsEnum(terms, query);
+    } else if (query.termEncoding == TermEncoding.NUMERIC) {
+      return new GeoPointNumericTermsEnum(terms, query);
+    }
+    throw new IllegalArgumentException("Invalid GeoPoint TermEncoding " + query.termEncoding);
+  }
+
+  public boolean boundaryTerm() {
+    if (currentCell == null) {
+      throw new IllegalStateException("GeoPointTermsEnum empty or not initialized");
+    }
+    return currentRange.boundary;
+  }
+
+  protected BytesRef peek() {
+    return nextSubRangeBRB.get();
+  }
+
+  abstract protected boolean hasNext();
+
+  protected void nextRange() {
+    currentRange.fillBytesRef(currentCellBRB);
+    currentCell = currentCellBRB.get();
+  }
+
+  /**
+   * The two-phase query approach. {@link #nextSeekTerm} is called to obtain the next term that matches a numeric
+   * range of the bounding box. Those terms that pass the initial range filter are then compared against the
+   * decoded min/max latitude and longitude values of the bounding box only if the range is not a "boundary" range
+   * (e.g., a range that straddles the boundary of the bbox).
+   * @param term term for candidate document
+   * @return match status
+   */
+  @Override
+  protected AcceptStatus accept(BytesRef term) {
+    // validate value is in range
+    while (currentCell == null || term.compareTo(currentCell) > 0) {
+      if (hasNext() == false) {
+        return AcceptStatus.END;
+      }
+      // peek next sub-range, only seek if the current term is smaller than next lower bound
+      if (term.compareTo(peek()) < 0) {
+        return AcceptStatus.NO_AND_SEEK;
+      }
+      // step forward to next range without seeking, as next range is less or equal current term
+      nextRange();
+    }
+
+    return AcceptStatus.YES;
+  }
+
+  protected boolean postFilter(final double lon, final double lat) {
+    return relationImpl.postFilter(lon, lat);
+  }
+
+  /**
+   * Internal class to represent a range along the space filling curve
+   */
+  abstract class BaseRange implements Comparable<BaseRange> {
+    protected short shift;
+    protected long start;
+    protected boolean boundary;
+
+    BaseRange(final long lower, final short shift, boolean boundary) {
+      this.boundary = boundary;
+      this.start = lower;
+      this.shift = shift;
+    }
+
+    /**
+     * Encode as a BytesRef using a reusable object. This allows us to lazily create the BytesRef (which is
+     * quite expensive), only when we need it.
+     */
+    abstract protected void fillBytesRef(BytesRefBuilder result);
+
+    @Override
+    public int compareTo(BaseRange other) {
+      final int result = Short.compare(this.shift, other.shift);
+      if (result == 0) {
+        return Long.compare(this.start, other.start);
+      }
+      return result;
+    }
+
+    protected void set(BaseRange other) {
+      this.start = other.start;
+      this.shift = other.shift;
+      this.boundary = other.boundary;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/package-info.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/package-info.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/package-info.java
new file mode 100644
index 0000000..0d923df
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Geospatial Query Implementations for Core Lucene
+ */
+package org.apache.lucene.spatial.geopoint.search;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoBoundingBox.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoBoundingBox.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoBoundingBox.java
deleted file mode 100644
index f26f8b1..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoBoundingBox.java
+++ /dev/null
@@ -1,53 +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.spatial.search;
-
-import org.apache.lucene.spatial.util.GeoUtils;
-
-/** NOTE: package private; just used so {@link GeoPointInPolygonQuery} can communicate its bounding box to {@link GeoPointInBBoxQuery}. */
-class GeoBoundingBox {
-  /** minimum longitude value (in degrees) */
-  public final double minLon;
-  /** minimum latitude value (in degrees) */
-  public final double maxLon;
-  /** maximum longitude value (in degrees) */
-  public final double minLat;
-  /** maximum latitude value (in degrees) */
-  public final double maxLat;
-
-  /**
-   * Constructs a bounding box by first validating the provided latitude and longitude coordinates
-   */
-  public GeoBoundingBox(double minLon, double maxLon, double minLat, double maxLat) {
-    if (GeoUtils.isValidLon(minLon) == false) {
-      throw new IllegalArgumentException("invalid minLon " + minLon);
-    }
-    if (GeoUtils.isValidLon(maxLon) == false) {
-      throw new IllegalArgumentException("invalid maxLon " + minLon);
-    }
-    if (GeoUtils.isValidLat(minLat) == false) {
-      throw new IllegalArgumentException("invalid minLat " + minLat);
-    }
-    if (GeoUtils.isValidLat(maxLat) == false) {
-      throw new IllegalArgumentException("invalid maxLat " + minLat);
-    }
-    this.minLon = minLon;
-    this.maxLon = maxLon;
-    this.minLat = minLat;
-    this.maxLat = maxLat;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
deleted file mode 100644
index 24b7f8f..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQuery.java
+++ /dev/null
@@ -1,189 +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.spatial.search;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-import org.apache.lucene.spatial.util.GeoDistanceUtils;
-import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoUtils;
-
-/** Implements a simple point distance query on a GeoPoint field. This is based on
- * {@link GeoPointInBBoxQuery} and is implemented using a two phase approach. First,
- * like {@code GeoPointInBBoxQueryImpl} candidate terms are queried using the numeric ranges based on
- * the morton codes of the min and max lat/lon pairs that intersect the boundary of the point-radius
- * circle. Terms
- * passing this initial filter are then passed to a secondary {@code postFilter} method that verifies whether the
- * decoded lat/lon point fall within the specified query distance (see {@link org.apache.lucene.util.SloppyMath#haversin}.
- * All morton value comparisons are subject to the same precision tolerance defined in
- * {@value org.apache.lucene.spatial.util.GeoEncodingUtils#TOLERANCE} and distance comparisons are subject to the accuracy of the
- * haversine formula (from R.W. Sinnott, "Virtues of the Haversine", Sky and Telescope, vol. 68, no. 2, 1984, p. 159)
- *
- * <p>Note: This query currently uses haversine which is a sloppy distance calculation (see above reference). For large
- * queries one can expect upwards of 400m error. Vincenty shrinks this to ~40m error but pays a penalty for computing
- * using the spheroid
- *
- * @lucene.experimental */
-public class GeoPointDistanceQuery extends GeoPointInBBoxQuery {
-  /** longitude value (in degrees) for query location */
-  protected final double centerLon;
-  /** latitude value (in degrees) for query location */
-  protected final double centerLat;
-  /** distance (in meters) from lon, lat center location */
-  protected final double radiusMeters;
-
-  /**
-   * Constructs a Query for all {@link org.apache.lucene.spatial.document.GeoPointField} types within a
-   * distance (in meters) from a given point
-   **/
-  public GeoPointDistanceQuery(final String field, final double centerLon, final double centerLat, final double radiusMeters) {
-    this(field, TermEncoding.PREFIX, centerLon, centerLat, radiusMeters);
-  }
-
-  public GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final double centerLon, final double centerLat, final double radiusMeters) {
-    this(field, termEncoding, GeoUtils.circleToBBox(centerLon, centerLat, radiusMeters), centerLon, centerLat, radiusMeters);
-  }
-
-  private GeoPointDistanceQuery(final String field, final TermEncoding termEncoding, final GeoRect bbox, final double centerLon,
-                                final double centerLat, final double radiusMeters) {
-    super(field, termEncoding, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
-    {
-      // check longitudinal overlap (restrict distance to maximum longitudinal radius)
-      // todo this restriction technically shouldn't be needed,
-      // its only purpose is to ensure the bounding box doesn't self overlap.
-      final double maxRadius = GeoDistanceUtils.maxRadialDistanceMeters(centerLon, centerLat);
-      if (radiusMeters > maxRadius) {
-        throw new IllegalArgumentException("radiusMeters " + radiusMeters + " exceeds maxRadius [" + maxRadius
-            + "] at location [" + centerLon + " " + centerLat + "]");
-      }
-    }
-
-    if (GeoUtils.isValidLon(centerLon) == false) {
-      throw new IllegalArgumentException("invalid centerLon " + centerLon);
-    }
-
-    if (GeoUtils.isValidLat(centerLat) == false) {
-      throw new IllegalArgumentException("invalid centerLat " + centerLat);
-    }
-
-    if (radiusMeters <= 0.0) {
-      throw new IllegalArgumentException("invalid radiusMeters " + radiusMeters);
-    }
-
-    this.centerLon = centerLon;
-    this.centerLat = centerLat;
-    this.radiusMeters = radiusMeters;
-  }
-
-  @Override
-  public Query rewrite(IndexReader reader) {
-    // query crosses dateline; split into left and right queries
-    if (maxLon < minLon) {
-      BooleanQuery.Builder bqb = new BooleanQuery.Builder();
-
-      // unwrap the longitude iff outside the specified min/max lon range
-      double unwrappedLon = centerLon;
-      if (unwrappedLon > maxLon) {
-        // unwrap left
-        unwrappedLon += -360.0D;
-      }
-      GeoPointDistanceQueryImpl left = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
-          new GeoRect(GeoUtils.MIN_LON_INCL, maxLon, minLat, maxLat));
-      bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
-
-      if (unwrappedLon < maxLon) {
-        // unwrap right
-        unwrappedLon += 360.0D;
-      }
-      GeoPointDistanceQueryImpl right = new GeoPointDistanceQueryImpl(field, termEncoding, this, unwrappedLon,
-          new GeoRect(minLon, GeoUtils.MAX_LON_INCL, minLat, maxLat));
-      bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
-
-      return bqb.build();
-    }
-    return new GeoPointDistanceQueryImpl(field, termEncoding, this, centerLon,
-        new GeoRect(this.minLon, this.maxLon, this.minLat, this.maxLat));
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (!(o instanceof GeoPointDistanceQuery)) return false;
-    if (!super.equals(o)) return false;
-
-    GeoPointDistanceQuery that = (GeoPointDistanceQuery) o;
-
-    if (Double.compare(that.centerLat, centerLat) != 0) return false;
-    if (Double.compare(that.centerLon, centerLon) != 0) return false;
-    if (Double.compare(that.radiusMeters, radiusMeters) != 0) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    long temp;
-    temp = Double.doubleToLongBits(centerLon);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(centerLat);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(radiusMeters);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    return result;
-  }
-
-  @Override
-  public String toString(String field) {
-    final StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(':');
-    if (!this.field.equals(field)) {
-      sb.append(" field=");
-      sb.append(this.field);
-      sb.append(':');
-    }
-    return sb.append( " Center: [")
-        .append(centerLon)
-        .append(',')
-        .append(centerLat)
-        .append(']')
-        .append(" Distance: ")
-        .append(radiusMeters)
-        .append(" meters")
-        .append("]")
-        .toString();
-  }
-
-  /** getter method for center longitude value */
-  public double getCenterLon() {
-    return this.centerLon;
-  }
-
-  /** getter method for center latitude value */
-  public double getCenterLat() {
-    return this.centerLat;
-  }
-
-  /** getter method for distance value (in meters) */
-  public double getRadiusMeters() {
-    return this.radiusMeters;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
deleted file mode 100644
index 39fc696..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceQueryImpl.java
+++ /dev/null
@@ -1,107 +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.spatial.search;
-
-import org.apache.lucene.search.MultiTermQuery;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-import org.apache.lucene.util.SloppyMath;
-
-/** Package private implementation for the public facing GeoPointDistanceQuery delegate class.
- *
- *    @lucene.experimental
- */
-final class GeoPointDistanceQueryImpl extends GeoPointInBBoxQueryImpl {
-  private final GeoPointDistanceQuery distanceQuery;
-  private final double centerLon;
-
-  GeoPointDistanceQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointDistanceQuery q,
-                            final double centerLonUnwrapped, final GeoRect bbox) {
-    super(field, termEncoding, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
-    distanceQuery = q;
-    centerLon = centerLonUnwrapped;
-  }
-
-  @Override
-  public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
-    throw new UnsupportedOperationException("cannot change rewrite method");
-  }
-
-  @Override
-  protected CellComparator newCellComparator() {
-    return new GeoPointRadiusCellComparator(this);
-  }
-
-  private final class GeoPointRadiusCellComparator extends CellComparator {
-    GeoPointRadiusCellComparator(GeoPointDistanceQueryImpl query) {
-      super(query);
-    }
-
-    @Override
-    protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return GeoRelationUtils.rectCrossesCircle(minLon, minLat, maxLon, maxLat,
-          centerLon, distanceQuery.centerLat, distanceQuery.radiusMeters, true);
-    }
-
-    @Override
-    protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return GeoRelationUtils.rectWithinCircle(minLon, minLat, maxLon, maxLat,
-          centerLon, distanceQuery.centerLat, distanceQuery.radiusMeters, true);
-    }
-
-    @Override
-    protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return cellCrosses(minLon, minLat, maxLon, maxLat);
-    }
-
-    /**
-     * The two-phase query approach. The parent {@link GeoPointTermsEnum} class matches
-     * encoded terms that fall within the minimum bounding box of the point-radius circle. Those documents that pass
-     * the initial bounding box filter are then post filter compared to the provided distance using the
-     * {@link org.apache.lucene.util.SloppyMath#haversin} method.
-     */
-    @Override
-    protected boolean postFilter(final double lon, final double lat) {
-      return (SloppyMath.haversin(distanceQuery.centerLat, centerLon, lat, lon) * 1000.0 <= distanceQuery.radiusMeters);
-    }
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (!(o instanceof GeoPointDistanceQueryImpl)) return false;
-    if (!super.equals(o)) return false;
-
-    GeoPointDistanceQueryImpl that = (GeoPointDistanceQueryImpl) o;
-
-    if (!distanceQuery.equals(that.distanceQuery)) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    result = 31 * result + distanceQuery.hashCode();
-    return result;
-  }
-
-  public double getRadiusMeters() {
-    return distanceQuery.getRadiusMeters();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
deleted file mode 100644
index eefdc79..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointDistanceRangeQuery.java
+++ /dev/null
@@ -1,116 +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.spatial.search;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-
-/** Implements a point distance range query on a GeoPoint field. This is based on
- * {@code org.apache.lucene.spatial.search.GeoPointDistanceQuery} and is implemented using a
- * {@code org.apache.lucene.search.BooleanClause.MUST_NOT} clause to exclude any points that fall within
- * minRadiusMeters from the provided point.
- *
- *    @lucene.experimental
- */
-public final class GeoPointDistanceRangeQuery extends GeoPointDistanceQuery {
-  protected final double minRadiusMeters;
-
-  /**
-   * Constructs a query for all {@link org.apache.lucene.spatial.document.GeoPointField} types within a minimum / maximum
-   * distance (in meters) range from a given point
-   */
-  public GeoPointDistanceRangeQuery(final String field, final double centerLon, final double centerLat,
-                                    final double minRadiusMeters, final double maxRadiusMeters) {
-    this(field, TermEncoding.PREFIX, centerLon, centerLat, minRadiusMeters, maxRadiusMeters);
-  }
-
-  public GeoPointDistanceRangeQuery(final String field, final TermEncoding termEncoding, final double centerLon, final double centerLat,
-                                    final double minRadiusMeters, final double maxRadius) {
-    super(field, termEncoding, centerLon, centerLat, maxRadius);
-    this.minRadiusMeters = minRadiusMeters;
-  }
-
-  @Override
-  public Query rewrite(IndexReader reader) {
-    Query q = super.rewrite(reader);
-    if (minRadiusMeters == 0.0) {
-      return q;
-    }
-
-    // add an exclusion query
-    BooleanQuery.Builder bqb = new BooleanQuery.Builder();
-
-    // create a new exclusion query
-    GeoPointDistanceQuery exclude = new GeoPointDistanceQuery(field, termEncoding, centerLon, centerLat, minRadiusMeters);
-    // full map search
-//    if (radiusMeters >= GeoProjectionUtils.SEMIMINOR_AXIS) {
-//      bqb.add(new BooleanClause(new GeoPointInBBoxQuery(this.field, -180.0, -90.0, 180.0, 90.0), BooleanClause.Occur.MUST));
-//    } else {
-      bqb.add(new BooleanClause(q, BooleanClause.Occur.MUST));
-//    }
-    bqb.add(new BooleanClause(exclude, BooleanClause.Occur.MUST_NOT));
-
-    return bqb.build();
-  }
-
-  @Override
-  public String toString(String field) {
-    final StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(':');
-    if (!this.field.equals(field)) {
-      sb.append(" field=");
-      sb.append(this.field);
-      sb.append(':');
-    }
-    return sb.append( " Center: [")
-        .append(centerLon)
-        .append(',')
-        .append(centerLat)
-        .append(']')
-        .append(" From Distance: ")
-        .append(minRadiusMeters)
-        .append(" m")
-        .append(" To Distance: ")
-        .append(radiusMeters)
-        .append(" m")
-        .append(" Lower Left: [")
-        .append(minLon)
-        .append(',')
-        .append(minLat)
-        .append(']')
-        .append(" Upper Right: [")
-        .append(maxLon)
-        .append(',')
-        .append(maxLat)
-        .append("]")
-        .toString();
-  }
-
-  /** getter method for minimum distance */
-  public double getMinRadiusMeters() {
-    return this.minRadiusMeters;
-  }
-
-  /** getter method for maximum distance */
-  public double getMaxRadiusMeters() {
-    return this.radiusMeters;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
deleted file mode 100644
index 0e6d5bf..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQuery.java
+++ /dev/null
@@ -1,173 +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.spatial.search;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.FieldValueQuery;
-import org.apache.lucene.search.LegacyNumericRangeQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-import org.apache.lucene.spatial.util.GeoUtils;
-
-/** Implements a simple bounding box query on a GeoPoint field. This is inspired by
- * {@link LegacyNumericRangeQuery} and is implemented using a
- * two phase approach. First, candidate terms are queried using a numeric
- * range based on the morton codes of the min and max lat/lon pairs. Terms
- * passing this initial filter are passed to a final check that verifies whether
- * the decoded lat/lon falls within (or on the boundary) of the query bounding box.
- * The value comparisons are subject to a precision tolerance defined in
- * {@value org.apache.lucene.spatial.util.GeoEncodingUtils#TOLERANCE}
- *
- * NOTES:
- *    1.  All latitude/longitude values must be in decimal degrees.
- *    2.  Complex computational geometry (e.g., dateline wrapping) is not supported
- *    3.  For more advanced GeoSpatial indexing and query operations see spatial module
- *    4.  This is well suited for small rectangles, large bounding boxes may result
- *        in many terms, depending whether the bounding box falls on the boundary of
- *        many cells (degenerate case)
- *
- * @lucene.experimental
- */
-public class GeoPointInBBoxQuery extends Query {
-  protected final String field;
-  protected final double minLon;
-  protected final double minLat;
-  protected final double maxLon;
-  protected final double maxLat;
-  protected final TermEncoding termEncoding;
-
-  /**
-   * Constructs a query for all {@link org.apache.lucene.spatial.document.GeoPointField} types that fall within a
-   * defined bounding box
-   */
-  public GeoPointInBBoxQuery(final String field, final double minLon, final double minLat, final double maxLon, final double maxLat) {
-    this(field, TermEncoding.PREFIX, minLon, minLat, maxLon, maxLat);
-  }
-
-  public GeoPointInBBoxQuery(final String field, final TermEncoding termEncoding, final double minLon, final double minLat, final double maxLon, final double maxLat) {
-    this.field = field;
-    this.minLon = minLon;
-    this.minLat = minLat;
-    this.maxLon = maxLon;
-    this.maxLat = maxLat;
-    this.termEncoding = termEncoding;
-  }
-
-  @Override
-  public Query rewrite(IndexReader reader) {
-    // short-circuit to match all if specifying the whole map
-    if (minLon == GeoUtils.MIN_LON_INCL && maxLon == GeoUtils.MAX_LON_INCL
-        && minLat == GeoUtils.MIN_LAT_INCL && maxLat == GeoUtils.MAX_LAT_INCL) {
-      // FieldValueQuery is valid since DocValues are *required* for GeoPointField
-      return new FieldValueQuery(field);
-    }
-
-    if (maxLon < minLon) {
-      BooleanQuery.Builder bqb = new BooleanQuery.Builder();
-
-      GeoPointInBBoxQueryImpl left = new GeoPointInBBoxQueryImpl(field, termEncoding, -180.0D, minLat, maxLon, maxLat);
-      bqb.add(new BooleanClause(left, BooleanClause.Occur.SHOULD));
-      GeoPointInBBoxQueryImpl right = new GeoPointInBBoxQueryImpl(field, termEncoding, minLon, minLat, 180.0D, maxLat);
-      bqb.add(new BooleanClause(right, BooleanClause.Occur.SHOULD));
-      return bqb.build();
-    }
-    return new GeoPointInBBoxQueryImpl(field, termEncoding, minLon, minLat, maxLon, maxLat);
-  }
-
-  @Override
-  public String toString(String field) {
-    final StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(':');
-    if (!this.field.equals(field)) {
-      sb.append(" field=");
-      sb.append(this.field);
-      sb.append(':');
-    }
-    return sb.append(" Lower Left: [")
-        .append(minLon)
-        .append(',')
-        .append(minLat)
-        .append(']')
-        .append(" Upper Right: [")
-        .append(maxLon)
-        .append(',')
-        .append(maxLat)
-        .append("]")
-        .toString();
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (!(o instanceof GeoPointInBBoxQuery)) return false;
-    if (!super.equals(o)) return false;
-
-    GeoPointInBBoxQuery that = (GeoPointInBBoxQuery) o;
-
-    if (Double.compare(that.maxLat, maxLat) != 0) return false;
-    if (Double.compare(that.maxLon, maxLon) != 0) return false;
-    if (Double.compare(that.minLat, minLat) != 0) return false;
-    if (Double.compare(that.minLon, minLon) != 0) return false;
-    if (!field.equals(that.field)) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    long temp;
-    result = 31 * result + field.hashCode();
-    temp = Double.doubleToLongBits(minLon);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(minLat);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(maxLon);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(maxLat);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    return result;
-  }
-
-  /** getter method for retrieving the field name */
-  public final String getField() {
-    return this.field;
-  }
-
-  /** getter method for retrieving the minimum longitude (in degrees) */
-  public final double getMinLon() {
-    return this.minLon;
-  }
-
-  /** getter method for retrieving the minimum latitude (in degrees) */
-  public final double getMinLat() {
-    return this.minLat;
-  }
-
-  /** getter method for retrieving the maximum longitude (in degrees) */
-  public final double getMaxLon() {
-    return this.maxLon;
-  }
-
-  /** getter method for retrieving the maximum latitude (in degrees) */
-  public final double getMaxLat() {
-    return this.maxLat;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
deleted file mode 100644
index 8bce0f0..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInBBoxQueryImpl.java
+++ /dev/null
@@ -1,156 +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.spatial.search;
-
-import org.apache.lucene.search.MultiTermQuery;
-import org.apache.lucene.util.SloppyMath;
-import org.apache.lucene.spatial.document.GeoPointField;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-
-/** Package private implementation for the public facing GeoPointInBBoxQuery delegate class.
- *
- *    @lucene.experimental
- */
-class GeoPointInBBoxQueryImpl extends GeoPointMultiTermQuery {
-  /**
-   * Constructs a new GeoBBoxQuery that will match encoded GeoPoint terms that fall within or on the boundary
-   * of the bounding box defined by the input parameters
-   * @param field the field name
-   * @param minLon lower longitude (x) value of the bounding box
-   * @param minLat lower latitude (y) value of the bounding box
-   * @param maxLon upper longitude (x) value of the bounding box
-   * @param maxLat upper latitude (y) value of the bounding box
-   */
-  GeoPointInBBoxQueryImpl(final String field, final TermEncoding termEncoding, final double minLon, final double minLat, final double maxLon, final double maxLat) {
-    super(field, termEncoding, minLon, minLat, maxLon, maxLat);
-  }
-
-  @Override
-  public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
-    throw new UnsupportedOperationException("cannot change rewrite method");
-  }
-
-  @Override
-  protected short computeMaxShift() {
-    final short shiftFactor;
-
-    // compute diagonal radius
-    double midLon = (minLon + maxLon) * 0.5;
-    double midLat = (minLat + maxLat) * 0.5;
-
-    if (SloppyMath.haversin(minLat, minLon, midLat, midLon)*1000 > 1000000) {
-      shiftFactor = 5;
-    } else {
-      shiftFactor = 4;
-    }
-
-    return (short)(GeoPointField.PRECISION_STEP * shiftFactor);
-  }
-
-  @Override
-  protected CellComparator newCellComparator() {
-    return new GeoPointInBBoxCellComparator(this);
-  }
-
-  private final class GeoPointInBBoxCellComparator extends CellComparator {
-    GeoPointInBBoxCellComparator(GeoPointMultiTermQuery query) {
-      super(query);
-    }
-
-    /**
-     * Determine whether the quad-cell crosses the shape
-     */
-    @Override
-    protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return GeoRelationUtils.rectCrosses(minLon, minLat, maxLon, maxLat, GeoPointInBBoxQueryImpl.this.minLon,
-          GeoPointInBBoxQueryImpl.this.minLat, GeoPointInBBoxQueryImpl.this.maxLon, GeoPointInBBoxQueryImpl.this.maxLat);    }
-
-    /**
-     * Determine whether quad-cell is within the shape
-     */
-    @Override
-    protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return GeoRelationUtils.rectWithin(minLon, minLat, maxLon, maxLat, GeoPointInBBoxQueryImpl.this.minLon,
-          GeoPointInBBoxQueryImpl.this.minLat, GeoPointInBBoxQueryImpl.this.maxLon, GeoPointInBBoxQueryImpl.this.maxLat);    }
-
-    @Override
-    protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return cellIntersectsMBR(minLon, minLat, maxLon, maxLat);
-    }
-
-    @Override
-    protected boolean postFilter(final double lon, final double lat) {
-      return GeoRelationUtils.pointInRectPrecise(lon, lat, minLon, minLat, maxLon, maxLat);
-    }
-  }
-
-  @Override
-  @SuppressWarnings({"unchecked","rawtypes"})
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-    if (!super.equals(o)) return false;
-
-    GeoPointInBBoxQueryImpl that = (GeoPointInBBoxQueryImpl) o;
-
-    if (Double.compare(that.maxLat, maxLat) != 0) return false;
-    if (Double.compare(that.maxLon, maxLon) != 0) return false;
-    if (Double.compare(that.minLat, minLat) != 0) return false;
-    if (Double.compare(that.minLon, minLon) != 0) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    long temp;
-    temp = Double.doubleToLongBits(minLon);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(minLat);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(maxLon);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    temp = Double.doubleToLongBits(maxLat);
-    result = 31 * result + (int) (temp ^ (temp >>> 32));
-    return result;
-  }
-
-  @Override
-  public String toString(String field) {
-    final StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(':');
-    if (!getField().equals(field)) {
-      sb.append(" field=");
-      sb.append(getField());
-      sb.append(':');
-    }
-    return sb.append(" Lower Left: [")
-        .append(minLon)
-        .append(',')
-        .append(minLat)
-        .append(']')
-        .append(" Upper Right: [")
-        .append(maxLon)
-        .append(',')
-        .append(maxLat)
-        .append("]")
-        .toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.java
deleted file mode 100644
index eb8c50c..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQuery.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.spatial.search;
-
-import java.util.Arrays;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-import org.apache.lucene.spatial.util.GeoEncodingUtils;
-import org.apache.lucene.spatial.util.GeoRect;
-import org.apache.lucene.spatial.util.GeoUtils;
-
-/** Implements a simple point in polygon query on a GeoPoint field. This is based on
- * {@code GeoPointInBBoxQueryImpl} and is implemented using a
- * three phase approach. First, like {@code GeoPointInBBoxQueryImpl}
- * candidate terms are queried using a numeric range based on the morton codes
- * of the min and max lat/lon pairs. Terms passing this initial filter are passed
- * to a secondary filter that verifies whether the decoded lat/lon point falls within
- * (or on the boundary) of the bounding box query. Finally, the remaining candidate
- * term is passed to the final point in polygon check. All value comparisons are subject
- * to the same precision tolerance defined in {@value GeoEncodingUtils#TOLERANCE}
- *
- * <p>NOTES:
- *    1.  The polygon coordinates need to be in either clockwise or counter-clockwise order.
- *    2.  The polygon must not be self-crossing, otherwise the query may result in unexpected behavior
- *    3.  All latitude/longitude values must be in decimal degrees.
- *    4.  Complex computational geometry (e.g., dateline wrapping, polygon with holes) is not supported
- *    5.  For more advanced GeoSpatial indexing and query operations see spatial module
- *
- * @lucene.experimental
- */
-public final class GeoPointInPolygonQuery extends GeoPointInBBoxQuery {
-  // polygon position arrays - this avoids the use of any objects or
-  // or geo library dependencies
-  protected final double[] x;
-  protected final double[] y;
-
-  public GeoPointInPolygonQuery(final String field, final double[] polyLons, final double[] polyLats) {
-    this(field, TermEncoding.PREFIX, GeoUtils.polyToBBox(polyLons, polyLats), polyLons, polyLats);
-  }
-
-  /**
-   * Constructs a new GeoPolygonQuery that will match encoded {@link org.apache.lucene.spatial.document.GeoPointField} terms
-   * that fall within or on the boundary of the polygon defined by the input parameters.
-   */
-  public GeoPointInPolygonQuery(final String field, final TermEncoding termEncoding, final double[] polyLons, final double[] polyLats) {
-    this(field, termEncoding, GeoUtils.polyToBBox(polyLons, polyLats), polyLons, polyLats);
-  }
-
-  /** Common constructor, used only internally. */
-  private GeoPointInPolygonQuery(final String field, TermEncoding termEncoding, GeoRect bbox, final double[] polyLons, final double[] polyLats) {
-    super(field, termEncoding, bbox.minLon, bbox.minLat, bbox.maxLon, bbox.maxLat);
-    if (polyLats.length != polyLons.length) {
-      throw new IllegalArgumentException("polyLats and polyLons must be equal length");
-    }
-    if (polyLats.length < 4) {
-      throw new IllegalArgumentException("at least 4 polygon points required");
-    }
-    if (polyLats[0] != polyLats[polyLats.length-1]) {
-      throw new IllegalArgumentException("first and last points of the polygon must be the same (it must close itself): polyLats[0]=" + polyLats[0] + " polyLats[" + (polyLats.length-1) + "]=" + polyLats[polyLats.length-1]);
-    }
-    if (polyLons[0] != polyLons[polyLons.length-1]) {
-      throw new IllegalArgumentException("first and last points of the polygon must be the same (it must close itself): polyLons[0]=" + polyLons[0] + " polyLons[" + (polyLons.length-1) + "]=" + polyLons[polyLons.length-1]);
-    }
-
-    this.x = polyLons;
-    this.y = polyLats;
-  }
-
-  /** throw exception if trying to change rewrite method */
-  @Override
-  public Query rewrite(IndexReader reader) {
-    return new GeoPointInPolygonQueryImpl(field, termEncoding, this, this.minLon, this.minLat, this.maxLon, this.maxLat);
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-    if (!super.equals(o)) return false;
-
-    GeoPointInPolygonQuery that = (GeoPointInPolygonQuery) o;
-
-    if (!Arrays.equals(x, that.x)) return false;
-    if (!Arrays.equals(y, that.y)) return false;
-
-    return true;
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    result = 31 * result + (x != null ? Arrays.hashCode(x) : 0);
-    result = 31 * result + (y != null ? Arrays.hashCode(y) : 0);
-    return result;
-  }
-
-  /** print out this polygon query */
-  @Override
-  public String toString(String field) {
-    assert x.length == y.length;
-
-    final StringBuilder sb = new StringBuilder();
-    sb.append(getClass().getSimpleName());
-    sb.append(':');
-    if (!getField().equals(field)) {
-      sb.append(" field=");
-      sb.append(getField());
-      sb.append(':');
-    }
-    sb.append(" Points: ");
-    for (int i=0; i<x.length; ++i) {
-      sb.append("[")
-          .append(x[i])
-          .append(", ")
-          .append(y[i])
-          .append("] ");
-    }
-
-    return sb.toString();
-  }
-
-  /**
-   * API utility method for returning the array of longitudinal values for this GeoPolygon
-   * The returned array is not a copy so do not change it!
-   */
-  public double[] getLons() {
-    return this.x;
-  }
-
-  /**
-   * API utility method for returning the array of latitudinal values for this GeoPolygon
-   * The returned array is not a copy so do not change it!
-   */
-  public double[] getLats() {
-    return this.y;
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java
deleted file mode 100644
index f0fe87b..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/search/GeoPointInPolygonQueryImpl.java
+++ /dev/null
@@ -1,103 +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.spatial.search;
-
-import org.apache.lucene.search.MultiTermQuery;
-import org.apache.lucene.spatial.document.GeoPointField.TermEncoding;
-import org.apache.lucene.spatial.util.GeoRelationUtils;
-
-/** Package private implementation for the public facing GeoPointInPolygonQuery delegate class.
- *
- *    @lucene.experimental
- */
-final class GeoPointInPolygonQueryImpl extends GeoPointInBBoxQueryImpl {
-  private final GeoPointInPolygonQuery polygonQuery;
-
-  GeoPointInPolygonQueryImpl(final String field, final TermEncoding termEncoding, final GeoPointInPolygonQuery q,
-                             final double minLon, final double minLat, final double maxLon, final double maxLat) {
-    super(field, termEncoding, minLon, minLat, maxLon, maxLat);
-    polygonQuery = q;
-  }
-
-  @Override
-  public void setRewriteMethod(MultiTermQuery.RewriteMethod method) {
-    throw new UnsupportedOperationException("cannot change rewrite method");
-  }
-
-  @Override
-  protected CellComparator newCellComparator() {
-    return new GeoPolygonCellComparator(this);
-  }
-
-  /**
-   * Custom {@code org.apache.lucene.spatial.search.GeoPointMultiTermQuery.CellComparator} that computes morton hash
-   * ranges based on the defined edges of the provided polygon.
-   */
-  private final class GeoPolygonCellComparator extends CellComparator {
-    GeoPolygonCellComparator(GeoPointMultiTermQuery query) {
-      super(query);
-    }
-
-    @Override
-    protected boolean cellCrosses(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return GeoRelationUtils.rectCrossesPolyApprox(minLon, minLat, maxLon, maxLat, polygonQuery.x, polygonQuery.y,
-          polygonQuery.minLon, polygonQuery.minLat, polygonQuery.maxLon, polygonQuery.maxLat);
-    }
-
-    @Override
-    protected boolean cellWithin(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return GeoRelationUtils.rectWithinPolyApprox(minLon, minLat, maxLon, maxLat, polygonQuery.x, polygonQuery.y,
-          polygonQuery.minLon, polygonQuery.minLat, polygonQuery.maxLon, polygonQuery.maxLat);
-    }
-
-    @Override
-    protected boolean cellIntersectsShape(final double minLon, final double minLat, final double maxLon, final double maxLat) {
-      return cellContains(minLon, minLat, maxLon, maxLat) || cellWithin(minLon, minLat, maxLon, maxLat)
-          || cellCrosses(minLon, minLat, maxLon, maxLat);
-    }
-
-    /**
-     * The two-phase query approach. The parent
-     * {@link org.apache.lucene.spatial.search.GeoPointTermsEnum#accept} method is called to match
-     * encoded terms that fall within the bounding box of the polygon. Those documents that pass the initial
-     * bounding box filter are then compared to the provided polygon using the
-     * {@link org.apache.lucene.spatial.util.GeoRelationUtils#pointInPolygon} method.
-     */
-    @Override
-    protected boolean postFilter(final double lon, final double lat) {
-      return GeoRelationUtils.pointInPolygon(polygonQuery.x, polygonQuery.y, lat, lon);
-    }
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-    if (!super.equals(o)) return false;
-
-    GeoPointInPolygonQueryImpl that = (GeoPointInPolygonQueryImpl) o;
-
-    return !(polygonQuery != null ? !polygonQuery.equals(that.polygonQuery) : that.polygonQuery != null);
-  }
-
-  @Override
-  public int hashCode() {
-    int result = super.hashCode();
-    result = 31 * result + (polygonQuery != null ? polygonQuery.hashCode() : 0);
-    return result;
-  }
-}


Mime
View raw message