You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by nk...@apache.org on 2016/02/10 04:51:56 UTC

[3/3] lucene-solr git commit: LUCENE-6997: refactor GeoPointField and query classes from lucene.spatial to lucene.spatial.geopoint package

LUCENE-6997: refactor GeoPointField and query classes from lucene.spatial to lucene.spatial.geopoint package


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/7d8f0127
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/7d8f0127
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/7d8f0127

Branch: refs/heads/master
Commit: 7d8f0127b7e8514b19c2898dbaac2d1419964b1b
Parents: b889109
Author: nknize <nk...@apache.org>
Authored: Tue Feb 9 21:51:30 2016 -0600
Committer: nknize <nk...@apache.org>
Committed: Tue Feb 9 21:51:48 2016 -0600

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |   2 +-
 .../lucene/spatial/document/GeoPointField.java  | 250 ------------
 .../spatial/document/GeoPointTokenStream.java   | 233 -----------
 .../lucene/spatial/document/package-info.java   |  21 -
 .../geopoint/document/GeoPointField.java        | 250 ++++++++++++
 .../geopoint/document/GeoPointTokenStream.java  | 233 +++++++++++
 .../spatial/geopoint/document/package-info.java |  21 +
 .../spatial/geopoint/search/GeoBoundingBox.java |  53 +++
 .../geopoint/search/GeoPointDistanceQuery.java  | 189 +++++++++
 .../search/GeoPointDistanceQueryImpl.java       | 107 +++++
 .../search/GeoPointDistanceRangeQuery.java      | 116 ++++++
 .../geopoint/search/GeoPointInBBoxQuery.java    | 173 ++++++++
 .../search/GeoPointInBBoxQueryImpl.java         | 156 +++++++
 .../geopoint/search/GeoPointInPolygonQuery.java | 153 +++++++
 .../search/GeoPointInPolygonQueryImpl.java      | 103 +++++
 .../geopoint/search/GeoPointMultiTermQuery.java | 166 ++++++++
 .../search/GeoPointNumericTermsEnum.java        | 161 ++++++++
 .../search/GeoPointPrefixTermsEnum.java         | 237 +++++++++++
 .../GeoPointTermQueryConstantScoreWrapper.java  | 153 +++++++
 .../geopoint/search/GeoPointTermsEnum.java      | 140 +++++++
 .../spatial/geopoint/search/package-info.java   |  21 +
 .../lucene/spatial/search/GeoBoundingBox.java   |  53 ---
 .../spatial/search/GeoPointDistanceQuery.java   | 189 ---------
 .../search/GeoPointDistanceQueryImpl.java       | 107 -----
 .../search/GeoPointDistanceRangeQuery.java      | 116 ------
 .../spatial/search/GeoPointInBBoxQuery.java     | 173 --------
 .../spatial/search/GeoPointInBBoxQueryImpl.java | 156 -------
 .../spatial/search/GeoPointInPolygonQuery.java  | 153 -------
 .../search/GeoPointInPolygonQueryImpl.java      | 103 -----
 .../spatial/search/GeoPointMultiTermQuery.java  | 166 --------
 .../search/GeoPointNumericTermsEnum.java        | 161 --------
 .../spatial/search/GeoPointPrefixTermsEnum.java | 237 -----------
 .../GeoPointTermQueryConstantScoreWrapper.java  | 153 -------
 .../spatial/search/GeoPointTermsEnum.java       | 140 -------
 .../lucene/spatial/search/package-info.java     |  21 -
 .../geopoint/search/TestGeoPointQuery.java      | 404 +++++++++++++++++++
 .../spatial/search/TestGeoPointQuery.java       | 404 -------------------
 .../spatial/util/BaseGeoPointTestCase.java      |   1 -
 38 files changed, 2837 insertions(+), 2838 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index e86b0ae..54124df 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -149,7 +149,7 @@ New Features
 API Changes
 
 * LUCENE-6997: refactor sandboxed GeoPointField and query classes to lucene-spatial 
-  module (Nick Knize)
+  module under new lucene.spatial.geopoint package (Nick Knize)
 
 * LUCENE-6908: GeoUtils static relational methods have been refactored to new 
   GeoRelationUtils and now correctly handle large irregular rectangles, and

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
deleted file mode 100644
index cafa67c..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointField.java
+++ /dev/null
@@ -1,250 +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.document;
-
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.FieldType;
-import org.apache.lucene.index.DocValuesType;
-import org.apache.lucene.index.IndexOptions;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.TokenStream;
-import org.apache.lucene.spatial.util.GeoEncodingUtils;
-
-/**
- * <p>
- * Field that indexes <code>latitude</code> <code>longitude</code> decimal-degree values
- * for efficient encoding, sorting, and querying. This Geo capability is intended
- * to provide a basic and efficient out of the box field type for indexing and
- * querying 2 dimensional points in WGS-84 decimal degrees. An example usage is as follows:
- *
- * <pre class="prettyprint">
- *  document.add(new GeoPointField(name, -96.33, 32.66, Field.Store.NO));
- * </pre>
- *
- * <p>To perform simple geospatial queries against a <code>GeoPointField</code>,
- * see {@link org.apache.lucene.spatial.search.GeoPointInBBoxQuery}, {@link org.apache.lucene.spatial.search.GeoPointInPolygonQuery},
- * or {@link org.apache.lucene.spatial.search.GeoPointDistanceQuery}
- *
- * NOTE: This indexes only high precision encoded terms which may result in visiting a high number
- * of terms for large queries. See LUCENE-6481 for a future improvement.
- *
- * @lucene.experimental
- */
-public final class GeoPointField extends Field {
-  /** encoding step value for GeoPoint prefix terms */
-  public static final int PRECISION_STEP = 9;
-
-  /**
-   * <b>Expert:</b> Optional flag to select term encoding for GeoPointField types
-   */
-  public enum TermEncoding {
-    /**
-     * encodes prefix terms only resulting in a small index and faster queries - use with
-     * {@code GeoPointTokenStream}
-     */
-    PREFIX,
-    /**
-     * @deprecated encodes prefix and full resolution terms - use with
-     * {@link org.apache.lucene.analysis.LegacyNumericTokenStream}
-     */
-    @Deprecated
-    NUMERIC
-  }
-
-  /**
-   * @deprecated Type for a GeoPointField that is not stored:
-   * normalization factors, frequencies, and positions are omitted.
-   */
-  @Deprecated
-  public static final FieldType NUMERIC_TYPE_NOT_STORED = new FieldType();
-  static {
-    NUMERIC_TYPE_NOT_STORED.setTokenized(false);
-    NUMERIC_TYPE_NOT_STORED.setOmitNorms(true);
-    NUMERIC_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
-    NUMERIC_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
-    NUMERIC_TYPE_NOT_STORED.setNumericType(FieldType.LegacyNumericType.LONG);
-    NUMERIC_TYPE_NOT_STORED.setNumericPrecisionStep(PRECISION_STEP);
-    NUMERIC_TYPE_NOT_STORED.freeze();
-  }
-
-  /**
-   * @deprecated Type for a stored GeoPointField:
-   * normalization factors, frequencies, and positions are omitted.
-   */
-  @Deprecated
-  public static final FieldType NUMERIC_TYPE_STORED = new FieldType();
-  static {
-    NUMERIC_TYPE_STORED.setTokenized(false);
-    NUMERIC_TYPE_STORED.setOmitNorms(true);
-    NUMERIC_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
-    NUMERIC_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
-    NUMERIC_TYPE_STORED.setNumericType(FieldType.LegacyNumericType.LONG);
-    NUMERIC_TYPE_STORED.setNumericPrecisionStep(PRECISION_STEP);
-    NUMERIC_TYPE_STORED.setStored(true);
-    NUMERIC_TYPE_STORED.freeze();
-  }
-
-  /**
-   * Type for a GeoPointField that is not stored:
-   * normalization factors, frequencies, and positions are omitted.
-   */
-  public static final FieldType PREFIX_TYPE_NOT_STORED = new FieldType();
-  static {
-    PREFIX_TYPE_NOT_STORED.setTokenized(false);
-    PREFIX_TYPE_NOT_STORED.setOmitNorms(true);
-    PREFIX_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
-    PREFIX_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
-    PREFIX_TYPE_NOT_STORED.freeze();
-  }
-
-  /**
-   * Type for a stored GeoPointField:
-   * normalization factors, frequencies, and positions are omitted.
-   */
-  public static final FieldType PREFIX_TYPE_STORED = new FieldType();
-  static {
-    PREFIX_TYPE_STORED.setTokenized(false);
-    PREFIX_TYPE_STORED.setOmitNorms(true);
-    PREFIX_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
-    PREFIX_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
-    PREFIX_TYPE_STORED.setStored(true);
-    PREFIX_TYPE_STORED.freeze();
-  }
-
-  /** Creates a stored or un-stored GeoPointField
-   *  @param name field name
-   *  @param lon longitude double value [-180.0 : 180.0]
-   *  @param lat latitude double value [-90.0 : 90.0]
-   *  @param stored Store.YES if the content should also be stored
-   *  @throws IllegalArgumentException if the field name is null.
-   */
-  public GeoPointField(String name, double lon, double lat, Store stored) {
-    this(name, lon, lat, getFieldType(stored));
-  }
-
-  /** Creates a stored or un-stored GeoPointField using the specified {@link TermEncoding} method
-   *  @param name field name
-   *  @param lon longitude double value [-180.0 : 180.0]
-   *  @param lat latitude double value [-90.0 : 90.0]
-   *  @param termEncoding encoding type to use ({@link TermEncoding#NUMERIC} Terms, or {@link TermEncoding#PREFIX} only Terms)
-   *  @param stored Store.YES if the content should also be stored
-   *  @throws IllegalArgumentException if the field name is null.
-   */
-  @Deprecated
-  public GeoPointField(String name, double lon, double lat, TermEncoding termEncoding, Store stored) {
-    this(name, lon, lat, getFieldType(termEncoding, stored));
-  }
-
-  /** Expert: allows you to customize the {@link
-   *  FieldType}.
-   *  @param name field name
-   *  @param lon longitude double value [-180.0 : 180.0]
-   *  @param lat latitude double value [-90.0 : 90.0]
-   *  @param type customized field type: must have {@link FieldType#numericType()}
-   *         of {@link org.apache.lucene.document.FieldType.LegacyNumericType#LONG}.
-   *  @throws IllegalArgumentException if the field name or type is null, or
-   *          if the field type does not have a LONG numericType()
-   */
-  public GeoPointField(String name, double lon, double lat, FieldType type) {
-    super(name, type);
-
-    // field must be indexed
-    // todo does it make sense here to provide the ability to store a GeoPointField but not index?
-    if (type.indexOptions() == IndexOptions.NONE && type.stored() == false) {
-      throw new IllegalArgumentException("type.indexOptions() is set to NONE but type.stored() is false");
-    } else if (type.indexOptions() == IndexOptions.DOCS) {
-      if (type.docValuesType() != DocValuesType.SORTED_NUMERIC) {
-        throw new IllegalArgumentException("type.docValuesType() must be SORTED_NUMERIC but got " + type.docValuesType());
-      }
-      if (type.numericType() != null) {
-        // make sure numericType is a LONG
-        if (type.numericType() != FieldType.LegacyNumericType.LONG) {
-          throw new IllegalArgumentException("type.numericType() must be LONG but got " + type.numericType());
-        }
-      }
-    } else {
-      throw new IllegalArgumentException("type.indexOptions() must be one of NONE or DOCS but got " + type.indexOptions());
-    }
-
-    // set field data
-    fieldsData = GeoEncodingUtils.mortonHash(lon, lat);
-  }
-
-  private static FieldType getFieldType(Store stored) {
-    return getFieldType(TermEncoding.PREFIX, stored);
-  }
-
-  /**
-   * @deprecated
-   * Static helper method for returning a valid FieldType based on termEncoding and stored options
-   */
-  @Deprecated
-  private static FieldType getFieldType(TermEncoding termEncoding, Store stored) {
-    if (stored == Store.YES) {
-      return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_STORED : NUMERIC_TYPE_STORED;
-    } else if (stored == Store.NO) {
-      return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_NOT_STORED : NUMERIC_TYPE_NOT_STORED;
-    } else {
-      throw new IllegalArgumentException("stored option must be NO or YES but got " + stored);
-    }
-  }
-
-  @Override
-  public TokenStream tokenStream(Analyzer analyzer, TokenStream reuse) {
-    if (fieldType().indexOptions() == IndexOptions.NONE) {
-      // not indexed
-      return null;
-    }
-
-    // if numericType is set
-    if (type.numericType() != null) {
-      // return numeric encoding
-      return super.tokenStream(analyzer, reuse);
-    }
-
-    if (reuse instanceof GeoPointTokenStream == false) {
-      reuse = new GeoPointTokenStream();
-    }
-
-    final GeoPointTokenStream gpts = (GeoPointTokenStream)reuse;
-    gpts.setGeoCode(((Number) fieldsData).longValue());
-
-    return reuse;
-  }
-
-  /** access longitude value */
-  public double getLon() {
-    return GeoEncodingUtils.mortonUnhashLon((long) fieldsData);
-  }
-
-  /** access latitude value */
-  public double getLat() {
-    return GeoEncodingUtils.mortonUnhashLat((long) fieldsData);
-  }
-
-  @Override
-  public String toString() {
-    if (fieldsData == null) {
-      return null;
-    }
-    StringBuilder sb = new StringBuilder();
-    sb.append(GeoEncodingUtils.mortonUnhashLon((long) fieldsData));
-    sb.append(',');
-    sb.append(GeoEncodingUtils.mortonUnhashLat((long) fieldsData));
-    return sb.toString();
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
deleted file mode 100644
index ff97fee..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/GeoPointTokenStream.java
+++ /dev/null
@@ -1,233 +0,0 @@
-package org.apache.lucene.spatial.document;
-
-/*
- * 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.Objects;
-
-import org.apache.lucene.analysis.TokenStream;
-import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
-import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
-import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
-import org.apache.lucene.util.Attribute;
-import org.apache.lucene.util.AttributeFactory;
-import org.apache.lucene.util.AttributeImpl;
-import org.apache.lucene.util.AttributeReflector;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.BytesRefBuilder;
-import org.apache.lucene.spatial.util.GeoEncodingUtils;
-
-import static org.apache.lucene.spatial.document.GeoPointField.PRECISION_STEP;
-
-/**
- * <b>Expert:</b> This class provides a {@link TokenStream} used by {@link GeoPointField}
- * for encoding {@link GeoPointField.TermEncoding#PREFIX} only GeoPointTerms.
- *
- * <p><i>NOTE: This is used as the default encoding unless
- * {@code GeoPointField.setNumericType(FieldType.LegacyNumericType.LONG)} is set</i></p>
- *
- * This class is similar to {@link org.apache.lucene.analysis.LegacyNumericTokenStream} but encodes terms up to a
- * a maximum of {@link #MAX_SHIFT} using a fixed precision step defined by
- * {@link GeoPointField#PRECISION_STEP}. This yields a total of 4 terms per GeoPoint
- * each consisting of 5 bytes (4 prefix bytes + 1 precision byte).
- *
- * <p>For best performance use the provided {@link GeoPointField#PREFIX_TYPE_NOT_STORED} or
- * {@link GeoPointField#PREFIX_TYPE_STORED}</p>
- *
- * <p>If prefix terms are used then the default GeoPoint query constructors may be used, but if
- * {@link org.apache.lucene.analysis.LegacyNumericTokenStream} is used, then be sure to pass
- * {@link GeoPointField.TermEncoding#NUMERIC} to all GeoPointQuery constructors</p>
- *
- * Here's an example usage:
- *
- * <pre class="prettyprint">
- *   // using prefix terms
- *   GeoPointField geoPointField = new GeoPointField(fieldName1, lon, lat, GeoPointField.PREFIX_TYPE_NOT_STORED);
- *   document.add(geoPointField);
- *
- *   // query by bounding box (default uses TermEncoding.PREFIX)
- *   Query q = new GeoPointInBBoxQuery(fieldName1, minLon, minLat, maxLon, maxLat);
- *
- *   // using numeric terms
- *   geoPointField = new GeoPointField(fieldName2, lon, lat, GeoPointField.NUMERIC_TYPE_NOT_STORED);
- *   document.add(geoPointField);
- *
- *   // query by distance (requires TermEncoding.NUMERIC)
- *   q = new GeoPointDistanceQuery(fieldName2, TermEncoding.NUMERIC, centerLon, centerLat, radiusMeters);
- * </pre>
- *
- * @lucene.experimental
- */
-final class GeoPointTokenStream extends TokenStream {
-  private static final int MAX_SHIFT = PRECISION_STEP * 4;
-
-  private final GeoPointTermAttribute geoPointTermAtt = addAttribute(GeoPointTermAttribute.class);
-  private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
-
-  private boolean isInit = false;
-
-  /**
-   * Expert: Creates a token stream for geo point fields with the specified
-   * <code>precisionStep</code> using the given
-   * {@link org.apache.lucene.util.AttributeFactory}.
-   * The stream is not yet initialized,
-   * before using set a value using the various setGeoCode method.
-   */
-  public GeoPointTokenStream() {
-    super(new GeoPointAttributeFactory(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY));
-    assert PRECISION_STEP > 0;
-  }
-
-  public GeoPointTokenStream setGeoCode(final long geoCode) {
-    geoPointTermAtt.init(geoCode, MAX_SHIFT-PRECISION_STEP);
-    isInit = true;
-    return this;
-  }
-
-  @Override
-  public void reset() {
-    if (isInit == false) {
-      throw new IllegalStateException("call setGeoCode() before usage");
-    }
-  }
-
-  @Override
-  public boolean incrementToken() {
-    if (isInit == false) {
-      throw new IllegalStateException("call setGeoCode() before usage");
-    }
-
-    // this will only clear all other attributes in this TokenStream
-    clearAttributes();
-
-    final int shift = geoPointTermAtt.incShift();
-    posIncrAtt.setPositionIncrement((shift == MAX_SHIFT) ? 1 : 0);
-    return (shift < 63);
-  }
-
-  /**
-   * Tracks shift values during encoding
-   */
-  public interface GeoPointTermAttribute extends Attribute {
-    /** Returns current shift value, undefined before first token */
-    int getShift();
-
-    /** <em>Don't call this method!</em>
-     * @lucene.internal */
-    void init(long value, int shift);
-
-    /** <em>Don't call this method!</em>
-     * @lucene.internal */
-    int incShift();
-  }
-
-  // just a wrapper to prevent adding CTA
-  private static final class GeoPointAttributeFactory extends AttributeFactory {
-    private final AttributeFactory delegate;
-
-    GeoPointAttributeFactory(AttributeFactory delegate) {
-      this.delegate = delegate;
-    }
-
-    @Override
-    public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
-      if (CharTermAttribute.class.isAssignableFrom(attClass)) {
-        throw new IllegalArgumentException("GeoPointTokenStream does not support CharTermAttribute.");
-      }
-      return delegate.createAttributeInstance(attClass);
-    }
-  }
-
-  public static final class GeoPointTermAttributeImpl extends AttributeImpl implements GeoPointTermAttribute,TermToBytesRefAttribute {
-    private long value = 0L;
-    private int shift = 0;
-    private BytesRefBuilder bytes = new BytesRefBuilder();
-
-    public GeoPointTermAttributeImpl() {
-      this.shift = MAX_SHIFT-PRECISION_STEP;
-    }
-
-    @Override
-    public BytesRef getBytesRef() {
-      GeoEncodingUtils.geoCodedToPrefixCoded(value, shift, bytes);
-      return bytes.get();
-    }
-
-    @Override
-    public void init(long value, int shift) {
-      this.value = value;
-      this.shift = shift;
-    }
-
-    @Override
-    public int getShift() { return shift; }
-
-    @Override
-    public int incShift() {
-      return (shift += PRECISION_STEP);
-    }
-
-    @Override
-    public void clear() {
-      // this attribute has no contents to clear!
-      // we keep it untouched as it's fully controlled by outer class.
-    }
-
-    @Override
-    public void reflectWith(AttributeReflector reflector) {
-      reflector.reflect(TermToBytesRefAttribute.class, "bytes", getBytesRef());
-      reflector.reflect(GeoPointTermAttribute.class, "shift", shift);
-    }
-
-    @Override
-    public void copyTo(AttributeImpl target) {
-      final GeoPointTermAttribute a = (GeoPointTermAttribute) target;
-      a.init(value, shift);
-    }
-
-    @Override
-    public GeoPointTermAttributeImpl clone() {
-      GeoPointTermAttributeImpl t = (GeoPointTermAttributeImpl)super.clone();
-      // Do a deep clone
-      t.bytes = new BytesRefBuilder();
-      t.bytes.copyBytes(getBytesRef());
-      return t;
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(shift, value);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) return true;
-      if (obj == null) return false;
-      if (getClass() != obj.getClass()) return false;
-      GeoPointTermAttributeImpl other = (GeoPointTermAttributeImpl) obj;
-      if (shift != other.shift) return false;
-      if (value != other.value) return false;
-      return true;
-    }
-  }
-
-  /** override toString because it can throw cryptic "illegal shift value": */
-  @Override
-  public String toString() {
-    return getClass().getSimpleName() + "(precisionStep=" + PRECISION_STEP + " shift=" + geoPointTermAtt.getShift() + ")";
-  }
-}
\ 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/document/package-info.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/document/package-info.java b/lucene/spatial/src/java/org/apache/lucene/spatial/document/package-info.java
deleted file mode 100644
index 2550fa1..0000000
--- a/lucene/spatial/src/java/org/apache/lucene/spatial/document/package-info.java
+++ /dev/null
@@ -1,21 +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.
- */
-
-/**
- * Geospatial Field Implementations for Core Lucene
- */
-package org.apache.lucene.spatial.document;
\ 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/geopoint/document/GeoPointField.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointField.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointField.java
new file mode 100644
index 0000000..39f12df
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointField.java
@@ -0,0 +1,250 @@
+/*
+ * 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.document;
+
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.FieldType;
+import org.apache.lucene.index.DocValuesType;
+import org.apache.lucene.index.IndexOptions;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+
+/**
+ * <p>
+ * Field that indexes <code>latitude</code> <code>longitude</code> decimal-degree values
+ * for efficient encoding, sorting, and querying. This Geo capability is intended
+ * to provide a basic and efficient out of the box field type for indexing and
+ * querying 2 dimensional points in WGS-84 decimal degrees. An example usage is as follows:
+ *
+ * <pre class="prettyprint">
+ *  document.add(new GeoPointField(name, -96.33, 32.66, Field.Store.NO));
+ * </pre>
+ *
+ * <p>To perform simple geospatial queries against a <code>GeoPointField</code>,
+ * see {@link org.apache.lucene.spatial.geopoint.search.GeoPointInBBoxQuery}, {@link org.apache.lucene.spatial.geopoint.search.GeoPointInPolygonQuery},
+ * or {@link org.apache.lucene.spatial.geopoint.search.GeoPointDistanceQuery}
+ *
+ * NOTE: This indexes only high precision encoded terms which may result in visiting a high number
+ * of terms for large queries. See LUCENE-6481 for a future improvement.
+ *
+ * @lucene.experimental
+ */
+public final class GeoPointField extends Field {
+  /** encoding step value for GeoPoint prefix terms */
+  public static final int PRECISION_STEP = 9;
+
+  /**
+   * <b>Expert:</b> Optional flag to select term encoding for GeoPointField types
+   */
+  public enum TermEncoding {
+    /**
+     * encodes prefix terms only resulting in a small index and faster queries - use with
+     * {@code GeoPointTokenStream}
+     */
+    PREFIX,
+    /**
+     * @deprecated encodes prefix and full resolution terms - use with
+     * {@link org.apache.lucene.analysis.LegacyNumericTokenStream}
+     */
+    @Deprecated
+    NUMERIC
+  }
+
+  /**
+   * @deprecated Type for a GeoPointField that is not stored:
+   * normalization factors, frequencies, and positions are omitted.
+   */
+  @Deprecated
+  public static final FieldType NUMERIC_TYPE_NOT_STORED = new FieldType();
+  static {
+    NUMERIC_TYPE_NOT_STORED.setTokenized(false);
+    NUMERIC_TYPE_NOT_STORED.setOmitNorms(true);
+    NUMERIC_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
+    NUMERIC_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+    NUMERIC_TYPE_NOT_STORED.setNumericType(FieldType.LegacyNumericType.LONG);
+    NUMERIC_TYPE_NOT_STORED.setNumericPrecisionStep(PRECISION_STEP);
+    NUMERIC_TYPE_NOT_STORED.freeze();
+  }
+
+  /**
+   * @deprecated Type for a stored GeoPointField:
+   * normalization factors, frequencies, and positions are omitted.
+   */
+  @Deprecated
+  public static final FieldType NUMERIC_TYPE_STORED = new FieldType();
+  static {
+    NUMERIC_TYPE_STORED.setTokenized(false);
+    NUMERIC_TYPE_STORED.setOmitNorms(true);
+    NUMERIC_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
+    NUMERIC_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+    NUMERIC_TYPE_STORED.setNumericType(FieldType.LegacyNumericType.LONG);
+    NUMERIC_TYPE_STORED.setNumericPrecisionStep(PRECISION_STEP);
+    NUMERIC_TYPE_STORED.setStored(true);
+    NUMERIC_TYPE_STORED.freeze();
+  }
+
+  /**
+   * Type for a GeoPointField that is not stored:
+   * normalization factors, frequencies, and positions are omitted.
+   */
+  public static final FieldType PREFIX_TYPE_NOT_STORED = new FieldType();
+  static {
+    PREFIX_TYPE_NOT_STORED.setTokenized(false);
+    PREFIX_TYPE_NOT_STORED.setOmitNorms(true);
+    PREFIX_TYPE_NOT_STORED.setIndexOptions(IndexOptions.DOCS);
+    PREFIX_TYPE_NOT_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+    PREFIX_TYPE_NOT_STORED.freeze();
+  }
+
+  /**
+   * Type for a stored GeoPointField:
+   * normalization factors, frequencies, and positions are omitted.
+   */
+  public static final FieldType PREFIX_TYPE_STORED = new FieldType();
+  static {
+    PREFIX_TYPE_STORED.setTokenized(false);
+    PREFIX_TYPE_STORED.setOmitNorms(true);
+    PREFIX_TYPE_STORED.setIndexOptions(IndexOptions.DOCS);
+    PREFIX_TYPE_STORED.setDocValuesType(DocValuesType.SORTED_NUMERIC);
+    PREFIX_TYPE_STORED.setStored(true);
+    PREFIX_TYPE_STORED.freeze();
+  }
+
+  /** Creates a stored or un-stored GeoPointField
+   *  @param name field name
+   *  @param lon longitude double value [-180.0 : 180.0]
+   *  @param lat latitude double value [-90.0 : 90.0]
+   *  @param stored Store.YES if the content should also be stored
+   *  @throws IllegalArgumentException if the field name is null.
+   */
+  public GeoPointField(String name, double lon, double lat, Store stored) {
+    this(name, lon, lat, getFieldType(stored));
+  }
+
+  /** Creates a stored or un-stored GeoPointField using the specified {@link TermEncoding} method
+   *  @param name field name
+   *  @param lon longitude double value [-180.0 : 180.0]
+   *  @param lat latitude double value [-90.0 : 90.0]
+   *  @param termEncoding encoding type to use ({@link TermEncoding#NUMERIC} Terms, or {@link TermEncoding#PREFIX} only Terms)
+   *  @param stored Store.YES if the content should also be stored
+   *  @throws IllegalArgumentException if the field name is null.
+   */
+  @Deprecated
+  public GeoPointField(String name, double lon, double lat, TermEncoding termEncoding, Store stored) {
+    this(name, lon, lat, getFieldType(termEncoding, stored));
+  }
+
+  /** Expert: allows you to customize the {@link
+   *  FieldType}.
+   *  @param name field name
+   *  @param lon longitude double value [-180.0 : 180.0]
+   *  @param lat latitude double value [-90.0 : 90.0]
+   *  @param type customized field type: must have {@link FieldType#numericType()}
+   *         of {@link org.apache.lucene.document.FieldType.LegacyNumericType#LONG}.
+   *  @throws IllegalArgumentException if the field name or type is null, or
+   *          if the field type does not have a LONG numericType()
+   */
+  public GeoPointField(String name, double lon, double lat, FieldType type) {
+    super(name, type);
+
+    // field must be indexed
+    // todo does it make sense here to provide the ability to store a GeoPointField but not index?
+    if (type.indexOptions() == IndexOptions.NONE && type.stored() == false) {
+      throw new IllegalArgumentException("type.indexOptions() is set to NONE but type.stored() is false");
+    } else if (type.indexOptions() == IndexOptions.DOCS) {
+      if (type.docValuesType() != DocValuesType.SORTED_NUMERIC) {
+        throw new IllegalArgumentException("type.docValuesType() must be SORTED_NUMERIC but got " + type.docValuesType());
+      }
+      if (type.numericType() != null) {
+        // make sure numericType is a LONG
+        if (type.numericType() != FieldType.LegacyNumericType.LONG) {
+          throw new IllegalArgumentException("type.numericType() must be LONG but got " + type.numericType());
+        }
+      }
+    } else {
+      throw new IllegalArgumentException("type.indexOptions() must be one of NONE or DOCS but got " + type.indexOptions());
+    }
+
+    // set field data
+    fieldsData = GeoEncodingUtils.mortonHash(lon, lat);
+  }
+
+  private static FieldType getFieldType(Store stored) {
+    return getFieldType(TermEncoding.PREFIX, stored);
+  }
+
+  /**
+   * @deprecated
+   * Static helper method for returning a valid FieldType based on termEncoding and stored options
+   */
+  @Deprecated
+  private static FieldType getFieldType(TermEncoding termEncoding, Store stored) {
+    if (stored == Store.YES) {
+      return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_STORED : NUMERIC_TYPE_STORED;
+    } else if (stored == Store.NO) {
+      return termEncoding == TermEncoding.PREFIX ? PREFIX_TYPE_NOT_STORED : NUMERIC_TYPE_NOT_STORED;
+    } else {
+      throw new IllegalArgumentException("stored option must be NO or YES but got " + stored);
+    }
+  }
+
+  @Override
+  public TokenStream tokenStream(Analyzer analyzer, TokenStream reuse) {
+    if (fieldType().indexOptions() == IndexOptions.NONE) {
+      // not indexed
+      return null;
+    }
+
+    // if numericType is set
+    if (type.numericType() != null) {
+      // return numeric encoding
+      return super.tokenStream(analyzer, reuse);
+    }
+
+    if (reuse instanceof GeoPointTokenStream == false) {
+      reuse = new GeoPointTokenStream();
+    }
+
+    final GeoPointTokenStream gpts = (GeoPointTokenStream)reuse;
+    gpts.setGeoCode(((Number) fieldsData).longValue());
+
+    return reuse;
+  }
+
+  /** access longitude value */
+  public double getLon() {
+    return GeoEncodingUtils.mortonUnhashLon((long) fieldsData);
+  }
+
+  /** access latitude value */
+  public double getLat() {
+    return GeoEncodingUtils.mortonUnhashLat((long) fieldsData);
+  }
+
+  @Override
+  public String toString() {
+    if (fieldsData == null) {
+      return null;
+    }
+    StringBuilder sb = new StringBuilder();
+    sb.append(GeoEncodingUtils.mortonUnhashLon((long) fieldsData));
+    sb.append(',');
+    sb.append(GeoEncodingUtils.mortonUnhashLat((long) fieldsData));
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/7d8f0127/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointTokenStream.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointTokenStream.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointTokenStream.java
new file mode 100644
index 0000000..4a70ea3
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/GeoPointTokenStream.java
@@ -0,0 +1,233 @@
+package org.apache.lucene.spatial.geopoint.document;
+
+/*
+ * 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.Objects;
+
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
+import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
+import org.apache.lucene.util.Attribute;
+import org.apache.lucene.util.AttributeFactory;
+import org.apache.lucene.util.AttributeImpl;
+import org.apache.lucene.util.AttributeReflector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.BytesRefBuilder;
+import org.apache.lucene.spatial.util.GeoEncodingUtils;
+
+import static org.apache.lucene.spatial.geopoint.document.GeoPointField.PRECISION_STEP;
+
+/**
+ * <b>Expert:</b> This class provides a {@link TokenStream} used by {@link GeoPointField}
+ * for encoding {@link GeoPointField.TermEncoding#PREFIX} only GeoPointTerms.
+ *
+ * <p><i>NOTE: This is used as the default encoding unless
+ * {@code GeoPointField.setNumericType(FieldType.LegacyNumericType.LONG)} is set</i></p>
+ *
+ * This class is similar to {@link org.apache.lucene.analysis.LegacyNumericTokenStream} but encodes terms up to a
+ * a maximum of {@link #MAX_SHIFT} using a fixed precision step defined by
+ * {@link GeoPointField#PRECISION_STEP}. This yields a total of 4 terms per GeoPoint
+ * each consisting of 5 bytes (4 prefix bytes + 1 precision byte).
+ *
+ * <p>For best performance use the provided {@link GeoPointField#PREFIX_TYPE_NOT_STORED} or
+ * {@link GeoPointField#PREFIX_TYPE_STORED}</p>
+ *
+ * <p>If prefix terms are used then the default GeoPoint query constructors may be used, but if
+ * {@link org.apache.lucene.analysis.LegacyNumericTokenStream} is used, then be sure to pass
+ * {@link GeoPointField.TermEncoding#NUMERIC} to all GeoPointQuery constructors</p>
+ *
+ * Here's an example usage:
+ *
+ * <pre class="prettyprint">
+ *   // using prefix terms
+ *   GeoPointField geoPointField = new GeoPointField(fieldName1, lon, lat, GeoPointField.PREFIX_TYPE_NOT_STORED);
+ *   document.add(geoPointField);
+ *
+ *   // query by bounding box (default uses TermEncoding.PREFIX)
+ *   Query q = new GeoPointInBBoxQuery(fieldName1, minLon, minLat, maxLon, maxLat);
+ *
+ *   // using numeric terms
+ *   geoPointField = new GeoPointField(fieldName2, lon, lat, GeoPointField.NUMERIC_TYPE_NOT_STORED);
+ *   document.add(geoPointField);
+ *
+ *   // query by distance (requires TermEncoding.NUMERIC)
+ *   q = new GeoPointDistanceQuery(fieldName2, TermEncoding.NUMERIC, centerLon, centerLat, radiusMeters);
+ * </pre>
+ *
+ * @lucene.experimental
+ */
+final class GeoPointTokenStream extends TokenStream {
+  private static final int MAX_SHIFT = PRECISION_STEP * 4;
+
+  private final GeoPointTermAttribute geoPointTermAtt = addAttribute(GeoPointTermAttribute.class);
+  private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
+
+  private boolean isInit = false;
+
+  /**
+   * Expert: Creates a token stream for geo point fields with the specified
+   * <code>precisionStep</code> using the given
+   * {@link org.apache.lucene.util.AttributeFactory}.
+   * The stream is not yet initialized,
+   * before using set a value using the various setGeoCode method.
+   */
+  public GeoPointTokenStream() {
+    super(new GeoPointAttributeFactory(AttributeFactory.DEFAULT_ATTRIBUTE_FACTORY));
+    assert PRECISION_STEP > 0;
+  }
+
+  public GeoPointTokenStream setGeoCode(final long geoCode) {
+    geoPointTermAtt.init(geoCode, MAX_SHIFT-PRECISION_STEP);
+    isInit = true;
+    return this;
+  }
+
+  @Override
+  public void reset() {
+    if (isInit == false) {
+      throw new IllegalStateException("call setGeoCode() before usage");
+    }
+  }
+
+  @Override
+  public boolean incrementToken() {
+    if (isInit == false) {
+      throw new IllegalStateException("call setGeoCode() before usage");
+    }
+
+    // this will only clear all other attributes in this TokenStream
+    clearAttributes();
+
+    final int shift = geoPointTermAtt.incShift();
+    posIncrAtt.setPositionIncrement((shift == MAX_SHIFT) ? 1 : 0);
+    return (shift < 63);
+  }
+
+  /**
+   * Tracks shift values during encoding
+   */
+  public interface GeoPointTermAttribute extends Attribute {
+    /** Returns current shift value, undefined before first token */
+    int getShift();
+
+    /** <em>Don't call this method!</em>
+     * @lucene.internal */
+    void init(long value, int shift);
+
+    /** <em>Don't call this method!</em>
+     * @lucene.internal */
+    int incShift();
+  }
+
+  // just a wrapper to prevent adding CTA
+  private static final class GeoPointAttributeFactory extends AttributeFactory {
+    private final AttributeFactory delegate;
+
+    GeoPointAttributeFactory(AttributeFactory delegate) {
+      this.delegate = delegate;
+    }
+
+    @Override
+    public AttributeImpl createAttributeInstance(Class<? extends Attribute> attClass) {
+      if (CharTermAttribute.class.isAssignableFrom(attClass)) {
+        throw new IllegalArgumentException("GeoPointTokenStream does not support CharTermAttribute.");
+      }
+      return delegate.createAttributeInstance(attClass);
+    }
+  }
+
+  public static final class GeoPointTermAttributeImpl extends AttributeImpl implements GeoPointTermAttribute,TermToBytesRefAttribute {
+    private long value = 0L;
+    private int shift = 0;
+    private BytesRefBuilder bytes = new BytesRefBuilder();
+
+    public GeoPointTermAttributeImpl() {
+      this.shift = MAX_SHIFT-PRECISION_STEP;
+    }
+
+    @Override
+    public BytesRef getBytesRef() {
+      GeoEncodingUtils.geoCodedToPrefixCoded(value, shift, bytes);
+      return bytes.get();
+    }
+
+    @Override
+    public void init(long value, int shift) {
+      this.value = value;
+      this.shift = shift;
+    }
+
+    @Override
+    public int getShift() { return shift; }
+
+    @Override
+    public int incShift() {
+      return (shift += PRECISION_STEP);
+    }
+
+    @Override
+    public void clear() {
+      // this attribute has no contents to clear!
+      // we keep it untouched as it's fully controlled by outer class.
+    }
+
+    @Override
+    public void reflectWith(AttributeReflector reflector) {
+      reflector.reflect(TermToBytesRefAttribute.class, "bytes", getBytesRef());
+      reflector.reflect(GeoPointTermAttribute.class, "shift", shift);
+    }
+
+    @Override
+    public void copyTo(AttributeImpl target) {
+      final GeoPointTermAttribute a = (GeoPointTermAttribute) target;
+      a.init(value, shift);
+    }
+
+    @Override
+    public GeoPointTermAttributeImpl clone() {
+      GeoPointTermAttributeImpl t = (GeoPointTermAttributeImpl)super.clone();
+      // Do a deep clone
+      t.bytes = new BytesRefBuilder();
+      t.bytes.copyBytes(getBytesRef());
+      return t;
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hash(shift, value);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (this == obj) return true;
+      if (obj == null) return false;
+      if (getClass() != obj.getClass()) return false;
+      GeoPointTermAttributeImpl other = (GeoPointTermAttributeImpl) obj;
+      if (shift != other.shift) return false;
+      if (value != other.value) return false;
+      return true;
+    }
+  }
+
+  /** override toString because it can throw cryptic "illegal shift value": */
+  @Override
+  public String toString() {
+    return getClass().getSimpleName() + "(precisionStep=" + PRECISION_STEP + " shift=" + geoPointTermAtt.getShift() + ")";
+  }
+}
\ 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/geopoint/document/package-info.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/package-info.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/package-info.java
new file mode 100644
index 0000000..2d23448
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/document/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 Field Implementations for Core Lucene
+ */
+package org.apache.lucene.spatial.geopoint.document;
\ 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/geopoint/search/GeoBoundingBox.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoBoundingBox.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoBoundingBox.java
new file mode 100644
index 0000000..8f30f60
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoBoundingBox.java
@@ -0,0 +1,53 @@
+/*
+ * 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.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/geopoint/search/GeoPointDistanceQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQuery.java
new file mode 100644
index 0000000..bd3dbdd
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQuery.java
@@ -0,0 +1,189 @@
+/*
+ * 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.IndexReader;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.geopoint.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.geopoint.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/geopoint/search/GeoPointDistanceQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java
new file mode 100644
index 0000000..f091bcb
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceQueryImpl.java
@@ -0,0 +1,107 @@
+/*
+ * 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.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/geopoint/search/GeoPointDistanceRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceRangeQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceRangeQuery.java
new file mode 100644
index 0000000..e7faccb
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointDistanceRangeQuery.java
@@ -0,0 +1,116 @@
+/*
+ * 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.IndexReader;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField.TermEncoding;
+
+/** Implements a point distance range query on a GeoPoint field. This is based on
+ * {@code org.apache.lucene.spatial.geopoint.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.geopoint.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/geopoint/search/GeoPointInBBoxQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInBBoxQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInBBoxQuery.java
new file mode 100644
index 0000000..64e8f76
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInBBoxQuery.java
@@ -0,0 +1,173 @@
+/*
+ * 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.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.geopoint.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.geopoint.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/geopoint/search/GeoPointInBBoxQueryImpl.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInBBoxQueryImpl.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInBBoxQueryImpl.java
new file mode 100644
index 0000000..675b2a1
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInBBoxQueryImpl.java
@@ -0,0 +1,156 @@
+/*
+ * 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.util.SloppyMath;
+import org.apache.lucene.spatial.geopoint.document.GeoPointField;
+import org.apache.lucene.spatial.geopoint.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/geopoint/search/GeoPointInPolygonQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQuery.java b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQuery.java
new file mode 100644
index 0000000..6b0d4dd
--- /dev/null
+++ b/lucene/spatial/src/java/org/apache/lucene/spatial/geopoint/search/GeoPointInPolygonQuery.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.util.Arrays;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.spatial.geopoint.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.geopoint.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;
+  }
+}