You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucenenet.apache.org by sy...@apache.org on 2015/03/08 22:51:41 UTC

[1/3] lucenenet git commit: Some code fixes to Spatial, merging in before re-porting from 4.8.0

Repository: lucenenet
Updated Branches:
  refs/heads/master 8223d5498 -> dc71f60de


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTreeFactory.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTreeFactory.cs b/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTreeFactory.cs
index e4e73c6..1702e29 100644
--- a/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTreeFactory.cs
+++ b/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTreeFactory.cs
@@ -1,20 +1,19 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 using System.Collections.Generic;
 using Spatial4n.Core.Context;
@@ -23,80 +22,112 @@ using Spatial4n.Core.Distance;
 namespace Lucene.Net.Spatial.Prefix.Tree
 {
     /// <summary>
-    /// Abstract Factory for creating {@link SpatialPrefixTree} instances with useful
+    /// Abstract Factory for creating
+    /// <see cref="SpatialPrefixTree">SpatialPrefixTree</see>
+    /// instances with useful
     /// defaults and passed on configurations defined in a Map.
     /// </summary>
+    /// <lucene.experimental></lucene.experimental>
     public abstract class SpatialPrefixTreeFactory
     {
-        private const double DEFAULT_GEO_MAX_DETAIL_KM = 0.001; //1m
-        public static readonly String PREFIX_TREE = "prefixTree";
-        public static readonly String MAX_LEVELS = "maxLevels";
-        public static readonly String MAX_DIST_ERR = "maxDistErr";
+        private const double DefaultGeoMaxDetailKm = 0.001;
 
-        protected Dictionary<String, String> args;
-        protected SpatialContext ctx;
-        protected int? maxLevels;
+        public const string PrefixTree = "prefixTree";
 
-        /// <summary>
+        public const string MaxLevels = "maxLevels";
+
+        public const string MaxDistErr = "maxDistErr";
+
+        protected internal IDictionary<string, string> args;
+
+        protected internal SpatialContext ctx;
+
+        protected internal int? maxLevels;
+
+        //1m
+        /// <summary>The factory  is looked up via "prefixTree" in args, expecting "geohash" or "quad".
+        /// 	</summary>
+        /// <remarks>
         /// The factory  is looked up via "prefixTree" in args, expecting "geohash" or "quad".
         /// If its neither of these, then "geohash" is chosen for a geo context, otherwise "quad" is chosen.
-        /// </summary>
-        /// <param name="args"></param>
-        /// <param name="ctx"></param>
-        /// <returns></returns>
-        public static SpatialPrefixTree MakeSPT(Dictionary<String, String> args, SpatialContext ctx)
+        /// </remarks>
+        public static SpatialPrefixTree MakeSPT(IDictionary<string, string> args, SpatialContext ctx)
         {
             SpatialPrefixTreeFactory instance;
-            String cname;
-            if (!args.TryGetValue(PREFIX_TREE, out cname) || cname == null)
+            string cname = args[PrefixTree];
+            if (cname == null)
+            {
                 cname = ctx.IsGeo() ? "geohash" : "quad";
-            if ("geohash".Equals(cname, StringComparison.InvariantCultureIgnoreCase))
+            }
+            if ("geohash".Equals(cname, StringComparison.OrdinalIgnoreCase))
+            {
                 instance = new GeohashPrefixTree.Factory();
-            else if ("quad".Equals(cname, StringComparison.InvariantCultureIgnoreCase))
-                instance = new QuadPrefixTree.Factory();
+            }
             else
             {
-                Type t = Type.GetType(cname);
-                instance = (SpatialPrefixTreeFactory)Activator.CreateInstance(t);
+                if ("quad".Equals(cname, StringComparison.OrdinalIgnoreCase))
+                {
+                    instance = new QuadPrefixTree.Factory();
+                }
+                else
+                {
+                    try
+                    {
+                        Type c = Type.GetType(cname);
+                        instance = (SpatialPrefixTreeFactory)System.Activator.CreateInstance(c);
+                    }
+                    catch (Exception e)
+                    {
+                        throw new Exception(string.Empty, e);
+                    }
+                }
             }
             instance.Init(args, ctx);
             return instance.NewSPT();
         }
 
-        protected void Init(Dictionary<String, String> args, SpatialContext ctx)
+        protected internal virtual void Init(IDictionary<string, string> args, SpatialContext
+             ctx)
         {
             this.args = args;
             this.ctx = ctx;
             InitMaxLevels();
         }
 
-        protected void InitMaxLevels()
+        protected internal virtual void InitMaxLevels()
         {
-            String mlStr;
-            if (args.TryGetValue(MAX_LEVELS, out mlStr) && mlStr != null)
+            string mlStr = args[MaxLevels];
+            if (mlStr != null)
             {
                 maxLevels = int.Parse(mlStr);
                 return;
             }
-
             double degrees;
-            if (!args.TryGetValue(MAX_DIST_ERR, out mlStr) || mlStr == null)
+            string maxDetailDistStr = args[MaxDistErr];
+            if (maxDetailDistStr == null)
             {
                 if (!ctx.IsGeo())
-                    return; //let default to max
-                degrees = DistanceUtils.Dist2Degrees(DEFAULT_GEO_MAX_DETAIL_KM, DistanceUtils.EARTH_MEAN_RADIUS_KM);
+                {
+                    return;
+                }
+                //let default to max
+                degrees = DistanceUtils.Dist2Degrees(DefaultGeoMaxDetailKm, DistanceUtils.EARTH_MEAN_RADIUS_KM);
             }
             else
             {
-                degrees = Double.Parse(mlStr);
+                degrees = double.Parse(maxDetailDistStr);
             }
             maxLevels = GetLevelForDistance(degrees);
         }
 
-        /* Calls {@link SpatialPrefixTree#getLevelForDistance(double)}. */
-        protected abstract int GetLevelForDistance(double degrees);
-
-        protected abstract SpatialPrefixTree NewSPT();
+        /// <summary>
+        /// Calls
+        /// <see cref="SpatialPrefixTree.GetLevelForDistance(double)">SpatialPrefixTree.GetLevelForDistance(double)
+        /// 	</see>
+        /// .
+        /// </summary>
+        protected internal abstract int GetLevelForDistance(double degrees);
 
+        protected internal abstract SpatialPrefixTree NewSPT();
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/WithinPrefixTreeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/WithinPrefixTreeFilter.cs b/src/Lucene.Net.Spatial/Prefix/WithinPrefixTreeFilter.cs
new file mode 100644
index 0000000..032554a
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Prefix/WithinPrefixTreeFilter.cs
@@ -0,0 +1,297 @@
+/* 
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Spatial.Prefix.Tree;
+using Lucene.Net.Spatial.Queries;
+using Lucene.Net.Util;
+using Spatial4n.Core.Context;
+using Spatial4n.Core.Distance;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Prefix
+{
+    /// <summary>
+    /// Finds docs where its indexed shape is
+    /// <see cref="SpatialOperation.IsWithin">WITHIN</see>
+    /// the query shape.  It works by looking at cells outside of the query
+    /// shape to ensure documents there are excluded. By default, it will
+    /// examine all cells, and it's fairly slow.  If you know that the indexed shapes
+    /// are never comprised of multiple disjoint parts (which also means it is not multi-valued),
+    /// then you can pass
+    /// <code>SpatialPrefixTree.getDistanceForLevel(maxLevels)</code>
+    /// as
+    /// the
+    /// <code>queryBuffer</code>
+    /// constructor parameter to minimally look this distance
+    /// beyond the query shape's edge.  Even if the indexed shapes are sometimes
+    /// comprised of multiple disjoint parts, you might want to use this option with
+    /// a large buffer as a faster approximation with minimal false-positives.
+    /// </summary>
+    /// <lucene.experimental></lucene.experimental>
+    public class WithinPrefixTreeFilter : AbstractVisitingPrefixTreeFilter
+    {
+        private readonly Shape bufferedQueryShape;
+
+        /// <summary>
+        /// See
+        /// <see cref="AbstractVisitingPrefixTreeFilter">AbstractVisitingPrefixTreeFilter.AbstractVisitingPrefixTreeFilter(Shape, string, Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree, int, int)
+        /// 	</see>
+        /// .
+        /// <code>queryBuffer</code>
+        /// is the (minimum) distance beyond the query shape edge
+        /// where non-matching documents are looked for so they can be excluded. If
+        /// -1 is used then the whole world is examined (a good default for correctness).
+        /// </summary>
+        public WithinPrefixTreeFilter(Shape queryShape, string fieldName
+                                      , SpatialPrefixTree grid, int detailLevel, int prefixGridScanLevel,
+                                      double queryBuffer
+            )
+            : base(queryShape, fieldName, grid, detailLevel, prefixGridScanLevel)
+        {
+            //TODO LUCENE-4869: implement faster algorithm based on filtering out false-positives of a
+            //  minimal query buffer by looking in a DocValues cache holding a representative
+            //  point of each disjoint component of a document's shape(s).
+            //if null then the whole world
+            if (queryBuffer == -1)
+            {
+                bufferedQueryShape = null;
+            }
+            else
+            {
+                bufferedQueryShape = BufferShape(queryShape, queryBuffer);
+            }
+        }
+
+        /// <summary>Returns a new shape that is larger than shape by at distErr.</summary>
+        /// <remarks>Returns a new shape that is larger than shape by at distErr.</remarks>
+        protected internal virtual Shape BufferShape(Shape
+                                                         shape, double distErr)
+        {
+            //TODO move this generic code elsewhere?  Spatial4j?
+            if (distErr <= 0)
+            {
+                throw new ArgumentException("distErr must be > 0");
+            }
+            SpatialContext ctx = grid.SpatialContext;
+            if (shape is Point)
+            {
+                return ctx.MakeCircle((Point)shape, distErr);
+            }
+            else
+            {
+                if (shape is Circle)
+                {
+                    var circle = (Circle)shape;
+                    double newDist = circle.GetRadius() + distErr;
+                    if (ctx.IsGeo() && newDist > 180)
+                    {
+                        newDist = 180;
+                    }
+                    return ctx.MakeCircle(circle.GetCenter(), newDist);
+                }
+                else
+                {
+                    Rectangle bbox = shape.GetBoundingBox();
+                    double newMinX = bbox.GetMinX() - distErr;
+                    double newMaxX = bbox.GetMaxX() + distErr;
+                    double newMinY = bbox.GetMinY() - distErr;
+                    double newMaxY = bbox.GetMaxY() + distErr;
+                    if (ctx.IsGeo())
+                    {
+                        if (newMinY < -90)
+                        {
+                            newMinY = -90;
+                        }
+                        if (newMaxY > 90)
+                        {
+                            newMaxY = 90;
+                        }
+                        if (newMinY == -90 || newMaxY == 90 || bbox.GetWidth() + 2 * distErr > 360)
+                        {
+                            newMinX = -180;
+                            newMaxX = 180;
+                        }
+                        else
+                        {
+                            newMinX = DistanceUtils.NormLonDEG(newMinX);
+                            newMaxX = DistanceUtils.NormLonDEG(newMaxX);
+                        }
+                    }
+                    else
+                    {
+                        //restrict to world bounds
+                        newMinX = Math.Max(newMinX, ctx.GetWorldBounds().GetMinX());
+                        newMaxX = Math.Min(newMaxX, ctx.GetWorldBounds().GetMaxX());
+                        newMinY = Math.Max(newMinY, ctx.GetWorldBounds().GetMinY());
+                        newMaxY = Math.Min(newMaxY, ctx.GetWorldBounds().GetMaxY());
+                    }
+                    return ctx.MakeRectangle(newMinX, newMaxX, newMinY, newMaxY);
+                }
+            }
+        }
+
+        /// <exception cref="System.IO.IOException"></exception>
+        public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs
+            )
+        {
+            return new _VisitorTemplate_121(this, context, acceptDocs, true).GetDocIdSet();
+        }
+
+        #region Nested type: _VisitorTemplate_121
+
+        private sealed class _VisitorTemplate_121 : VisitorTemplate
+        {
+            private readonly WithinPrefixTreeFilter _enclosing;
+            private FixedBitSet inside;
+
+            private FixedBitSet outside;
+
+            private SpatialRelation visitRelation;
+
+            public _VisitorTemplate_121(WithinPrefixTreeFilter _enclosing, AtomicReaderContext
+                                                                               baseArg1, IBits baseArg2, bool baseArg3)
+                : base(_enclosing, baseArg1, baseArg2, baseArg3)
+            {
+                this._enclosing = _enclosing;
+            }
+
+            protected internal override void Start()
+            {
+                inside = new FixedBitSet(maxDoc);
+                outside = new FixedBitSet(maxDoc);
+            }
+
+            protected internal override DocIdSet Finish()
+            {
+                inside.AndNot(outside);
+                return inside;
+            }
+
+            protected internal override IEnumerator<Cell> FindSubCellsToVisit(Cell cell)
+            {
+                //use buffered query shape instead of orig.  Works with null too.
+                return cell.GetSubCells(_enclosing.bufferedQueryShape).GetEnumerator();
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal override bool Visit(Cell cell)
+            {
+                //cell.relate is based on the bufferedQueryShape; we need to examine what
+                // the relation is against the queryShape
+                visitRelation = cell.GetShape().Relate(_enclosing.queryShape);
+                if (visitRelation == SpatialRelation.WITHIN)
+                {
+                    CollectDocs(inside);
+                    return false;
+                }
+                else
+                {
+                    if (visitRelation == SpatialRelation.DISJOINT)
+                    {
+                        CollectDocs(outside);
+                        return false;
+                    }
+                    else
+                    {
+                        if (cell.Level == _enclosing.detailLevel)
+                        {
+                            CollectDocs(inside);
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal override void VisitLeaf(Cell cell)
+            {
+                //visitRelation is declared as a field, populated by visit() so we don't recompute it
+                Debug.Assert(_enclosing.detailLevel != cell.Level);
+                Debug.Assert(visitRelation == cell.GetShape().Relate(_enclosing.queryShape));
+                if (AllCellsIntersectQuery(cell, visitRelation))
+                {
+                    CollectDocs(inside);
+                }
+                else
+                {
+                    CollectDocs(outside);
+                }
+            }
+
+            /// <summary>
+            /// Returns true if the provided cell, and all its sub-cells down to
+            /// detailLevel all intersect the queryShape.
+            /// </summary>
+            /// <remarks>
+            /// Returns true if the provided cell, and all its sub-cells down to
+            /// detailLevel all intersect the queryShape.
+            /// </remarks>
+            private bool AllCellsIntersectQuery(Cell cell, SpatialRelation relate)
+            {
+                if (relate == SpatialRelation.NULL_VALUE)
+                {
+                    relate = cell.GetShape().Relate(_enclosing.queryShape);
+                }
+                if (cell.Level == _enclosing.detailLevel)
+                {
+                    return relate.Intersects();
+                }
+                if (relate == SpatialRelation.WITHIN)
+                {
+                    return true;
+                }
+                if (relate == SpatialRelation.DISJOINT)
+                {
+                    return false;
+                }
+                // Note: Generating all these cells just to determine intersection is not ideal.
+                // It was easy to implement but could be optimized. For example if the docs
+                // in question are already marked in the 'outside' bitset then it can be avoided.
+                ICollection<Cell> subCells = cell.GetSubCells(null);
+                foreach (Cell subCell in subCells)
+                {
+                    if (!AllCellsIntersectQuery(subCell, SpatialRelation.NULL_VALUE))
+                    {
+                        //recursion
+                        return false;
+                    }
+                }
+                return true;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal override void VisitScanned(Cell cell)
+            {
+                if (AllCellsIntersectQuery(cell, SpatialRelation.NULL_VALUE))
+                {
+                    CollectDocs(inside);
+                }
+                else
+                {
+                    CollectDocs(outside);
+                }
+            }
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Queries/SpatialArgs.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Queries/SpatialArgs.cs b/src/Lucene.Net.Spatial/Queries/SpatialArgs.cs
index 9af4c7f..0383508 100644
--- a/src/Lucene.Net.Spatial/Queries/SpatialArgs.cs
+++ b/src/Lucene.Net.Spatial/Queries/SpatialArgs.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -73,7 +73,7 @@ namespace Lucene.Net.Spatial.Queries
             // and this is the longest distance that might be occurring within the shape.
             double diagonalDist = ctx.GetDistCalc().Distance(
                 ctx.MakePoint(bbox.GetMinX(), bbox.GetMinY()), bbox.GetMaxX(), bbox.GetMaxY());
-            return diagonalDist*0.5*distErrPct;
+            return diagonalDist * 0.5 * distErrPct;
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Queries/SpatialArgsParser.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Queries/SpatialArgsParser.cs b/src/Lucene.Net.Spatial/Queries/SpatialArgsParser.cs
index 6335780..b6faa9f 100644
--- a/src/Lucene.Net.Spatial/Queries/SpatialArgsParser.cs
+++ b/src/Lucene.Net.Spatial/Queries/SpatialArgsParser.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -37,11 +37,11 @@ namespace Lucene.Net.Spatial.Queries
         public static String WriteSpatialArgs(SpatialArgs args)
         {
             var str = new StringBuilder();
-            str.Append(args.Operation.GetName());
+            str.Append(args.Operation.Name);
             str.Append('(');
             str.Append(args.Shape);
             if (args.DistErrPct != null)
-                str.Append(" distErrPct=").Append(String.Format("{0:0.00}%", args.DistErrPct*100d));
+                str.Append(" distErrPct=").Append(String.Format("{0:0.00}%", args.DistErrPct * 100d));
             if (args.DistErr != null)
                 str.Append(" distErr=").Append(args.DistErr);
             str.Append(')');
@@ -117,7 +117,7 @@ namespace Lucene.Net.Spatial.Queries
         {
             var map = new Dictionary<String, String>();
             int tokenPos = 0;
-            var st = body.Split(new[] {' ', '\n', '\t'}, StringSplitOptions.RemoveEmptyEntries);
+            var st = body.Split(new[] { ' ', '\n', '\t' }, StringSplitOptions.RemoveEmptyEntries);
             while (tokenPos < st.Length)
             {
                 String a = st[tokenPos++];

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Queries/SpatialOperation.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Queries/SpatialOperation.cs b/src/Lucene.Net.Spatial/Queries/SpatialOperation.cs
index af82b7d..26f57d3 100644
--- a/src/Lucene.Net.Spatial/Queries/SpatialOperation.cs
+++ b/src/Lucene.Net.Spatial/Queries/SpatialOperation.cs
@@ -1,4 +1,4 @@
-/* See the NOTICE file distributed with
+/* See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
  * Esri Inc. licenses this file to You under the Apache License, Version 2.0
  * (the "License"); you may not use this file except in compliance with
@@ -33,7 +33,7 @@ namespace Lucene.Net.Spatial.Queries
         /// Bounding box of the *indexed* shape.
         /// </summary>
         public static readonly SpatialOperation BBoxIntersects = new SpatialOperation("BBoxIntersects", true, false, false);
-        
+
         /// <summary>
         /// Bounding box of the *indexed* shape.
         /// </summary>
@@ -102,9 +102,9 @@ namespace Lucene.Net.Spatial.Queries
             return targetNeedsArea;
         }
 
-        public String GetName()
+        public String Name
         {
-            return name;
+            get { return name; }
         }
 
         public override String ToString()

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Queries/UnsupportedSpatialOperation.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Queries/UnsupportedSpatialOperation.cs b/src/Lucene.Net.Spatial/Queries/UnsupportedSpatialOperation.cs
index ad93734..35949bc 100644
--- a/src/Lucene.Net.Spatial/Queries/UnsupportedSpatialOperation.cs
+++ b/src/Lucene.Net.Spatial/Queries/UnsupportedSpatialOperation.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -22,7 +22,8 @@ namespace Lucene.Net.Spatial.Queries
     [Serializable]
     public class UnsupportedSpatialOperation : InvalidOperationException
     {
-        public UnsupportedSpatialOperation(SpatialOperation op) : base(op.GetName())
+        public UnsupportedSpatialOperation(SpatialOperation op)
+            : base(op.Name)
         {
         }
     }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/SpatialStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/SpatialStrategy.cs b/src/Lucene.Net.Spatial/SpatialStrategy.cs
index f47deca..0b283c5 100644
--- a/src/Lucene.Net.Spatial/SpatialStrategy.cs
+++ b/src/Lucene.Net.Spatial/SpatialStrategy.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -19,6 +19,7 @@ using System;
 using Lucene.Net.Documents;
 using Lucene.Net.Search;
 using Lucene.Net.Search.Function;
+using Lucene.Net.Search.Function.ValueSources;
 using Lucene.Net.Spatial.Queries;
 using Lucene.Net.Spatial.Util;
 using Spatial4n.Core.Context;
@@ -54,9 +55,9 @@ namespace Lucene.Net.Spatial
             this.fieldName = fieldName;
         }
 
-        public SpatialContext GetSpatialContext()
+        public SpatialContext SpatialContext
         {
-            return ctx;
+            get { return ctx; }
         }
 
         /// <summary>
@@ -64,9 +65,9 @@ namespace Lucene.Net.Spatial
         /// fields needed internally.
         /// </summary>
         /// <returns></returns>
-        public String GetFieldName()
+        public String FieldName
         {
-            return fieldName;
+            get { return fieldName; }
         }
 
         /// <summary>
@@ -82,11 +83,11 @@ namespace Lucene.Net.Spatial
         /// </summary>
         /// <param name="shape"></param>
         /// <returns>Not null nor will it have null elements.</returns>
-        public abstract AbstractField[] CreateIndexableFields(Shape shape);
+        public abstract Field[] CreateIndexableFields(Shape shape);
 
-        public AbstractField CreateStoredField(Shape shape)
+        public Field CreateStoredField(Shape shape)
         {
-            return new Field(GetFieldName(), ctx.ToString(shape), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
+            return new Field(FieldName, ctx.ToString(shape), Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS, Field.TermVector.NO);
         }
 
         /// <summary>
@@ -137,8 +138,8 @@ namespace Lucene.Net.Spatial
             Rectangle bbox = queryShape.GetBoundingBox();
             double diagonalDist = ctx.GetDistCalc().Distance(
                 ctx.MakePoint(bbox.GetMinX(), bbox.GetMinY()), bbox.GetMaxX(), bbox.GetMaxY());
-            double distToEdge = diagonalDist*0.5;
-            float c = (float) distToEdge*0.1f; //one tenth
+            double distToEdge = diagonalDist * 0.5;
+            float c = (float)distToEdge * 0.1f; //one tenth
             return new ReciprocalFloatFunction(MakeDistanceValueSource(queryShape.GetCenter()), 1f, c, c);
         }
 

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs b/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
index ef7a174..b6f9c17 100644
--- a/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
+++ b/src/Lucene.Net.Spatial/Util/CachingDoubleValueSource.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -23,8 +23,8 @@ namespace Lucene.Net.Spatial.Util
 {
     public class CachingDoubleValueSource : ValueSource
     {
-        protected readonly ValueSource source;
         protected readonly Dictionary<int, double> cache;
+        protected readonly ValueSource source;
 
         public CachingDoubleValueSource(ValueSource source)
         {
@@ -32,22 +32,53 @@ namespace Lucene.Net.Spatial.Util
             cache = new Dictionary<int, double>();
         }
 
-        public class CachingDoubleDocValue : DocValues
+        public override string Description
+        {
+            get { return "Cached[" + source.Description + "]"; }
+        }
+
+        public override FunctionValues GetValues(IDictionary<object, object> context, AtomicReaderContext readerContext)
+        {
+            int @base = readerContext.docBase;
+            FunctionValues vals = source.GetValues(context, readerContext);
+            return new CachingDoubleFunctionValue(@base, vals, cache);
+        }
+
+        public override bool Equals(object o)
+        {
+            if (this == o) return true;
+
+            var that = o as CachingDoubleValueSource;
+
+            if (that == null) return false;
+            if (source != null ? !source.Equals(that.source) : that.source != null) return false;
+
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            return source != null ? source.GetHashCode() : 0;
+        }
+
+        #region Nested type: CachingDoubleFunctionValue
+
+        public class CachingDoubleFunctionValue : FunctionValues
         {
-            private readonly int docBase;
-            private readonly DocValues values;
             private readonly Dictionary<int, double> cache;
+            private readonly int docBase;
+            private readonly FunctionValues values;
 
-            public CachingDoubleDocValue(int docBase, DocValues vals, Dictionary<int, double> cache)
+            public CachingDoubleFunctionValue(int docBase, FunctionValues vals, Dictionary<int, double> cache)
             {
                 this.docBase = docBase;
-                this.values = vals;
+                values = vals;
                 this.cache = cache;
             }
 
             public override double DoubleVal(int doc)
             {
-                var key = docBase + doc;
+                int key = docBase + doc;
                 double v;
                 if (!cache.TryGetValue(key, out v))
                 {
@@ -68,34 +99,6 @@ namespace Lucene.Net.Spatial.Util
             }
         }
 
-        public override DocValues GetValues(IndexReader reader)
-        {
-            var @base = 0; //reader.DocBase;
-            var vals = source.GetValues(reader);
-            return new CachingDoubleDocValue(@base, vals, cache);
-
-        }
-
-        public override string Description()
-        {
-            return "Cached[" + source.Description() + "]";
-        }
-
-        public override bool Equals(object o)
-        {
-            if (this == o) return true;
-
-            var that = o as CachingDoubleValueSource;
-
-            if (that == null) return false;
-            if (source != null ? !source.Equals(that.source) : that.source != null) return false;
-
-            return true;
-        }
-
-        public override int GetHashCode()
-        {
-            return source != null ? source.GetHashCode() : 0;
-        }
+        #endregion
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs b/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
index 26a1462..be9b185 100644
--- a/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
+++ b/src/Lucene.Net.Spatial/Util/CompatibilityExtensions.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -21,36 +21,18 @@ using Lucene.Net.Support.Compatibility;
 #else
 using System.Collections.Concurrent;
 #endif
-using System.Collections.Generic;
 using System.Diagnostics;
+using System.Linq;
 using Lucene.Net.Analysis.Tokenattributes;
 using Lucene.Net.Index;
 using Lucene.Net.Search;
+using Lucene.Net.Util;
 
 namespace Lucene.Net.Spatial.Util
 {
     public static class CompatibilityExtensions
     {
-        public static void Append(this ITermAttribute termAtt, string str)
-        {
-            termAtt.SetTermBuffer(termAtt.Term + str); // TODO: Not optimal, but works
-        }
-
-        public static void Append(this ITermAttribute termAtt, char ch)
-        {
-            termAtt.SetTermBuffer(termAtt.Term + new string(new[] {ch})); // TODO: Not optimal, but works
-        }
-
-        private static readonly ConcurrentDictionary<Key<string, IndexReader>, IBits> _docsWithFieldCache =
-            new ConcurrentDictionary<Key<string, IndexReader>, IBits>();
-
-        internal static IBits GetDocsWithField(this FieldCache fc, IndexReader reader, String field)
-        {
-            return _docsWithFieldCache.GetOrAdd(new Key<string, IndexReader>(field, reader),
-                                                key =>
-                                                DocsWithFieldCacheEntry_CreateValue(key.Item2,
-                                                                                    new Entry(key.Item1, null), false));
-        }
+        private static readonly ConcurrentDictionary<string, IBits> _docsWithFieldCache = new ConcurrentDictionary<string, IBits>();
 
         /// <summary> <p/>
         /// EXPERT: Instructs the FieldCache to forcibly expunge all entries 
@@ -67,63 +49,15 @@ namespace Lucene.Net.Spatial.Util
         /// of Lucene.
         /// <p/>
         /// </summary>
-        public static void PurgeSpatialCaches(this FieldCache fc)
+        public static void PurgeSpatialCaches(this IFieldCache fc)
         {
             _docsWithFieldCache.Clear();
         }
 
-        private static IBits DocsWithFieldCacheEntry_CreateValue(IndexReader reader, Entry entryKey,
-                                                                 bool setDocsWithField /* ignored */)
-        {
-            var field = entryKey.field;
-            FixedBitSet res = null;
-            var terms = new TermsEnumCompatibility(reader, field);
-            var maxDoc = reader.MaxDoc;
-
-            var term = terms.Next();
-            if (term != null)
-            {
-                int termsDocCount = terms.GetDocCount();
-                Debug.Assert(termsDocCount <= maxDoc);
-                if (termsDocCount == maxDoc)
-                {
-                    // Fast case: all docs have this field:
-                    return new MatchAllBits(maxDoc);
-                }
-
-                while (true)
-                {
-                    if (res == null)
-                    {
-                        // lazy init
-                        res = new FixedBitSet(maxDoc);
-                    }
-
-                    var termDocs = reader.TermDocs(term);
-                    while (termDocs.Next())
-                    {
-                        res.Set(termDocs.Doc);
-                    }
 
-                    term = terms.Next();
-                    if (term == null)
-                    {
-                        break;
-                    }
-                }
-            }
-            if (res == null)
-            {
-                return new MatchNoBits(maxDoc);
-            }
-            int numSet = res.Cardinality();
-            if (numSet >= maxDoc)
-            {
-                // The cardinality of the BitSet is maxDoc if all documents have a value.
-                Debug.Assert(numSet == maxDoc);
-                return new MatchAllBits(maxDoc);
-            }
-            return res;
+        public static byte[] ToByteArray(this sbyte[] sbytes)
+        {
+            return sbytes.Select(Convert.ToByte).ToArray();
         }
 
         /* table of number of leading zeros in a byte */
@@ -142,6 +76,10 @@ namespace Lucene.Net.Spatial.Util
                 , 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
             };
+        public static sbyte[] ToSByteArray(this byte[] bytes)
+        {
+            return bytes.Select(Convert.ToSByte).ToArray();
+        }
 
         /// <summary>
         /// Returns the number of leading zero bits.
@@ -152,23 +90,11 @@ namespace Lucene.Net.Spatial.Util
         {
             int n = 0;
             // do the first step as a long
-            var y = (int) ((ulong) x >> 32);
-            if (y == 0)
-            {
-                n += 32;
-                y = (int) (x);
-            }
-            if ((y & 0xFFFF0000) == 0)
-            {
-                n += 16;
-                y <<= 16;
-            }
-            if ((y & 0xFF000000) == 0)
-            {
-                n += 8;
-                y <<= 8;
-            }
-            return n + nlzTable[(uint) y >> 24];
+            var y = (int)((ulong)x >> 32);
+            if (y == 0) { n += 32; y = (int)(x); }
+            if ((y & 0xFFFF0000) == 0) { n += 16; y <<= 16; }
+            if ((y & 0xFF000000) == 0) { n += 8; y <<= 8; }
+            return n + nlzTable[(uint)y >> 24];
             /* implementation without table:
               if ((y & 0xF0000000) == 0) { n+=4; y<<=4; }
               if ((y & 0xC0000000) == 0) { n+=2; y<<=2; }
@@ -207,11 +133,10 @@ namespace Lucene.Net.Spatial.Util
     /// </summary>
     internal class Entry
     {
-        internal readonly String field; // which Fieldable
-        internal readonly Object custom; // which custom comparator or parser
+        internal readonly String field;        // which Fieldable
+        internal readonly Object custom;       // which custom comparator or parser
 
         /* Creates one of these objects for a custom comparator/parser. */
-
         public Entry(String field, Object custom)
         {
             this.field = field;
@@ -219,7 +144,6 @@ namespace Lucene.Net.Spatial.Util
         }
 
         /* Two of these are equal iff they reference the same field and type. */
-
         public override bool Equals(Object o)
         {
             var other = o as Entry;
@@ -241,56 +165,9 @@ namespace Lucene.Net.Spatial.Util
         }
 
         /* Composes a hashcode based on the field and type. */
-
         public override int GetHashCode()
         {
             return field.GetHashCode() ^ (custom == null ? 0 : custom.GetHashCode());
         }
     }
-
-    internal struct Key<T1, T2> : IEquatable<Key<T1, T2>>
-    {
-        public bool Equals(Key<T1, T2> other)
-        {
-            return EqualityComparer<T1>.Default.Equals(Item1, other.Item1) &&
-                   EqualityComparer<T2>.Default.Equals(Item2, other.Item2);
-        }
-
-        public override bool Equals(object obj)
-        {
-            if (ReferenceEquals(null, obj))
-            {
-                return false;
-            }
-            return obj is Key<T1, T2> && Equals((Key<T1, T2>) obj);
-        }
-
-        public override int GetHashCode()
-        {
-            unchecked
-            {
-                return (EqualityComparer<T1>.Default.GetHashCode(Item1)*397) ^
-                       EqualityComparer<T2>.Default.GetHashCode(Item2);
-            }
-        }
-
-        public static bool operator ==(Key<T1, T2> left, Key<T1, T2> right)
-        {
-            return left.Equals(right);
-        }
-
-        public static bool operator !=(Key<T1, T2> left, Key<T1, T2> right)
-        {
-            return !left.Equals(right);
-        }
-
-        public readonly T1 Item1;
-        public readonly T2 Item2;
-
-        public Key(T1 item1, T2 item2)
-        {
-            Item1 = item1;
-            Item2 = item2;
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/FunctionQuery.cs b/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
index 64eda65..8d5d1c7 100644
--- a/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
+++ b/src/Lucene.Net.Spatial/Util/FunctionQuery.cs
@@ -209,7 +209,7 @@ namespace Lucene.Net.Spatial.Util
 
         public override int GetHashCode()
         {
-            return (int) (func.GetHashCode() * 31 + BitConverter.DoubleToInt64Bits(Boost));
+            return (int)(func.GetHashCode() * 31 + BitConverter.DoubleToInt64Bits(Boost));
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs b/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
index 5789df4..010f628 100644
--- a/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
+++ b/src/Lucene.Net.Spatial/Util/ReciprocalFloatFunction.cs
@@ -92,9 +92,9 @@ namespace Lucene.Net.Spatial.Util
 
         public override int GetHashCode()
         {
-            int h = (int) BitConverter.DoubleToInt64Bits(a) + (int) BitConverter.DoubleToInt64Bits(m);
+            int h = (int)BitConverter.DoubleToInt64Bits(a) + (int)BitConverter.DoubleToInt64Bits(m);
             h ^= (h << 13) | (int)((uint)h >> 20);
-            return h + ((int) BitConverter.DoubleToInt64Bits(b)) + source.GetHashCode();
+            return h + ((int)BitConverter.DoubleToInt64Bits(b)) + source.GetHashCode();
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
index 59db379..6c27c54 100644
--- a/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
index d806767..b487d0c 100644
--- a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheDistanceValueSource.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -16,6 +16,7 @@
  */
 
 using System;
+using System.Collections.Generic;
 using Lucene.Net.Index;
 using Lucene.Net.Search.Function;
 using Spatial4n.Core.Context;
@@ -40,7 +41,7 @@ namespace Lucene.Net.Spatial.Util
             this.provider = provider;
         }
 
-        public class CachedDistanceDocValues : DocValues
+        public class CachedDistanceFunctionValue : FunctionValues
         {
             private readonly ShapeFieldCacheDistanceValueSource enclosingInstance;
             private readonly ShapeFieldCache<Point> cache;
@@ -48,11 +49,11 @@ namespace Lucene.Net.Spatial.Util
             private readonly DistanceCalculator calculator;
             private readonly double nullValue;
 
-            public CachedDistanceDocValues(IndexReader reader, ShapeFieldCacheDistanceValueSource enclosingInstance)
+            public CachedDistanceFunctionValue(AtomicReader reader, ShapeFieldCacheDistanceValueSource enclosingInstance)
             {
                 cache = enclosingInstance.provider.GetCache(reader);
                 this.enclosingInstance = enclosingInstance;
-                
+
                 from = enclosingInstance.from;
                 calculator = enclosingInstance.ctx.GetDistCalc();
                 nullValue = (enclosingInstance.ctx.IsGeo() ? 180 : double.MaxValue);
@@ -80,18 +81,21 @@ namespace Lucene.Net.Spatial.Util
 
             public override string ToString(int doc)
             {
-                return enclosingInstance.Description() + "=" + FloatVal(doc);
+                return enclosingInstance.Description + "=" + FloatVal(doc);
             }
         }
 
-        public override DocValues GetValues(IndexReader reader)
+        public override string Description
         {
-            return new CachedDistanceDocValues(reader, this);
+            get
+            {
+                return GetType().Name + "(" + provider + ", " + from + ")";
+            }
         }
 
-        public override string Description()
+        public override FunctionValues GetValues(IDictionary<object, object> context, AtomicReaderContext readerContext)
         {
-            return GetType().Name + "(" + provider + ", " + from + ")";
+            return new CachedDistanceFunctionValue(readerContext.AtomicReader, this);
         }
 
         public override bool Equals(object o)

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
index 782ebc9..51827c3 100644
--- a/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
+++ b/src/Lucene.Net.Spatial/Util/ShapeFieldCacheProvider.cs
@@ -1,23 +1,24 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 using System.Runtime.CompilerServices;
 using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Util;
 using Spatial4n.Core.Shapes;
 #if NET35
 using Lucene.Net.Support;
@@ -26,41 +27,47 @@ using Lucene.Net.Support;
 namespace Lucene.Net.Spatial.Util
 {
     /// <summary>
-    /// Provides access to a {@link ShapeFieldCache} for a given {@link AtomicReader}.
-    /// 
+    /// Provides access to a
+    /// <see cref="ShapeFieldCache{T}">ShapeFieldCache&lt;T&gt;</see>
+    /// for a given
+    /// <see cref="Lucene.Net.Index.AtomicReader">Lucene.Net.Index.AtomicReader
+    /// 	</see>
+    /// .
     /// If a Cache does not exist for the Reader, then it is built by iterating over
     /// the all terms for a given field, reconstructing the Shape from them, and adding
     /// them to the Cache.
     /// </summary>
-    /// <typeparam name="T"></typeparam>
-    public abstract class ShapeFieldCacheProvider<T> where T : Shape
+    /// <lucene.internal></lucene.internal>
+    public abstract class ShapeFieldCacheProvider<T>
+        where T : Shape
     {
-        //private Logger log = Logger.getLogger(getClass().getName());
+        //private Logger log = Logger.GetLogger(GetType().FullName);
 
-        // it may be a List<T> or T
 #if !NET35
         private readonly ConditionalWeakTable<IndexReader, ShapeFieldCache<T>> sidx =
             new ConditionalWeakTable<IndexReader, ShapeFieldCache<T>>(); // WeakHashMap
 #else
-        private readonly WeakDictionary<IndexReader, ShapeFieldCache<T>> sidx =
-            new WeakDictionary<IndexReader, ShapeFieldCache<T>>();
+	    private readonly WeakDictionary<IndexReader, ShapeFieldCache<T>> sidx =
+	        new WeakDictionary<IndexReader, ShapeFieldCache<T>>();
 #endif
 
+        protected internal readonly int defaultSize;
 
-        protected readonly int defaultSize;
-        protected readonly String shapeField;
+        protected internal readonly string shapeField;
 
-        protected ShapeFieldCacheProvider(String shapeField, int defaultSize)
+        public ShapeFieldCacheProvider(string shapeField, int defaultSize)
         {
+            // it may be a List<T> or T
             this.shapeField = shapeField;
             this.defaultSize = defaultSize;
         }
 
-        protected abstract T ReadShape(/*BytesRef*/ Term term);
+        protected internal abstract T ReadShape(BytesRef term);
 
         private readonly object locker = new object();
 
-        public ShapeFieldCache<T> GetCache(IndexReader reader)
+        /// <exception cref="System.IO.IOException"></exception>
+        public virtual ShapeFieldCache<T> GetCache(AtomicReader reader)
         {
             lock (locker)
             {
@@ -69,35 +76,37 @@ namespace Lucene.Net.Spatial.Util
                 {
                     return idx;
                 }
-
-                //long startTime = System.CurrentTimeMillis();
-                //log.fine("Building Cache [" + reader.MaxDoc() + "]");
-
+                /*long startTime = Runtime.CurrentTimeMillis();
+				log.Fine("Building Cache [" + reader.MaxDoc() + "]");*/
                 idx = new ShapeFieldCache<T>(reader.MaxDoc, defaultSize);
-                var count = 0;
-                var tec = new TermsEnumCompatibility(reader, shapeField);
-
-                var term = tec.Next();
-                while (term != null)
+                int count = 0;
+                DocsEnum docs = null;
+                Terms terms = reader.Terms(shapeField);
+                TermsEnum te = null;
+                if (terms != null)
                 {
-                    var shape = ReadShape(term);
-                    if (shape != null)
+                    te = terms.Iterator(te);
+                    BytesRef term = te.Next();
+                    while (term != null)
                     {
-                        var docs = reader.TermDocs(new Term(shapeField, tec.Term().Text));
-                        while (docs.Next())
+                        T shape = ReadShape(term);
+                        if (shape != null)
                         {
-                            idx.Add(docs.Doc, shape);
-                            count++;
+                            docs = te.Docs(null, docs, DocsEnum.FLAG_NONE);
+                            int docid = docs.NextDoc();
+                            while (docid != DocIdSetIterator.NO_MORE_DOCS)
+                            {
+                                idx.Add(docid, shape);
+                                docid = docs.NextDoc();
+                                count++;
+                            }
                         }
+                        term = te.Next();
                     }
-                    term = tec.Next();
                 }
-
                 sidx.Add(reader, idx);
-                tec.Close();
-
-                //long elapsed = System.CurrentTimeMillis() - startTime;
-                //log.fine("Cached: [" + count + " in " + elapsed + "ms] " + idx);
+                /*long elapsed = Runtime.CurrentTimeMillis() - startTime;
+                log.Fine("Cached: [" + count + " in " + elapsed + "ms] " + idx);*/
                 return idx;
             }
         }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs b/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
index e92c120..6bba8c7 100644
--- a/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
+++ b/src/Lucene.Net.Spatial/Util/ValueSourceFilter.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -16,8 +16,10 @@
  */
 
 using System;
+using Lucene.Net.Index;
 using Lucene.Net.Search;
 using Lucene.Net.Search.Function;
+using Lucene.Net.Util;
 
 namespace Lucene.Net.Spatial.Util
 {
@@ -44,18 +46,18 @@ namespace Lucene.Net.Spatial.Util
             this.max = max;
         }
 
-        public override DocIdSet GetDocIdSet(Index.IndexReader reader)
+        public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs)
         {
-            var values = source.GetValues(reader);
-            return new ValueSourceFilteredDocIdSet(startingFilter.GetDocIdSet(reader), values, this);
+            var values = source.GetValues(null, context);
+            return new ValueSourceFilteredDocIdSet(startingFilter.GetDocIdSet(context, acceptDocs), values, this);
         }
 
         public class ValueSourceFilteredDocIdSet : FilteredDocIdSet
         {
             private readonly ValueSourceFilter enclosingFilter;
-            private readonly DocValues values;
+            private readonly FunctionValues values;
 
-            public ValueSourceFilteredDocIdSet(DocIdSet innerSet, DocValues values, ValueSourceFilter caller)
+            public ValueSourceFilteredDocIdSet(DocIdSet innerSet, FunctionValues values, ValueSourceFilter caller)
                 : base(innerSet)
             {
                 this.enclosingFilter = caller;
@@ -68,5 +70,6 @@ namespace Lucene.Net.Spatial.Util
                 return val >= enclosingFilter.min && val <= enclosingFilter.max;
             }
         }
+
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs b/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
index 19e2ea0..509146a 100644
--- a/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
+++ b/src/Lucene.Net.Spatial/Vector/DistanceValueSource.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -15,14 +15,14 @@
  * limitations under the License.
  */
 
+using System.Collections.Generic;
 using System.Diagnostics;
 using Lucene.Net.Index;
 using Lucene.Net.Search;
 using Lucene.Net.Search.Function;
-using Lucene.Net.Spatial.Util;
+using Lucene.Net.Util;
 using Spatial4n.Core.Distance;
 using Spatial4n.Core.Shapes;
-using Spatial4n.Core.Shapes.Impl;
 
 namespace Lucene.Net.Spatial.Vector
 {
@@ -31,8 +31,8 @@ namespace Lucene.Net.Spatial.Vector
     /// </summary>
     public class DistanceValueSource : ValueSource
     {
-        private readonly PointVectorStrategy strategy;
         private readonly Point from;
+        private readonly PointVectorStrategy strategy;
 
         public DistanceValueSource(PointVectorStrategy strategy, Point from)
         {
@@ -40,29 +40,58 @@ namespace Lucene.Net.Spatial.Vector
             this.from = from;
         }
 
-        public class DistanceDocValues : DocValues
+        public override string Description
         {
-            private readonly DistanceValueSource enclosingInstance;
+            get { return "DistanceValueSource(" + strategy + ", " + from + ")"; }
+        }
 
-            private readonly double[] ptX, ptY;
-            private readonly IBits validX, validY;
+        public override FunctionValues GetValues(IDictionary<object, object> context, AtomicReaderContext readerContext)
+        {
+            return new DistanceFunctionValue(this, readerContext.AtomicReader);
+        }
 
-            private readonly Point from;
+        public override bool Equals(object o)
+        {
+            if (this == o) return true;
+
+            var that = o as DistanceValueSource;
+            if (that == null) return false;
+
+            if (!from.Equals(that.from)) return false;
+            if (!strategy.Equals(that.strategy)) return false;
+
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            return from.GetHashCode();
+        }
+
+        #region Nested type: DistanceFunctionValues
+
+        public class DistanceFunctionValue : FunctionValues
+        {
             private readonly DistanceCalculator calculator;
+            private readonly DistanceValueSource enclosingInstance;
+            private readonly Point from;
             private readonly double nullValue;
 
-            public DistanceDocValues(DistanceValueSource enclosingInstance, IndexReader reader)
+            private readonly FieldCache.Doubles ptX, ptY;
+            private readonly IBits validX, validY;
+
+            public DistanceFunctionValue(DistanceValueSource enclosingInstance, AtomicReader reader)
             {
                 this.enclosingInstance = enclosingInstance;
 
-                ptX = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.GetFieldNameX()/*, true*/);
-                ptY = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.GetFieldNameY()/*, true*/);
-                validX = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.GetFieldNameX());
-                validY = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.GetFieldNameY());
+                ptX = FieldCache.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.FieldNameX, true);
+                ptY = FieldCache.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.FieldNameY, true);
+                validX = FieldCache.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.FieldNameX);
+                validY = FieldCache.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.FieldNameY);
 
                 from = enclosingInstance.from;
-                calculator = enclosingInstance.strategy.GetSpatialContext().GetDistCalc();
-                nullValue = (enclosingInstance.strategy.GetSpatialContext().IsGeo() ? 180 : double.MaxValue);
+                calculator = enclosingInstance.strategy.SpatialContext.GetDistCalc();
+                nullValue = (enclosingInstance.strategy.SpatialContext.IsGeo() ? 180 : double.MaxValue);
             }
 
             public override float FloatVal(int doc)
@@ -73,46 +102,20 @@ namespace Lucene.Net.Spatial.Vector
             public override double DoubleVal(int doc)
             {
                 // make sure it has minX and area
-                if (validX.Get(doc))
+                if (validX[doc])
                 {
-                    Debug.Assert(validY.Get(doc));
-                    return calculator.Distance(from, ptX[doc], ptY[doc]);
+                    Debug.Assert(validY[doc]);
+                    return calculator.Distance(from, ptX.Get(doc), ptY.Get(doc));
                 }
                 return nullValue;
             }
 
             public override string ToString(int doc)
             {
-                return enclosingInstance.Description() + "=" + FloatVal(doc);
+                return enclosingInstance.Description + "=" + FloatVal(doc);
             }
         }
 
-        public override DocValues GetValues(IndexReader reader)
-        {
-            return new DistanceDocValues(this, reader);
-        }
-
-        public override string Description()
-        {
-            return "DistanceValueSource(" + strategy + ", " + from + ")";
-        }
-
-        public override bool Equals(object o)
-        {
-            if (this == o) return true;
-
-            var that = o as DistanceValueSource;
-            if (that == null) return false;
-
-            if (!from.Equals(that.from)) return false;
-            if (!strategy.Equals(that.strategy)) return false;
-
-            return true;
-        }
-
-        public override int GetHashCode()
-        {
-            return from.GetHashCode();
-        }
+        #endregion
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs b/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
index f3ef815..0f1238f 100644
--- a/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
+++ b/src/Lucene.Net.Spatial/Vector/PointVectorStrategy.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -53,24 +53,27 @@ namespace Lucene.Net.Spatial.Vector
             this.fieldNameY = fieldNamePrefix + SUFFIX_Y;
         }
 
-        public void SetPrecisionStep(int p)
+        public int PrecisionStep
         {
-            precisionStep = p;
-            if (precisionStep <= 0 || precisionStep >= 64)
-                precisionStep = int.MaxValue;
+            set
+            {
+                precisionStep = value;
+                if (precisionStep <= 0 || precisionStep >= 64)
+                    precisionStep = int.MaxValue;
+            }
         }
 
-        public string GetFieldNameX()
+        public string FieldNameX
         {
-            return fieldNameX;
+            get { return fieldNameX; }
         }
 
-        public string GetFieldNameY()
+        public string FieldNameY
         {
-            return fieldNameY;
+            get { return fieldNameY; }
         }
 
-        public override AbstractField[] CreateIndexableFields(Shape shape)
+        public override Field[] CreateIndexableFields(Shape shape)
         {
             var point = shape as Point;
             if (point != null)
@@ -79,21 +82,18 @@ namespace Lucene.Net.Spatial.Vector
             throw new InvalidOperationException("Can only index Point, not " + shape);
         }
 
-        public AbstractField[] CreateIndexableFields(Point point)
+        public Field[] CreateIndexableFields(Point point)
         {
-                var f = new AbstractField[2];
-
-                var f0 = new NumericField(fieldNameX, precisionStep, Field.Store.NO, true)
-                             {OmitNorms = true, OmitTermFreqAndPositions = true};
-                f0.SetDoubleValue(point.GetX());
-                f[0] = f0;
-
-                var f1 = new NumericField(fieldNameY, precisionStep, Field.Store.NO, true)
-                             {OmitNorms = true, OmitTermFreqAndPositions = true};
-                f1.SetDoubleValue(point.GetY());
-                f[1] = f1;
-
-                return f;
+            FieldType doubleFieldType = new FieldType(DoubleField.TYPE_NOT_STORED)
+                                            {
+                                                NumericPrecisionStep = precisionStep
+                                            };
+            var f = new Field[]
+                        {
+                            new DoubleField(fieldNameX, point.GetX(), doubleFieldType),
+                            new DoubleField(fieldNameY, point.GetY(), doubleFieldType)
+                        };
+            return f;
         }
 
         public override ValueSource MakeDistanceValueSource(Point queryPoint)
@@ -124,7 +124,7 @@ namespace Lucene.Net.Spatial.Vector
                     circle.GetRadius());
                 return new ConstantScoreQuery(vsf);
             }
-            
+
             throw new InvalidOperationException("Only Rectangles and Circles are currently supported, " +
                                             "found [" + shape.GetType().Name + "]"); //TODO
         }
@@ -253,7 +253,7 @@ namespace Lucene.Net.Spatial.Vector
                 throw new InvalidOperationException("MakeDisjoint doesn't handle dateline cross");
             Query qX = RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX());
             Query qY = RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY());
-            var bq = new BooleanQuery {{qX, Occur.MUST_NOT}, {qY, Occur.MUST_NOT}};
+            var bq = new BooleanQuery { { qX, Occur.MUST_NOT }, { qY, Occur.MUST_NOT } };
             return bq;
         }
     }


[2/3] lucenenet git commit: Some code fixes to Spatial, merging in before re-porting from 4.8.0

Posted by sy...@apache.org.
http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/PrefixTreeStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/PrefixTreeStrategy.cs b/src/Lucene.Net.Spatial/Prefix/PrefixTreeStrategy.cs
index e15f6cd..36c6177 100644
--- a/src/Lucene.Net.Spatial/Prefix/PrefixTreeStrategy.cs
+++ b/src/Lucene.Net.Spatial/Prefix/PrefixTreeStrategy.cs
@@ -1,20 +1,19 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 #if !NET35
 using System.Collections.Concurrent;
@@ -25,98 +24,180 @@ using System.Collections.Generic;
 using Lucene.Net.Analysis;
 using Lucene.Net.Analysis.Tokenattributes;
 using Lucene.Net.Documents;
+using Lucene.Net.Index;
 using Lucene.Net.Search.Function;
 using Lucene.Net.Spatial.Prefix.Tree;
 using Lucene.Net.Spatial.Queries;
 using Lucene.Net.Spatial.Util;
+using Lucene.Net.Support;
 using Spatial4n.Core.Shapes;
 
 namespace Lucene.Net.Spatial.Prefix
 {
     /// <summary>
-    /// Abstract SpatialStrategy which provides common functionality for those 
-    /// Strategys which use {@link SpatialPrefixTree}s
+    /// An abstract SpatialStrategy based on
+    /// <see cref="Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree">Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree
+    /// 	</see>
+    /// . The two
+    /// subclasses are
+    /// <see cref="RecursivePrefixTreeStrategy">RecursivePrefixTreeStrategy</see>
+    /// and
+    /// <see cref="TermQueryPrefixTreeStrategy">TermQueryPrefixTreeStrategy</see>
+    /// .  This strategy is most effective as a fast
+    /// approximate spatial search filter.
+    /// <h4>Characteristics:</h4>
+    /// <ul>
+    /// <li>Can index any shape; however only
+    /// <see cref="RecursivePrefixTreeStrategy">RecursivePrefixTreeStrategy</see>
+    /// can effectively search non-point shapes.</li>
+    /// <li>Can index a variable number of shapes per field value. This strategy
+    /// can do it via multiple calls to
+    /// <see cref="CreateIndexableFields(Shape)">CreateIndexableFields(Shape)
+    /// 	</see>
+    /// for a document or by giving it some sort of Shape aggregate (e.g. JTS
+    /// WKT MultiPoint).  The shape's boundary is approximated to a grid precision.
+    /// </li>
+    /// <li>Can query with any shape.  The shape's boundary is approximated to a grid
+    /// precision.</li>
+    /// <li>Only
+    /// <see cref="Lucene.Net.Spatial.Query.SpatialOperation.Intersects">Lucene.Net.Spatial.Query.SpatialOperation.Intersects
+    /// 	</see>
+    /// is supported.  If only points are indexed then this is effectively equivalent
+    /// to IsWithin.</li>
+    /// <li>The strategy supports
+    /// <see cref="MakeDistanceValueSource(Point)">MakeDistanceValueSource(Point)
+    /// 	</see>
+    /// even for multi-valued data, so long as the indexed data is all points; the
+    /// behavior is undefined otherwise.  However, <em>it will likely be removed in
+    /// the future</em> in lieu of using another strategy with a more scalable
+    /// implementation.  Use of this call is the only
+    /// circumstance in which a cache is used.  The cache is simple but as such
+    /// it doesn't scale to large numbers of points nor is it real-time-search
+    /// friendly.</li>
+    /// </ul>
+    /// <h4>Implementation:</h4>
+    /// The
+    /// <see cref="Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree">Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree
+    /// 	</see>
+    /// does most of the work, for example returning
+    /// a list of terms representing grids of various sizes for a supplied shape.
+    /// An important
+    /// configuration item is
+    /// <see cref="SetDistErrPct(double)">SetDistErrPct(double)</see>
+    /// which balances
+    /// shape precision against scalability.  See those javadocs.
     /// </summary>
+    /// <lucene.internal></lucene.internal>
     public abstract class PrefixTreeStrategy : SpatialStrategy
     {
-        protected readonly SpatialPrefixTree grid;
+        protected internal readonly SpatialPrefixTree grid;
+
+        private readonly IDictionary<string, PointPrefixTreeFieldCacheProvider> provider =
+            new ConcurrentHashMap<string, PointPrefixTreeFieldCacheProvider>();
 
-        private readonly IDictionary<String, PointPrefixTreeFieldCacheProvider> provider =
-            new ConcurrentDictionary<string, PointPrefixTreeFieldCacheProvider>();
+        protected internal readonly bool simplifyIndexedCells;
 
-        protected int defaultFieldValuesArrayLen = 2;
-        protected double distErrPct = SpatialArgs.DEFAULT_DISTERRPCT; // [ 0 TO 0.5 ]
+        protected internal int defaultFieldValuesArrayLen = 2;
 
-        protected PrefixTreeStrategy(SpatialPrefixTree grid, String fieldName)
-            : base(grid.GetSpatialContext(), fieldName)
+        protected internal double distErrPct = SpatialArgs.DEFAULT_DISTERRPCT;
+
+        public PrefixTreeStrategy(SpatialPrefixTree grid, string fieldName, bool simplifyIndexedCells
+            )
+            : base(grid.SpatialContext, fieldName)
         {
+            // [ 0 TO 0.5 ]
             this.grid = grid;
+            this.simplifyIndexedCells = simplifyIndexedCells;
         }
 
-        /* Used in the in-memory ValueSource as a default ArrayList length for this field's array of values, per doc. */
-
-        public void SetDefaultFieldValuesArrayLen(int defaultFieldValuesArrayLen)
+        /// <summary>
+        /// A memory hint used by
+        /// <see cref="MakeDistanceValueSource(Point)">MakeDistanceValueSource(Point)
+        /// 	</see>
+        /// for how big the initial size of each Document's array should be. The
+        /// default is 2.  Set this to slightly more than the default expected number
+        /// of points per document.
+        /// </summary>
+        public virtual int DefaultFieldValuesArrayLen
         {
-            this.defaultFieldValuesArrayLen = defaultFieldValuesArrayLen;
+            set { defaultFieldValuesArrayLen = value; }
         }
 
         /// <summary>
-        /// The default measure of shape precision affecting indexed and query shapes.
-        /// Specific shapes at index and query time can use something different.
-        /// @see org.apache.lucene.spatial.query.SpatialArgs#getDistErrPct()
+        /// The default measure of shape precision affecting shapes at index and query
+        /// times.
         /// </summary>
-        public double DistErrPct { get; set; }
+        /// <remarks>
+        /// The default measure of shape precision affecting shapes at index and query
+        /// times. Points don't use this as they are always indexed at the configured
+        /// maximum precision (
+        /// <see cref="Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree.GetMaxLevels()
+        /// 	">Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree.GetMaxLevels()</see>
+        /// );
+        /// this applies to all other shapes. Specific shapes at index and query time
+        /// can use something different than this default value.  If you don't set a
+        /// default then the default is
+        /// <see cref="Lucene.Net.Spatial.Query.SpatialArgs.DefaultDisterrpct">Lucene.Net.Spatial.Query.SpatialArgs.DefaultDisterrpct
+        /// 	</see>
+        /// --
+        /// 2.5%.
+        /// </remarks>
+        /// <seealso cref="Lucene.Net.Spatial.Query.SpatialArgs.GetDistErrPct()">Lucene.Net.Spatial.Query.SpatialArgs.GetDistErrPct()
+        /// 	</seealso>
+        public virtual double DistErrPct
+        {
+            get { return distErrPct; }
+            set { distErrPct = value; }
+        }
 
-        public override AbstractField[] CreateIndexableFields(Shape shape)
+        public override Field[] CreateIndexableFields(Shape shape
+            )
         {
             double distErr = SpatialArgs.CalcDistanceFromErrPct(shape, distErrPct, ctx);
             return CreateIndexableFields(shape, distErr);
         }
 
-        public AbstractField[] CreateIndexableFields(Shape shape, double distErr)
+        public virtual Field[] CreateIndexableFields(Shape shape
+            , double distErr)
         {
             int detailLevel = grid.GetLevelForDistance(distErr);
-            var cells = grid.GetNodes(shape, detailLevel, true);//true=intermediates cells
-            //If shape isn't a point, add a full-resolution center-point so that
-            // PointPrefixTreeFieldCacheProvider has the center-points.
-            // TODO index each center of a multi-point? Yes/no?
-            if (!(shape is Point))
-            {
-                Point ctr = shape.GetCenter();
-                //TODO should be smarter; don't index 2 tokens for this in CellTokenStream. Harmless though.
-                cells.Add(grid.GetNodes(ctr, grid.GetMaxLevels(), false)[0]);
-            }
-
+            IList<Cell> cells = grid.GetCells(shape, detailLevel, true, simplifyIndexedCells);
+            //intermediates cells
             //TODO is CellTokenStream supposed to be re-used somehow? see Uwe's comments:
             //  http://code.google.com/p/lucene-spatial-playground/issues/detail?id=4
+            Field field = new Field(FieldName, new PrefixTreeStrategy.CellTokenStream(cells
+                .GetEnumerator()), FieldType);
+            return new Field[] { field };
+        }
+
+        public static readonly FieldType FieldType = new FieldType();
 
-            return new AbstractField[]
-                       {
-                           new Field(GetFieldName(), new CellTokenStream(cells.GetEnumerator()))
-                               {OmitNorms = true, OmitTermFreqAndPositions = true}
-                       };
+        static PrefixTreeStrategy()
+        {
+            FieldType.Indexed = true;
+            FieldType.Tokenized = true;
+            FieldType.OmitNorms = true;
+            FieldType.IndexOptions = FieldInfo.IndexOptions.DOCS_ONLY;
+            FieldType.Freeze();
         }
 
-        /// <summary>
-        /// Outputs the tokenString of a cell, and if its a leaf, outputs it again with the leaf byte.
-        /// </summary>
-        protected class CellTokenStream : TokenStream
+        /// <summary>Outputs the tokenString of a cell, and if its a leaf, outputs it again with the leaf byte.
+        /// 	</summary>
+        /// <remarks>Outputs the tokenString of a cell, and if its a leaf, outputs it again with the leaf byte.
+        /// 	</remarks>
+        internal sealed class CellTokenStream : TokenStream
         {
-            private ITermAttribute termAtt;
-            private readonly IEnumerator<Node> iter;
+            private readonly CharTermAttribute termAtt;
 
-            public CellTokenStream(IEnumerator<Node> tokens)
-            {
-                this.iter = tokens;
-                Init();
-            }
+            private IEnumerator<Cell> iter = null;
 
-            private void Init()
+            public CellTokenStream(IEnumerator<Cell> tokens)
             {
-                termAtt = AddAttribute<ITermAttribute>();
+                this.iter = tokens;
+                termAtt = AddAttribute<CharTermAttribute>();
             }
 
-            private string nextTokenStringNeedingLeaf;
+            internal string nextTokenStringNeedingLeaf;
 
             public override bool IncrementToken()
             {
@@ -124,38 +205,36 @@ namespace Lucene.Net.Spatial.Prefix
                 if (nextTokenStringNeedingLeaf != null)
                 {
                     termAtt.Append(nextTokenStringNeedingLeaf);
-                    termAtt.Append((char)Node.LEAF_BYTE);
+                    termAtt.Append((char)Cell.LEAF_BYTE);
                     nextTokenStringNeedingLeaf = null;
                     return true;
                 }
                 if (iter.MoveNext())
                 {
-                    Node cell = iter.Current;
-                    var token = cell.GetTokenString();
+                    Cell cell = iter.Current;
+                    string token = cell.TokenString;
                     termAtt.Append(token);
                     if (cell.IsLeaf())
+                    {
                         nextTokenStringNeedingLeaf = token;
+                    }
                     return true;
                 }
                 return false;
             }
-
-            protected override void Dispose(bool disposing)
-            {
-            }
         }
 
         public ShapeFieldCacheProvider<Point> GetCacheProvider()
         {
             PointPrefixTreeFieldCacheProvider p;
-            if (!provider.TryGetValue(GetFieldName(), out p) || p == null)
+            if (!provider.TryGetValue(FieldName, out p) || p == null)
             {
                 lock (this)
                 {//double checked locking idiom is okay since provider is threadsafe
-                    if (!provider.ContainsKey(GetFieldName()))
+                    if (!provider.ContainsKey(FieldName))
                     {
-                        p = new PointPrefixTreeFieldCacheProvider(grid, GetFieldName(), defaultFieldValuesArrayLen);
-                        provider[GetFieldName()] = p;
+                        p = new PointPrefixTreeFieldCacheProvider(grid, FieldName, defaultFieldValuesArrayLen);
+                        provider[FieldName] = p;
                     }
                 }
             }
@@ -168,9 +247,9 @@ namespace Lucene.Net.Spatial.Prefix
             return new ShapeFieldCacheDistanceValueSource(ctx, p, queryPoint);
         }
 
-        public SpatialPrefixTree GetGrid()
+        public virtual SpatialPrefixTree Grid
         {
-            return grid;
+            get { return grid; }
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/RecursivePrefixTreeStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/RecursivePrefixTreeStrategy.cs b/src/Lucene.Net.Spatial/Prefix/RecursivePrefixTreeStrategy.cs
index d6fa681..1e0ccd0 100644
--- a/src/Lucene.Net.Spatial/Prefix/RecursivePrefixTreeStrategy.cs
+++ b/src/Lucene.Net.Spatial/Prefix/RecursivePrefixTreeStrategy.cs
@@ -1,20 +1,19 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using Lucene.Net.Search;
 using Lucene.Net.Spatial.Prefix.Tree;
 using Lucene.Net.Spatial.Queries;
@@ -23,40 +22,84 @@ using Spatial4n.Core.Shapes;
 namespace Lucene.Net.Spatial.Prefix
 {
     /// <summary>
-    /// Based on {@link RecursivePrefixTreeFilter}.
+    /// A
+    /// <see cref="PrefixTreeStrategy">PrefixTreeStrategy</see>
+    /// which uses
+    /// <see cref="AbstractVisitingPrefixTreeFilter">AbstractVisitingPrefixTreeFilter</see>
+    /// .
+    /// This strategy has support for searching non-point shapes (note: not tested).
+    /// Even a query shape with distErrPct=0 (fully precise to the grid) should have
+    /// good performance for typical data, unless there is a lot of indexed data
+    /// coincident with the shape's edge.
     /// </summary>
+    /// <lucene.experimental></lucene.experimental>
     public class RecursivePrefixTreeStrategy : PrefixTreeStrategy
     {
         private int prefixGridScanLevel;
 
         public RecursivePrefixTreeStrategy(SpatialPrefixTree grid, string fieldName)
-            : base(grid, fieldName)
+            : base(grid, fieldName, true)
         {
-            prefixGridScanLevel = grid.GetMaxLevels() - 4;//TODO this default constant is dependent on the prefix grid size
+            //simplify indexed cells
+            prefixGridScanLevel = grid.MaxLevels - 4;
         }
 
-        public void SetPrefixGridScanLevel(int prefixGridScanLevel)
+        //TODO this default constant is dependent on the prefix grid size
+        /// <summary>
+        /// Sets the grid level [1-maxLevels] at which indexed terms are scanned brute-force
+        /// instead of by grid decomposition.
+        /// </summary>
+        /// <remarks>
+        /// Sets the grid level [1-maxLevels] at which indexed terms are scanned brute-force
+        /// instead of by grid decomposition.  By default this is maxLevels - 4.  The
+        /// final level, maxLevels, is always scanned.
+        /// </remarks>
+        public virtual int PrefixGridScanLevel
         {
-            //TODO if negative then subtract from maxlevels
-            this.prefixGridScanLevel = prefixGridScanLevel;
+            set
+            {
+                //TODO if negative then subtract from maxlevels
+                prefixGridScanLevel = value;
+            }
         }
 
-        public override Filter MakeFilter(SpatialArgs args)
+        public override string ToString()
         {
-            var op = args.Operation;
-            if (op != SpatialOperation.Intersects)
-                throw new UnsupportedSpatialOperation(op);
-
-            Shape shape = args.Shape;
-
-            int detailLevel = grid.GetLevelForDistance(args.ResolveDistErr(ctx, distErrPct));
-
-            return new RecursivePrefixTreeFilter(GetFieldName(), grid, shape, prefixGridScanLevel, detailLevel);
+            return GetType().Name + "(prefixGridScanLevel:" + prefixGridScanLevel + ",SPG:(" + grid + "))";
         }
 
-        public override string ToString()
+        public override Filter MakeFilter(SpatialArgs args)
         {
-            return GetType().Name + "(prefixGridScanLevel:" + prefixGridScanLevel + ",SPG:(" + grid + "))";
+            SpatialOperation op = args.Operation;
+            if (op == SpatialOperation.IsDisjointTo)
+            {
+                return new DisjointSpatialFilter(this, args, FieldName);
+            }
+            Shape shape = args.Shape;
+            int detailLevel = grid.GetLevelForDistance(args.ResolveDistErr(ctx, distErrPct));
+            bool hasIndexedLeaves = true;
+            if (op == SpatialOperation.Intersects)
+            {
+                return new IntersectsPrefixTreeFilter(shape, FieldName, grid, detailLevel, prefixGridScanLevel
+                                                      , hasIndexedLeaves);
+            }
+            else
+            {
+                if (op == SpatialOperation.IsWithin)
+                {
+                    return new WithinPrefixTreeFilter(shape, FieldName, grid, detailLevel, prefixGridScanLevel
+                                                      , -1);
+                }
+                else
+                {
+                    //-1 flag is slower but ensures correct results
+                    if (op == SpatialOperation.Contains)
+                    {
+                        return new ContainsPrefixTreeFilter(shape, FieldName, grid, detailLevel);
+                    }
+                }
+            }
+            throw new UnsupportedSpatialOperation(op);
         }
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/TermQueryPrefixTreeStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/TermQueryPrefixTreeStrategy.cs b/src/Lucene.Net.Spatial/Prefix/TermQueryPrefixTreeStrategy.cs
index 84a074a..20cbf5f 100644
--- a/src/Lucene.Net.Spatial/Prefix/TermQueryPrefixTreeStrategy.cs
+++ b/src/Lucene.Net.Spatial/Prefix/TermQueryPrefixTreeStrategy.cs
@@ -1,56 +1,72 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
-using System;
-using Lucene.Net.Index;
+using System.Collections.Generic;
 using Lucene.Net.Search;
 using Lucene.Net.Spatial.Prefix.Tree;
 using Lucene.Net.Spatial.Queries;
-using Lucene.Net.Spatial.Util;
+using Lucene.Net.Util;
 using Spatial4n.Core.Shapes;
 
 namespace Lucene.Net.Spatial.Prefix
 {
     /// <summary>
-    /// A basic implementation using a large {@link TermsFilter} of all the nodes from
-    /// {@link SpatialPrefixTree#getNodes(com.spatial4j.core.shape.Shape, int, boolean)}.
+    /// A basic implementation of
+    /// <see cref="PrefixTreeStrategy">PrefixTreeStrategy</see>
+    /// using a large
+    /// <see cref="Lucene.Net.Queries.TermsFilter">Lucene.Net.Queries.TermsFilter
+    /// 	</see>
+    /// of all the cells from
+    /// <see cref="Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree.GetCells(Shape, int, bool, bool)
+    /// 	">Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree.GetCells(Shape, int, bool, bool)
+    /// 	</see>
+    /// . It only supports the search of indexed Point shapes.
+    /// <p/>
+    /// The precision of query shapes (distErrPct) is an important factor in using
+    /// this Strategy. If the precision is too precise then it will result in many
+    /// terms which will amount to a slower query.
     /// </summary>
+    /// <lucene.experimental></lucene.experimental>
     public class TermQueryPrefixTreeStrategy : PrefixTreeStrategy
     {
         public TermQueryPrefixTreeStrategy(SpatialPrefixTree grid, string fieldName)
-            : base(grid, fieldName)
+            : base(grid, fieldName, false)
         {
         }
 
+        //do not simplify indexed cells
         public override Filter MakeFilter(SpatialArgs args)
         {
             SpatialOperation op = args.Operation;
             if (op != SpatialOperation.Intersects)
+            {
                 throw new UnsupportedSpatialOperation(op);
-
+            }
             Shape shape = args.Shape;
             int detailLevel = grid.GetLevelForDistance(args.ResolveDistErr(ctx, distErrPct));
-            var cells = grid.GetNodes(shape, detailLevel, false);
-            var filter = new TermsFilter();
-            foreach (Node cell in cells)
+            IList<Cell> cells = grid.GetCells(shape, detailLevel, false, true);
+            //no parents
+            //simplify
+            var terms = new BytesRef[cells.Count];
+            int i = 0;
+            foreach (Cell cell in cells)
             {
-                filter.AddTerm(new Term(GetFieldName(), cell.GetTokenString()));
+                terms[i++] = new BytesRef(cell.TokenString);
             }
-            return filter;
+            return new TermsFilter(FieldName, terms);
         }
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/Tree/Cell.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/Tree/Cell.cs b/src/Lucene.Net.Spatial/Prefix/Tree/Cell.cs
new file mode 100644
index 0000000..f572f2c
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Prefix/Tree/Cell.cs
@@ -0,0 +1,329 @@
+/* 
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+using System.Text;
+using Lucene.Net.Spatial.Util;
+using Lucene.Net.Util;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Prefix.Tree
+{
+    /// <summary>Represents a grid cell.</summary>
+    /// <remarks>
+    /// Represents a grid cell. These are not necessarily thread-safe, although new
+    /// Cell("") (world cell) must be.
+    /// </remarks>
+    /// <lucene.experimental></lucene.experimental>
+    public abstract class Cell : IComparable<Cell>
+    {
+        public const byte LEAF_BYTE = (byte)('+');//NOTE: must sort before letters & numbers
+
+        /*
+        Holds a byte[] and/or String representation of the cell. Both are lazy constructed from the other.
+        Neither contains the trailing leaf byte.
+        */
+        private byte[] bytes;
+        private int b_off;
+        private int b_len;
+
+        /// <summary>Always false for points.</summary>
+        /// <remarks>
+        /// Always false for points. Otherwise, indicate no further sub-cells are going
+        /// to be provided because shapeRel is WITHIN or maxLevels or a detailLevel is
+        /// hit.
+        /// </remarks>
+        protected internal bool leaf;
+
+        /// <summary>
+        /// When set via getSubCells(filter), it is the relationship between this cell
+        /// and the given shape filter.
+        /// </summary>
+        /// <remarks>
+        /// When set via getSubCells(filter), it is the relationship between this cell
+        /// and the given shape filter.
+        /// </remarks>
+        protected internal SpatialRelation shapeRel = SpatialRelation.NULL_VALUE;//set in getSubCells(filter), and via setLeaf().
+
+        private string token;//this is the only part of equality
+
+        protected internal Cell(string token)
+        {
+            //NOTE: must sort before letters & numbers
+            //this is the only part of equality
+            this.token = token;
+            if (token.Length > 0 && token[token.Length - 1] == (char)LEAF_BYTE)
+            {
+                this.token = token.Substring(0, token.Length - 1);
+                SetLeaf();
+            }
+            if (Level == 0)
+            {
+                GetShape();//ensure any lazy instantiation completes to make this threadsafe
+            }
+        }
+
+        protected internal Cell(byte[] bytes, int off, int len)
+        {
+            //ensure any lazy instantiation completes to make this threadsafe
+            this.bytes = bytes;
+            b_off = off;
+            b_len = len;
+            B_fixLeaf();
+        }
+
+        #region IComparable<Cell> Members
+
+        public virtual int CompareTo(Cell o)
+        {
+            return string.CompareOrdinal(TokenString, o.TokenString);
+        }
+
+        #endregion
+
+        public virtual void Reset(byte[] bytes, int off, int len)
+        {
+            Debug.Assert(Level != 0);
+            token = null;
+            shapeRel = SpatialRelation.NULL_VALUE;
+            this.bytes = bytes;
+            b_off = off;
+            b_len = len;
+            B_fixLeaf();
+        }
+
+        public virtual void Reset(string token)
+        {
+            Debug.Assert(Level != 0);
+            this.token = token;
+            shapeRel = SpatialRelation.NULL_VALUE;
+
+            //converting string t0 byte[]
+            //bytes = Encoding.UTF8.GetBytes(token);
+            BytesRef utf8Result = new BytesRef(token.Length);
+            UnicodeUtil.UTF16toUTF8(token.ToCharArray(), 0, token.Length, utf8Result);
+            bytes = utf8Result.bytes.ToByteArray();
+
+            b_off = 0;
+            b_len = bytes.Length;
+            B_fixLeaf();
+        }
+
+        private void B_fixLeaf()
+        {
+            //note that non-point shapes always have the maxLevels cell set with setLeaf
+            if (bytes[b_off + b_len - 1] == LEAF_BYTE)
+            {
+                b_len--;
+                SetLeaf();
+            }
+            else
+            {
+                leaf = false;
+            }
+        }
+
+        public virtual SpatialRelation GetShapeRel()
+        {
+            return shapeRel;
+        }
+
+        /// <summary>For points, this is always false.</summary>
+        /// <remarks>
+        /// For points, this is always false.  Otherwise this is true if there are no
+        /// further cells with this prefix for the shape (always true at maxLevels).
+        /// </remarks>
+        public virtual bool IsLeaf()
+        {
+            return leaf;
+        }
+
+        /// <summary>Note: not supported at level 0.</summary>
+        /// <remarks>Note: not supported at level 0.</remarks>
+        public virtual void SetLeaf()
+        {
+            Debug.Assert(Level != 0);
+            leaf = true;
+        }
+
+        /*
+         * Note: doesn't contain a trailing leaf byte.
+         */
+        public virtual String TokenString
+        {
+            get
+            {
+                if (token == null)
+                    throw new InvalidOperationException("Somehow we got a null token");
+                return token;
+            }
+        }
+
+        /// <summary>Note: doesn't contain a trailing leaf byte.</summary>
+        /// <remarks>Note: doesn't contain a trailing leaf byte.</remarks>
+        public virtual byte[] GetTokenBytes()
+        {
+            if (bytes != null)
+            {
+                if (b_off != 0 || b_len != bytes.Length)
+                {
+                    throw new InvalidOperationException("Not supported if byte[] needs to be recreated.");
+                }
+            }
+            else
+            {
+                //converting string t0 byte[]
+                //bytes = Encoding.UTF8.GetBytes(token);
+                BytesRef utf8Result = new BytesRef(token.Length);
+                UnicodeUtil.UTF16toUTF8(token.ToCharArray(), 0, token.Length, utf8Result);
+                bytes = utf8Result.bytes.ToByteArray();
+                b_off = 0;
+                b_len = bytes.Length;
+            }
+            return bytes;
+        }
+
+        public virtual int Level
+        {
+            get
+            {
+                return token.Length;
+                //return token != null ? token.Length : b_len;
+            }
+        }
+
+        //TODO add getParent() and update some algorithms to use this?
+        //public Cell getParent();
+        /// <summary>
+        /// Like
+        /// <see cref="GetSubCells()">GetSubCells()</see>
+        /// but with the results filtered by a shape. If
+        /// that shape is a
+        /// <see cref="Point">Spatial4n.Core.Shapes.Point</see>
+        /// then it must call
+        /// <see cref="GetSubCell(Point)">GetSubCell(Spatial4n.Core.Shapes.Point)
+        /// 	</see>
+        /// . The returned cells
+        /// should have
+        /// <see cref="GetShapeRel()">GetShapeRel()</see>
+        /// set to their relation with
+        /// <code>shapeFilter</code>
+        /// . In addition,
+        /// <see cref="IsLeaf()">IsLeaf()</see>
+        /// must be true when that relation is WITHIN.
+        /// <p/>
+        /// Precondition: Never called when getLevel() == maxLevel.
+        /// </summary>
+        /// <param name="shapeFilter">an optional filter for the returned cells.</param>
+        /// <returns>A set of cells (no dups), sorted. Not Modifiable.</returns>
+        public virtual ICollection<Cell> GetSubCells(Shape shapeFilter)
+        {
+            //Note: Higher-performing subclasses might override to consider the shape filter to generate fewer cells.
+            if (shapeFilter is Point)
+            {
+                Cell subCell = GetSubCell((Point)shapeFilter);
+                subCell.shapeRel = SpatialRelation.CONTAINS;
+#if !NET35
+                return new ReadOnlyCollectionBuilder<Cell>(new[] { subCell }).ToReadOnlyCollection();
+#else
+                return new List<Cell>(new[] { subCell }).AsReadOnly();
+#endif
+            }
+
+            ICollection<Cell> cells = GetSubCells();
+            if (shapeFilter == null)
+            {
+                return cells;
+            }
+            //TODO change API to return a filtering iterator
+            IList<Cell> copy = new List<Cell>(cells.Count);
+            foreach (Cell cell in cells)
+            {
+                SpatialRelation rel = cell.GetShape().Relate(shapeFilter);
+                if (rel == SpatialRelation.DISJOINT)
+                {
+                    continue;
+                }
+                cell.shapeRel = rel;
+                if (rel == SpatialRelation.WITHIN)
+                {
+                    cell.SetLeaf();
+                }
+                copy.Add(cell);
+            }
+            return copy;
+        }
+
+        /// <summary>
+        /// Performant implementations are expected to implement this efficiently by
+        /// considering the current cell's boundary.
+        /// </summary>
+        /// <remarks>
+        /// Performant implementations are expected to implement this efficiently by
+        /// considering the current cell's boundary. Precondition: Never called when
+        /// getLevel() == maxLevel.
+        /// <p/>
+        /// Precondition: this.getShape().relate(p) != DISJOINT.
+        /// </remarks>
+        public abstract Cell GetSubCell(Point p);
+
+        //TODO Cell getSubCell(byte b)
+        /// <summary>Gets the cells at the next grid cell level that cover this cell.</summary>
+        /// <remarks>
+        /// Gets the cells at the next grid cell level that cover this cell.
+        /// Precondition: Never called when getLevel() == maxLevel.
+        /// </remarks>
+        /// <returns>A set of cells (no dups), sorted, modifiable, not empty, not null.</returns>
+        protected internal abstract ICollection<Cell> GetSubCells();
+
+        /// <summary>
+        /// <see cref="GetSubCells()">GetSubCells()</see>
+        /// .size() -- usually a constant. Should be &gt;=2
+        /// </summary>
+        public abstract int GetSubCellsSize();
+
+        public abstract Shape GetShape();
+
+        public virtual Point GetCenter()
+        {
+            return GetShape().GetCenter();
+        }
+
+        #region Equality overrides
+
+        public override bool Equals(object obj)
+        {
+            return !(obj == null || !(obj is Cell)) &&
+                   TokenString.Equals(((Cell)obj).TokenString);
+        }
+
+        public override int GetHashCode()
+        {
+            return TokenString.GetHashCode();
+        }
+
+        public override string ToString()
+        {
+            return TokenString + (IsLeaf() ? ((char)LEAF_BYTE).ToString() : string.Empty);
+        }
+
+        #endregion
+
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/Tree/GeohashPrefixTree.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/Tree/GeohashPrefixTree.cs b/src/Lucene.Net.Spatial/Prefix/Tree/GeohashPrefixTree.cs
index b05d7d2..3452948 100644
--- a/src/Lucene.Net.Spatial/Prefix/Tree/GeohashPrefixTree.cs
+++ b/src/Lucene.Net.Spatial/Prefix/Tree/GeohashPrefixTree.cs
@@ -1,20 +1,19 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 using System.Collections.Generic;
 using Spatial4n.Core.Context;
@@ -24,135 +23,167 @@ using Spatial4n.Core.Util;
 namespace Lucene.Net.Spatial.Prefix.Tree
 {
     /// <summary>
-    /// A SpatialPrefixGrid based on Geohashes.  Uses {@link GeohashUtils} to do all the geohash work.
+    /// A
+    /// <see cref="SpatialPrefixTree">SpatialPrefixTree</see>
+    /// based on
+    /// <a href="http://en.wikipedia.org/wiki/Geohash">Geohashes</a>.
+    /// Uses
+    /// <see cref="Spatial4n.Core.IO.GeohashUtils">Spatial4n.Core.IO.GeohashUtils
+    /// 	</see>
+    /// to do all the geohash work.
     /// </summary>
+    /// <lucene.experimental></lucene.experimental>
     public class GeohashPrefixTree : SpatialPrefixTree
     {
-        /// <summary>
-        /// Factory for creating {@link GeohashPrefixTree} instances with useful defaults
-        /// </summary>
-        public class Factory : SpatialPrefixTreeFactory
-        {
-            protected override int GetLevelForDistance(double degrees)
-            {
-                var grid = new GeohashPrefixTree(ctx, GeohashPrefixTree.GetMaxLevelsPossible());
-                return grid.GetLevelForDistance(degrees);
-            }
-
-            protected override SpatialPrefixTree NewSPT()
-            {
-                return new GeohashPrefixTree(ctx, maxLevels != null ? maxLevels.Value : GeohashPrefixTree.GetMaxLevelsPossible());
-            }
-        }
-
-
         public GeohashPrefixTree(SpatialContext ctx, int maxLevels)
             : base(ctx, maxLevels)
         {
             Rectangle bounds = ctx.GetWorldBounds();
             if (bounds.GetMinX() != -180)
-                throw new ArgumentException("Geohash only supports lat-lon world bounds. Got " + bounds);
-            int MAXP = GetMaxLevelsPossible();
-            if (maxLevels <= 0 || maxLevels > MAXP)
-                throw new ArgumentException("maxLen must be [1-" + MAXP + "] but got " + maxLevels);
-
+            {
+                throw new ArgumentException("Geohash only supports lat-lon world bounds. Got " +
+                                            bounds);
+            }
+            int Maxp = MaxLevelsPossible;
+            if (maxLevels <= 0 || maxLevels > Maxp)
+            {
+                throw new ArgumentException("maxLen must be [1-" + Maxp + "] but got " + maxLevels
+                    );
+            }
         }
 
-        /// <summary>
-        /// Any more than this and there's no point (double lat and lon are the same).
-        /// </summary>
-        /// <returns></returns>
-        public static int GetMaxLevelsPossible()
+        /// <summary>Any more than this and there's no point (double lat & lon are the same).
+        /// 	</summary>
+        /// <remarks>Any more than this and there's no point (double lat & lon are the same).
+        /// 	</remarks>
+        public static int MaxLevelsPossible
         {
-            return GeohashUtils.MAX_PRECISION;
+            get { return GeohashUtils.MAX_PRECISION; }
         }
 
         public override int GetLevelForDistance(double dist)
         {
             if (dist == 0)
-                return maxLevels;//short circuit
+            {
+                return maxLevels;
+            }
+            //short circuit
             int level = GeohashUtils.LookupHashLenForWidthHeight(dist, dist);
             return Math.Max(Math.Min(level, maxLevels), 1);
         }
 
-        protected override Node GetNode(Point p, int level)
+        protected internal override Cell GetCell(Point p, int level)
         {
-            return new GhCell(GeohashUtils.EncodeLatLon(p.GetY(), p.GetX(), level), this);//args are lat,lon (y,x)
+            return new GhCell(this, GeohashUtils.EncodeLatLon(p.GetY(), p.GetX
+                                                                            (), level));
         }
 
-        public override Node GetNode(string token)
+        //args are lat,lon (y,x)
+        public override Cell GetCell(string token)
         {
-            return new GhCell(token, this);
+            return new GhCell(this, token);
         }
 
-        public override Node GetNode(byte[] bytes, int offset, int len)
+        public override Cell GetCell(byte[] bytes, int offset, int len)
         {
-            throw new System.NotImplementedException();
+            return new GhCell(this, bytes, offset, len);
         }
 
-        public override IList<Node> GetNodes(Shape shape, int detailLevel, bool inclParents)
+        #region Nested type: Factory
+
+        /// <summary>
+        /// Factory for creating
+        /// <see cref="GeohashPrefixTree">GeohashPrefixTree</see>
+        /// instances with useful defaults
+        /// </summary>
+        public class Factory : SpatialPrefixTreeFactory
         {
-            var s = shape as Point;
-            return (s != null) ? base.GetNodesAltPoint(s, detailLevel, inclParents) : base.GetNodes(shape, detailLevel, inclParents);
+            protected internal override int GetLevelForDistance(double degrees)
+            {
+                var grid = new GeohashPrefixTree(ctx, MaxLevelsPossible);
+                return grid.GetLevelForDistance(degrees);
+            }
+
+            protected internal override SpatialPrefixTree NewSPT()
+            {
+                return new GeohashPrefixTree(ctx, maxLevels.HasValue ? maxLevels.Value : MaxLevelsPossible);
+            }
         }
 
-        public class GhCell : Node
+        #endregion
+
+        #region Nested type: GhCell
+
+        internal class GhCell : Cell
         {
-            public GhCell(String token, GeohashPrefixTree enclosingInstance)
-                : base(enclosingInstance, token)
+            private readonly GeohashPrefixTree _enclosing;
+            private Shape shape;
+
+            internal GhCell(GeohashPrefixTree _enclosing, string token)
+                : base(token)
             {
+                this._enclosing = _enclosing;
             }
 
-            public override void Reset(string newToken)
+            internal GhCell(GeohashPrefixTree _enclosing, byte[] bytes, int off, int len)
+                : base(bytes, off, len)
             {
-                base.Reset(newToken);
-                shape = null;
+                this._enclosing = _enclosing;
             }
 
-            public override IList<Node> GetSubCells()
+            public override void Reset(byte[] bytes, int off, int len)
             {
-                String[] hashes = GeohashUtils.GetSubGeohashes(GetGeohash());//sorted
-                var cells = new List<Node>(hashes.Length);
+                base.Reset(bytes, off, len);
+                shape = null;
+            }
 
-                var enclosingInstance = (GeohashPrefixTree)spatialPrefixTree;
-                foreach (String hash in hashes)
+            protected internal override ICollection<Cell> GetSubCells()
+            {
+                string[] hashes = GeohashUtils.GetSubGeohashes(Geohash);
+                //sorted
+                IList<Cell> cells = new List<Cell>(hashes.Length);
+                foreach (string hash in hashes)
                 {
-                    cells.Add(new GhCell(hash, enclosingInstance));
+                    cells.Add(new GhCell(_enclosing, hash));
                 }
                 return cells;
             }
 
             public override int GetSubCellsSize()
             {
-                return 32;//8x4
+                return 32;
             }
 
-            public override Node GetSubCell(Point p)
+            //8x4
+            public override Cell GetSubCell(Point p)
             {
-                return ((GeohashPrefixTree)spatialPrefixTree).GetNode(p, GetLevel() + 1); //not performant!
+                return _enclosing.GetCell(p, Level + 1);
             }
 
-            private Shape shape;//cache
-
+            //not performant!
+            //cache
             public override Shape GetShape()
             {
                 if (shape == null)
                 {
-                    shape = GeohashUtils.DecodeBoundary(GetGeohash(), ((GeohashPrefixTree)spatialPrefixTree).ctx);
+                    shape = GeohashUtils.DecodeBoundary(Geohash, _enclosing.ctx);
                 }
                 return shape;
             }
 
             public override Point GetCenter()
             {
-                return GeohashUtils.Decode(GetGeohash(), ((GeohashPrefixTree)spatialPrefixTree).ctx);
+                return GeohashUtils.Decode(Geohash, _enclosing.ctx);
             }
 
-            private String GetGeohash()
+            private string Geohash
             {
-                return GetTokenString();
+                get { return TokenString; }
             }
 
-        }//class GhCell
+            //class GhCell
+        }
+
+        #endregion
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/Tree/Node.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/Tree/Node.cs b/src/Lucene.Net.Spatial/Prefix/Tree/Node.cs
index ce403b0..bb05720 100644
--- a/src/Lucene.Net.Spatial/Prefix/Tree/Node.cs
+++ b/src/Lucene.Net.Spatial/Prefix/Tree/Node.cs
@@ -143,7 +143,7 @@ namespace Lucene.Net.Spatial.Prefix.Tree
             if (point != null)
             {
 #if !NET35
-                return new ReadOnlyCollectionBuilder<Node>(new[] {GetSubCell(point)}).ToReadOnlyCollection();
+                return new ReadOnlyCollectionBuilder<Node>(new[] { GetSubCell(point) }).ToReadOnlyCollection();
 #else
                 return new List<Node>(new[]{GetSubCell(point)}).AsReadOnly();
 #endif
@@ -206,7 +206,7 @@ namespace Lucene.Net.Spatial.Prefix.Tree
 
         public override bool Equals(object obj)
         {
-            return !(obj == null || !(obj is Node)) && GetTokenString().Equals(((Node) obj).GetTokenString());
+            return !(obj == null || !(obj is Node)) && GetTokenString().Equals(((Node)obj).GetTokenString());
         }
 
         public override int GetHashCode()
@@ -216,7 +216,7 @@ namespace Lucene.Net.Spatial.Prefix.Tree
 
         public override string ToString()
         {
-            return GetTokenString() + (IsLeaf() ? new string(new[] {(char) LEAF_BYTE}) : string.Empty);
+            return GetTokenString() + (IsLeaf() ? new string(new[] { (char)LEAF_BYTE }) : string.Empty);
         }
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/Tree/QuadPrefixTree.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/Tree/QuadPrefixTree.cs b/src/Lucene.Net.Spatial/Prefix/Tree/QuadPrefixTree.cs
index 230fd4a..bde41b1 100644
--- a/src/Lucene.Net.Spatial/Prefix/Tree/QuadPrefixTree.cs
+++ b/src/Lucene.Net.Spatial/Prefix/Tree/QuadPrefixTree.cs
@@ -1,23 +1,23 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 using System.Collections.Generic;
 using System.Diagnostics;
+using System.IO;
 using System.Text;
 using Spatial4n.Core.Context;
 using Spatial4n.Core.Shapes;
@@ -25,76 +25,48 @@ using Spatial4n.Core.Shapes;
 namespace Lucene.Net.Spatial.Prefix.Tree
 {
     /// <summary>
-    /// Implementation of {@link SpatialPrefixTree} which uses a quad tree
-    /// (http://en.wikipedia.org/wiki/Quadtree)
+    /// A
+    /// <see cref="SpatialPrefixTree">SpatialPrefixTree</see>
+    /// which uses a
+    /// <a href="http://en.wikipedia.org/wiki/Quadtree">quad tree</a> in which an
+    /// indexed term will be generated for each cell, 'A', 'B', 'C', 'D'.
     /// </summary>
+    /// <lucene.experimental></lucene.experimental>
     public class QuadPrefixTree : SpatialPrefixTree
     {
-        /// <summary>
-        /// Factory for creating {@link QuadPrefixTree} instances with useful defaults
-        /// </summary>
-        public class Factory : SpatialPrefixTreeFactory
-        {
-            protected override int GetLevelForDistance(double degrees)
-            {
-                var grid = new QuadPrefixTree(ctx, MAX_LEVELS_POSSIBLE);
-                return grid.GetLevelForDistance(degrees);
-            }
-
-            protected override SpatialPrefixTree NewSPT()
-            {
-                return new QuadPrefixTree(ctx, maxLevels != null ? maxLevels.Value : MAX_LEVELS_POSSIBLE);
-            }
-        }
+        public const int MaxLevelsPossible = 50;
 
-        public static readonly int MAX_LEVELS_POSSIBLE = 50;//not really sure how big this should be
+        public const int DefaultMaxLevels = 12;
 
-        public static readonly int DEFAULT_MAX_LEVELS = 12;
-        private double xmin;
-        private double xmax;
-        private double ymin;
-        private double ymax;
-        private double xmid;
-        private double ymid;
+        public readonly double gridH;
+        private readonly double gridW;
 
-        private double gridW;
-        private double gridH;
+        internal readonly double[] levelH;
 
-        double[] levelW;
-        double[] levelH;
-        int[] levelS; // side
-        int[] levelN; // number
+        internal readonly int[] levelN;
+        internal readonly int[] levelS;
+        internal readonly double[] levelW;
+        private readonly double xmax;
+        private readonly double xmid;
+        private readonly double xmin;
+        private readonly double ymax;
+        private readonly double ymid;
+        private readonly double ymin;
 
         public QuadPrefixTree(SpatialContext ctx, Rectangle bounds, int maxLevels)
             : base(ctx, maxLevels)
         {
-            Init(ctx, bounds, maxLevels);
-        }
-
-        public QuadPrefixTree(SpatialContext ctx)
-            : base(ctx, DEFAULT_MAX_LEVELS)
-        {
-            Init(ctx, ctx.GetWorldBounds(), DEFAULT_MAX_LEVELS);
-        }
-
-        public QuadPrefixTree(SpatialContext ctx, int maxLevels)
-            : base(ctx, maxLevels)
-        {
-            Init(ctx, ctx.GetWorldBounds(), maxLevels);
-        }
-
-        protected void Init(SpatialContext ctx, Rectangle bounds, int maxLevels)
-        {
-            this.xmin = bounds.GetMinX();
-            this.xmax = bounds.GetMaxX();
-            this.ymin = bounds.GetMinY();
-            this.ymax = bounds.GetMaxY();
-
+            //not really sure how big this should be
+            // side
+            // number
+            xmin = bounds.GetMinX();
+            xmax = bounds.GetMaxX();
+            ymin = bounds.GetMinY();
+            ymax = bounds.GetMaxY();
             levelW = new double[maxLevels];
             levelH = new double[maxLevels];
             levelS = new int[maxLevels];
             levelN = new int[maxLevels];
-
             gridW = xmax - xmin;
             gridH = ymax - ymin;
             xmid = xmin + gridW / 2.0;
@@ -103,7 +75,6 @@ namespace Lucene.Net.Spatial.Prefix.Tree
             levelH[0] = gridH / 2.0;
             levelS[0] = 2;
             levelN[0] = 4;
-
             for (int i = 1; i < levelW.Length; i++)
             {
                 levelW[i] = levelW[i - 1] / 2.0;
@@ -111,13 +82,40 @@ namespace Lucene.Net.Spatial.Prefix.Tree
                 levelS[i] = levelS[i - 1] * 2;
                 levelN[i] = levelN[i - 1] * 4;
             }
+        }
+
+        public QuadPrefixTree(SpatialContext ctx)
+            : this(ctx, DefaultMaxLevels)
+        {
+        }
+
+        public QuadPrefixTree(SpatialContext ctx, int maxLevels)
+            : this(ctx, ctx.GetWorldBounds(), maxLevels)
+        {
+        }
 
+        public virtual void PrintInfo(TextWriter @out)
+        {
+            // Format the number to min 3 integer digits and exactly 5 fraction digits
+            const string FORMAT_STR = @"000.00000";
+            /*NumberFormat nf = NumberFormat.GetNumberInstance(CultureInfo.Root);
+			nf.SetMaximumFractionDigits(5);
+			nf.SetMinimumFractionDigits(5);
+			nf.SetMinimumIntegerDigits(3);*/
+            for (int i = 0; i < maxLevels; i++)
+            {
+                @out.WriteLine(i + "]\t" + levelW[i].ToString(FORMAT_STR) + "\t" + levelH[i].ToString(FORMAT_STR) + "\t" +
+                               levelS[i] + "\t" + (levelS[i] * levelS[i]));
+            }
         }
 
         public override int GetLevelForDistance(double dist)
         {
-            if (dist == 0)//short circuit
+            if (dist == 0)
+            {
+                //short circuit
                 return maxLevels;
+            }
             for (int i = 0; i < maxLevels - 1; i++)
             {
                 //note: level[i] is actually a lookup for level i+1
@@ -129,65 +127,49 @@ namespace Lucene.Net.Spatial.Prefix.Tree
             return maxLevels;
         }
 
-        protected override Node GetNode(Point p, int level)
+        protected internal override Cell GetCell(Point p, int level)
         {
-            var cells = new List<Node>(1);
+            IList<Cell> cells = new List<Cell>(1);
             Build(xmid, ymid, 0, cells, new StringBuilder(), ctx.MakePoint(p.GetX(), p.GetY()), level);
-            return cells[0];//note cells could be longer if p on edge
-        }
-
-        public override Node GetNode(string token)
-        {
-            return new QuadCell(token, this);
+            return cells[0];
         }
 
-        public override Node GetNode(byte[] bytes, int offset, int len)
+        //note cells could be longer if p on edge
+        public override Cell GetCell(string token)
         {
-            throw new System.NotImplementedException();
+            return new QuadCell(this, token);
         }
 
-        public override IList<Node> GetNodes(Shape shape, int detailLevel, bool inclParents)
+        public override Cell GetCell(byte[] bytes, int offset, int len)
         {
-            var point = shape as Point;
-            if (point != null)
-                return base.GetNodesAltPoint(point, detailLevel, inclParents);
-            else
-                return base.GetNodes(shape, detailLevel, inclParents);
+            return new QuadCell(this, bytes, offset, len);
         }
 
-        private void Build(double x, double y, int level, List<Node> matches, StringBuilder str, Shape shape, int maxLevel)
+        private void Build(double x, double y, int level, IList<Cell> matches, StringBuilder
+                                                                                   str, Shape shape, int maxLevel)
         {
             Debug.Assert(str.Length == level);
             double w = levelW[level] / 2;
             double h = levelH[level] / 2;
-
             // Z-Order
             // http://en.wikipedia.org/wiki/Z-order_%28curve%29
             CheckBattenberg('A', x - w, y + h, level, matches, str, shape, maxLevel);
             CheckBattenberg('B', x + w, y + h, level, matches, str, shape, maxLevel);
             CheckBattenberg('C', x - w, y - h, level, matches, str, shape, maxLevel);
             CheckBattenberg('D', x + w, y - h, level, matches, str, shape, maxLevel);
-
-            // possibly consider hilbert curve
-            // http://en.wikipedia.org/wiki/Hilbert_curve
-            // http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
-            // if we actually use the range property in the query, this could be useful
         }
 
-        private void CheckBattenberg(
-            char c,
-            double cx,
-            double cy,
-            int level,
-            List<Node> matches,
-            StringBuilder str,
-            Shape shape,
-            int maxLevel)
+        // possibly consider hilbert curve
+        // http://en.wikipedia.org/wiki/Hilbert_curve
+        // http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
+        // if we actually use the range property in the query, this could be useful
+        private void CheckBattenberg(char c, double cx, double cy, int level, IList<Cell>
+                                                                                  matches, StringBuilder str,
+                                     Shape shape, int maxLevel)
         {
             Debug.Assert(str.Length == level);
             double w = levelW[level] / 2;
             double h = levelH[level] / 2;
-
             int strlen = str.Length;
             Rectangle rectangle = ctx.MakeRectangle(cx - w, cx + w, cy - h, cy + h);
             SpatialRelation v = shape.Relate(rectangle);
@@ -195,60 +177,96 @@ namespace Lucene.Net.Spatial.Prefix.Tree
             {
                 str.Append(c);
                 //str.append(SpatialPrefixGrid.COVER);
-                matches.Add(new QuadCell(str.ToString(), v.Transpose(), this));
-            }
-            else if (SpatialRelation.DISJOINT == v)
-            {
-                // nothing
+                matches.Add(new QuadCell(this, str.ToString(), v.Transpose()));
             }
             else
-            { // SpatialRelation.WITHIN, SpatialRelation.INTERSECTS
-                str.Append(c);
-
-                int nextLevel = level + 1;
-                if (nextLevel >= maxLevel)
+            {
+                if (SpatialRelation.DISJOINT == v)
                 {
-                    //str.append(SpatialPrefixGrid.INTERSECTS);
-                    matches.Add(new QuadCell(str.ToString(), v.Transpose(), this));
                 }
                 else
                 {
-                    Build(cx, cy, nextLevel, matches, str, shape, maxLevel);
+                    // nothing
+                    // SpatialRelation.WITHIN, SpatialRelation.INTERSECTS
+                    str.Append(c);
+                    int nextLevel = level + 1;
+                    if (nextLevel >= maxLevel)
+                    {
+                        //str.append(SpatialPrefixGrid.INTERSECTS);
+                        matches.Add(new QuadCell(this, str.ToString(), v.Transpose()));
+                    }
+                    else
+                    {
+                        Build(cx, cy, nextLevel, matches, str, shape, maxLevel);
+                    }
                 }
             }
             str.Length = strlen;
         }
 
-        public class QuadCell : Node
+        #region Nested type: Factory
+
+        /// <summary>
+        /// Factory for creating
+        /// <see cref="QuadPrefixTree">QuadPrefixTree</see>
+        /// instances with useful defaults
+        /// </summary>
+        public class Factory : SpatialPrefixTreeFactory
+        {
+            protected internal override int GetLevelForDistance(double degrees)
+            {
+                var grid = new QuadPrefixTree(ctx, MaxLevelsPossible);
+                return grid.GetLevelForDistance(degrees);
+            }
+
+            protected internal override SpatialPrefixTree NewSPT()
+            {
+                return new QuadPrefixTree(ctx, maxLevels.HasValue ? maxLevels.Value : MaxLevelsPossible);
+            }
+        }
+
+        #endregion
+
+        #region Nested type: QuadCell
+
+        internal class QuadCell : Cell
         {
+            private readonly QuadPrefixTree _enclosing;
+            private Shape shape;
 
-            public QuadCell(String token, QuadPrefixTree enclosingInstance)
-                : base(enclosingInstance, token)
+            public QuadCell(QuadPrefixTree _enclosing, string token)
+                : base(token)
             {
+                this._enclosing = _enclosing;
             }
 
-            public QuadCell(String token, SpatialRelation shapeRel, QuadPrefixTree enclosingInstance)
-                : base(enclosingInstance, token)
+            public QuadCell(QuadPrefixTree _enclosing, string token, SpatialRelation shapeRel
+                )
+                : base(token)
             {
+                this._enclosing = _enclosing;
                 this.shapeRel = shapeRel;
             }
 
-            public override void Reset(string newToken)
+            internal QuadCell(QuadPrefixTree _enclosing, byte[] bytes, int off, int len)
+                : base(bytes, off, len)
             {
-                base.Reset(newToken);
+                this._enclosing = _enclosing;
+            }
+
+            public override void Reset(byte[] bytes, int off, int len)
+            {
+                base.Reset(bytes, off, len);
                 shape = null;
             }
 
-            public override IList<Node> GetSubCells()
+            protected internal override ICollection<Cell> GetSubCells()
             {
-                var tree = (QuadPrefixTree)spatialPrefixTree;
-                var cells = new List<Node>(4)
-                      {
-                          new QuadCell(GetTokenString() + "A", tree),
-                          new QuadCell(GetTokenString() + "B", tree),
-                          new QuadCell(GetTokenString() + "C", tree),
-                          new QuadCell(GetTokenString() + "D", tree)
-                      };
+                IList<Cell> cells = new List<Cell>(4);
+                cells.Add(new QuadCell(_enclosing, TokenString + "A"));
+                cells.Add(new QuadCell(_enclosing, TokenString + "B"));
+                cells.Add(new QuadCell(_enclosing, TokenString + "C"));
+                cells.Add(new QuadCell(_enclosing, TokenString + "D"));
                 return cells;
             }
 
@@ -257,67 +275,80 @@ namespace Lucene.Net.Spatial.Prefix.Tree
                 return 4;
             }
 
-            public override Node GetSubCell(Point p)
+            public override Cell GetSubCell(Point p)
             {
-                return ((QuadPrefixTree)spatialPrefixTree).GetNode(p, GetLevel() + 1); //not performant!
+                return _enclosing.GetCell(p, Level + 1);
             }
 
-            private Shape shape;//cache
-
+            //not performant!
+            //cache
             public override Shape GetShape()
             {
                 if (shape == null)
+                {
                     shape = MakeShape();
+                }
                 return shape;
             }
 
             private Rectangle MakeShape()
             {
-                String token = GetTokenString();
-                var tree = ((QuadPrefixTree)spatialPrefixTree);
-                double xmin = tree.xmin;
-                double ymin = tree.ymin;
-
+                string token = TokenString;
+                double xmin = _enclosing.xmin;
+                double ymin = _enclosing.ymin;
                 for (int i = 0; i < token.Length; i++)
                 {
                     char c = token[i];
                     if ('A' == c || 'a' == c)
                     {
-                        ymin += tree.levelH[i];
-                    }
-                    else if ('B' == c || 'b' == c)
-                    {
-                        xmin += tree.levelW[i];
-                        ymin += tree.levelH[i];
-                    }
-                    else if ('C' == c || 'c' == c)
-                    {
-                        // nothing really
-                    }
-                    else if ('D' == c || 'd' == c)
-                    {
-                        xmin += tree.levelW[i];
+                        ymin += _enclosing.levelH[i];
                     }
                     else
                     {
-                        throw new Exception("unexpected char: " + c);
+                        if ('B' == c || 'b' == c)
+                        {
+                            xmin += _enclosing.levelW[i];
+                            ymin += _enclosing.levelH[i];
+                        }
+                        else
+                        {
+                            if ('C' == c || 'c' == c)
+                            {
+                            }
+                            else
+                            {
+                                // nothing really
+                                if ('D' == c || 'd' == c)
+                                {
+                                    xmin += _enclosing.levelW[i];
+                                }
+                                else
+                                {
+                                    throw new Exception("unexpected char: " + c);
+                                }
+                            }
+                        }
                     }
                 }
                 int len = token.Length;
-                double width, height;
+                double width;
+                double height;
                 if (len > 0)
                 {
-                    width = tree.levelW[len - 1];
-                    height = tree.levelH[len - 1];
+                    width = _enclosing.levelW[len - 1];
+                    height = _enclosing.levelH[len - 1];
                 }
                 else
                 {
-                    width = tree.gridW;
-                    height = tree.gridH;
+                    width = _enclosing.gridW;
+                    height = _enclosing.gridH;
                 }
-                return spatialPrefixTree.ctx.MakeRectangle(xmin, xmin + width, ymin, ymin + height);
+                return _enclosing.ctx.MakeRectangle(xmin, xmin + width, ymin, ymin + height);
             }
-        }//QuadCell
 
+            //QuadCell
+        }
+
+        #endregion
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTree.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTree.cs b/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTree.cs
index 908fded..e13245e 100644
--- a/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTree.cs
+++ b/src/Lucene.Net.Spatial/Prefix/Tree/SpatialPrefixTree.cs
@@ -1,268 +1,319 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Text;
 using Spatial4n.Core.Context;
 using Spatial4n.Core.Shapes;
 
 namespace Lucene.Net.Spatial.Prefix.Tree
 {
     /// <summary>
-    /// A spatial Prefix Tree, or Trie, which decomposes shapes into prefixed strings at variable lengths corresponding to
-    /// variable precision.  Each string corresponds to a spatial region.
-    /// 
-    /// Implementations of this class should be thread-safe and immutable once initialized. 
+    /// A spatial Prefix Tree, or Trie, which decomposes shapes into prefixed strings
+    /// at variable lengths corresponding to variable precision.
     /// </summary>
+    /// <remarks>
+    /// A spatial Prefix Tree, or Trie, which decomposes shapes into prefixed strings
+    /// at variable lengths corresponding to variable precision.   Each string
+    /// corresponds to a rectangular spatial region.  This approach is
+    /// also referred to "Grids", "Tiles", and "Spatial Tiers".
+    /// <p/>
+    /// Implementations of this class should be thread-safe and immutable once
+    /// initialized.
+    /// </remarks>
+    /// <lucene.experimental></lucene.experimental>
     public abstract class SpatialPrefixTree
     {
-        protected readonly int maxLevels;
-        internal readonly SpatialContext ctx;// it's internal to allow Node to access it
+        protected internal readonly int maxLevels;
+
+        protected internal readonly SpatialContext ctx;
 
-        protected SpatialPrefixTree(SpatialContext ctx, int maxLevels)
+        public SpatialPrefixTree(SpatialContext ctx, int maxLevels)
         {
             Debug.Assert(maxLevels > 0);
             this.ctx = ctx;
             this.maxLevels = maxLevels;
         }
 
-        public SpatialContext GetSpatialContext()
+        public virtual SpatialContext SpatialContext
         {
-            return ctx;
+            get { return ctx; }
         }
 
-        public int GetMaxLevels()
+        public virtual int MaxLevels
         {
-            return maxLevels;
+            get { return maxLevels; }
         }
 
-        public override String ToString()
+        public override string ToString()
         {
             return GetType().Name + "(maxLevels:" + maxLevels + ",ctx:" + ctx + ")";
         }
 
         /// <summary>
         /// Returns the level of the largest grid in which its longest side is less
-        /// than or equal to the provided distance (in degrees). Consequently {@link
-        /// dist} acts as an error epsilon declaring the amount of detail needed in the
+        /// than or equal to the provided distance (in degrees).
+        /// </summary>
+        /// <remarks>
+        /// Returns the level of the largest grid in which its longest side is less
+        /// than or equal to the provided distance (in degrees). Consequently
+        /// <code>dist</code>
+        /// acts as an error epsilon declaring the amount of detail needed in the
         /// grid, such that you can get a grid with just the right amount of
         /// precision.
-        /// </summary>
-        /// <param name="dist">>= 0</param>
+        /// </remarks>
+        /// <param name="dist">&gt;= 0</param>
         /// <returns>level [1 to maxLevels]</returns>
         public abstract int GetLevelForDistance(double dist);
 
-        //TODO double getDistanceForLevel(int level)
-
-        //[NotSerialized]
-        private Node worldNode;//cached
-
-        /*
-         * Returns the level 0 cell which encompasses all spatial data. Equivalent to {@link #getNode(String)} with "".
-         * This cell is threadsafe, just like a spatial prefix grid is, although cells aren't
-         * generally threadsafe.
-         * TODO rename to getTopCell or is this fine?
-         */
-        public Node GetWorldNode()
+        /// <summary>
+        /// Given a cell having the specified level, returns the distance from opposite
+        /// corners.
+        /// </summary>
+        /// <remarks>
+        /// Given a cell having the specified level, returns the distance from opposite
+        /// corners. Since this might very depending on where the cell is, this method
+        /// may over-estimate.
+        /// </remarks>
+        /// <param name="level">[1 to maxLevels]</param>
+        /// <returns>&gt; 0</returns>
+        public virtual double GetDistanceForLevel(int level)
         {
-            if (worldNode == null)
+            if (level < 1 || level > MaxLevels)
             {
-                worldNode = GetNode("");
+                throw new ArgumentException("Level must be in 1 to maxLevels range");
             }
-            return worldNode;
+            //TODO cache for each level
+            Cell cell = GetCell(ctx.GetWorldBounds().GetCenter(), level);
+            Rectangle bbox = cell.GetShape().GetBoundingBox();
+            double width = bbox.GetWidth();
+            double height = bbox.GetHeight();
+            //Use standard cartesian hypotenuse. For geospatial, this answer is larger
+            // than the correct one but it's okay to over-estimate.
+            return Math.Sqrt(width * width + height * height);
         }
 
-        /*
-         * The cell for the specified token. The empty string should be equal to {@link #getWorldNode()}.
-         * Precondition: Never called when token length > maxLevel.
-         */
-        public abstract Node GetNode(String token);
+        [System.NonSerialized]
+        private Cell worldCell;
 
-        public abstract Node GetNode(byte[] bytes, int offset, int len);
+        //cached
+        /// <summary>Returns the level 0 cell which encompasses all spatial data.</summary>
+        /// <remarks>
+        /// Returns the level 0 cell which encompasses all spatial data. Equivalent to
+        /// <see cref="GetCell(string)">GetCell(string)</see>
+        /// with "".
+        /// This cell is threadsafe, just like a spatial prefix grid is, although cells aren't
+        /// generally threadsafe.
+        /// TODO rename to getTopCell or is this fine?
+        /// </remarks>
+        public virtual Cell WorldCell
+        {
+            get
+            {
+                if (worldCell == null)
+                {
+                    worldCell = GetCell(string.Empty);
+                }
+                return worldCell;
+            }
+        }
 
-        //public Node GetNode(byte[] bytes, int offset, int len, Node target)
-        //{
-        //    if (target == null)
-        //    {
-        //        return GetNode(bytes, offset, len);
-        //    }
+        /// <summary>The cell for the specified token.</summary>
+        /// <remarks>
+        /// The cell for the specified token. The empty string should be equal to
+        /// <see cref="WorldCell">WorldCell</see>
+        /// .
+        /// Precondition: Never called when token length &gt; maxLevel.
+        /// </remarks>
+        public abstract Cell GetCell(string token);
 
-        //    target.Reset(bytes, offset, len);
-        //    return target;
-        //}
+        public abstract Cell GetCell(byte[] bytes, int offset, int len);
 
-        public Node GetNode(string token, Node target)
+        public Cell GetCell(byte[] bytes, int offset, int len, Cell target)
         {
             if (target == null)
             {
-                return GetNode(token);
+                return GetCell(bytes, offset, len);
             }
-
-            target.Reset(token);
+            target.Reset(bytes, offset, len);
             return target;
         }
 
-        protected virtual Node GetNode(Point p, int level)
+        /// <summary>
+        /// Returns the cell containing point
+        /// <code>p</code>
+        /// at the specified
+        /// <code>level</code>
+        /// .
+        /// </summary>
+        protected internal virtual Cell GetCell(Point p, int level)
         {
-            return GetNodes(p, level, false).ElementAt(0);
+            return GetCells(p, level, false)[0];
         }
 
-        /*
-         * Gets the intersecting & including cells for the specified shape, without exceeding detail level.
-         * The result is a set of cells (no dups), sorted. Unmodifiable.
-         * <p/>
-         * This implementation checks if shape is a Point and if so uses an implementation that
-         * recursively calls {@link Node#getSubCell(com.spatial4j.core.shape.Point)}. Cell subclasses
-         * ideally implement that method with a quick implementation, otherwise, subclasses should
-         * override this method to invoke {@link #getNodesAltPoint(com.spatial4j.core.shape.Point, int, boolean)}.
-         * TODO consider another approach returning an iterator -- won't build up all cells in memory.
-         */
-        public virtual IList<Node> GetNodes(Shape shape, int detailLevel, bool inclParents)
+        /// <summary>
+        /// Gets the intersecting cells for the specified shape, without exceeding
+        /// detail level.
+        /// </summary>
+        /// <remarks>
+        /// Gets the intersecting cells for the specified shape, without exceeding
+        /// detail level. If a cell is within the query shape then it's marked as a
+        /// leaf and none of its children are added.
+        /// <p/>
+        /// This implementation checks if shape is a Point and if so returns
+        /// <see cref="GetCells(Point, int, bool)">GetCells(Point, int, bool)
+        /// 	</see>
+        /// .
+        /// </remarks>
+        /// <param name="shape">the shape; non-null</param>
+        /// <param name="detailLevel">the maximum detail level to get cells for</param>
+        /// <param name="inclParents">
+        /// if true then all parent cells of leaves are returned
+        /// too. The top world cell is never returned.
+        /// </param>
+        /// <param name="simplify">
+        /// for non-point shapes, this will simply/aggregate sets of
+        /// complete leaves in a cell to its parent, resulting in
+        /// ~20-25% fewer cells.
+        /// </param>
+        /// <returns>a set of cells (no dups), sorted, immutable, non-null</returns>
+        public virtual IList<Cell> GetCells(Shape shape, int detailLevel
+            , bool inclParents, bool simplify)
         {
+            //TODO consider an on-demand iterator -- it won't build up all cells in memory.
             if (detailLevel > maxLevels)
             {
-                throw new ArgumentException("detailLevel > maxLevels", "detailLevel");
+                throw new ArgumentException("detailLevel > maxLevels");
             }
-
-            List<Node> cells;
             if (shape is Point)
             {
-                //optimized point algorithm
-                int initialCapacity = inclParents ? 1 + detailLevel : 1;
-                cells = new List<Node>(initialCapacity);
-                RecursiveGetNodes(GetWorldNode(), (Point)shape, detailLevel, true, cells);
-                Debug.Assert(cells.Count == initialCapacity);
-            }
-            else
-            {
-                cells = new List<Node>(inclParents ? 1024 : 512);
-                RecursiveGetNodes(GetWorldNode(), shape, detailLevel, inclParents, cells);
-            }
-            if (inclParents)
-            {
-                Debug.Assert(cells[0].GetLevel() == 0);
-                cells.RemoveAt(0);//remove getWorldNode()
+                return GetCells((Point)shape, detailLevel, inclParents);
             }
+            IList<Cell> cells = new List<Cell>(inclParents ? 4096 : 2048);
+            RecursiveGetCells(WorldCell, shape, detailLevel, inclParents, simplify, cells
+                );
             return cells;
         }
 
-        private void RecursiveGetNodes(Node node, Shape shape, int detailLevel, bool inclParents, IList<Node> result)
+        /// <summary>Returns true if cell was added as a leaf.</summary>
+        /// <remarks>
+        /// Returns true if cell was added as a leaf. If it wasn't it recursively
+        /// descends.
+        /// </remarks>
+        private bool RecursiveGetCells(Cell cell, Shape shape, int
+             detailLevel, bool inclParents, bool simplify, IList<Cell> result)
         {
-            if (node.IsLeaf())
-            {//cell is within shape
-                result.Add(node);
-                return;
+            if (cell.Level == detailLevel)
+            {
+                cell.SetLeaf();
             }
-
-            var subCells = node.GetSubCells(shape);
-            if (node.GetLevel() == detailLevel - 1)
+            //FYI might already be a leaf
+            if (cell.IsLeaf())
+            {
+                result.Add(cell);
+                return true;
+            }
+            if (inclParents && cell.Level != 0)
             {
-                if (subCells.Count < node.GetSubCellsSize())
+                result.Add(cell);
+            }
+            ICollection<Cell> subCells = cell.GetSubCells(shape);
+            int leaves = 0;
+            foreach (Cell subCell in subCells)
+            {
+                if (RecursiveGetCells(subCell, shape, detailLevel, inclParents, simplify, result))
                 {
-                    if (inclParents)
-                        result.Add(node);
-                    foreach (var subCell in subCells)
-                    {
-                        subCell.SetLeaf();
-                        result.Add(subCell);
-                    }
-                }
-                else
-                {//a bottom level (i.e. detail level) optimization where all boxes intersect, so use parent cell.
-                    node.SetLeaf();
-                    result.Add(node);
+                    leaves++;
                 }
             }
-            else
+            //can we simplify?
+            if (simplify && leaves == cell.GetSubCellsSize() && cell.Level != 0)
             {
-                if (inclParents)
+                do
                 {
-                    result.Add(node);
+                    //Optimization: substitute the parent as a leaf instead of adding all
+                    // children as leaves
+                    //remove the leaves
+                    result.RemoveAt(result.Count - 1);
                 }
-                foreach (var subCell in subCells)
+                while (--leaves > 0);
+                //remove last
+                //add cell as the leaf
+                cell.SetLeaf();
+                if (!inclParents)
                 {
-                    RecursiveGetNodes(subCell, shape, detailLevel, inclParents, result);//tail call
+                    // otherwise it was already added up above
+                    result.Add(cell);
                 }
+                return true;
             }
+            return false;
         }
 
-        private void RecursiveGetNodes(Node node, Point point, int detailLevel, bool inclParents, IList<Node> result)
-        {
-            if (inclParents)
-            {
-                result.Add(node);
-            }
-            Node pCell = node.GetSubCell(point);
-            if (node.GetLevel() == detailLevel - 1)
-            {
-                pCell.SetLeaf();
-                result.Add(pCell);
-            }
-            else
-            {
-                RecursiveGetNodes(pCell, point, detailLevel, inclParents, result);//tail call
-            }
-        }
-
-        /*
-         * Subclasses might override {@link #getNodes(com.spatial4j.core.shape.Shape, int, boolean)}
-         * and check if the argument is a shape and if so, delegate
-         * to this implementation, which calls {@link #getNode(com.spatial4j.core.shape.Point, int)} and
-         * then calls {@link #getNode(String)} repeatedly if inclParents is true.
-         */
-        protected virtual IList<Node> GetNodesAltPoint(Point p, int detailLevel, bool inclParents)
+        /// <summary>
+        /// A Point-optimized implementation of
+        /// <see cref="GetCells(Shape, int, bool, bool)">GetCells(Shape, int, bool, bool)
+        /// 	</see>
+        /// . That
+        /// method in facts calls this for points.
+        /// <p/>
+        /// This implementation depends on
+        /// <see cref="GetCell(string)">GetCell(string)</see>
+        /// being fast, as its
+        /// called repeatedly when incPlarents is true.
+        /// </summary>
+        public virtual IList<Cell> GetCells(Point p, int detailLevel, bool inclParents)
         {
-            Node cell = GetNode(p, detailLevel);
+            Cell cell = GetCell(p, detailLevel);
             if (!inclParents)
             {
 #if !NET35
-                return new ReadOnlyCollectionBuilder<Node>(new[] { cell }).ToReadOnlyCollection();
+                return new ReadOnlyCollectionBuilder<Cell>(new[] { cell }).ToReadOnlyCollection();
 #else
-                return new List<Node>(new[] { cell }).AsReadOnly();
+                return new List<Cell>(new[] { cell }).AsReadOnly();
 #endif
             }
-
-            String endToken = cell.GetTokenString();
-            Debug.Assert(endToken.Length == detailLevel);
-            var cells = new List<Node>(detailLevel);
+            string endToken = cell.TokenString;
+            System.Diagnostics.Debug.Assert(endToken.Length == detailLevel);
+            IList<Cell> cells = new List<Cell>(detailLevel);
             for (int i = 1; i < detailLevel; i++)
             {
-                cells.Add(GetNode(endToken.Substring(0, i)));
+                cells.Add(GetCell(endToken.Substring(0, i)));
             }
             cells.Add(cell);
             return cells;
         }
 
-        /*
-         * Will add the trailing leaf byte for leaves. This isn't particularly efficient.
-         */
-        public static List<String> NodesToTokenStrings(Collection<Node> nodes)
+        /// <summary>Will add the trailing leaf byte for leaves.</summary>
+        /// <remarks>Will add the trailing leaf byte for leaves. This isn't particularly efficient.
+        /// 	</remarks>
+        public static IList<string> CellsToTokenStrings(ICollection<Cell> cells)
         {
-            var tokens = new List<String>((nodes.Count));
-            foreach (Node node in nodes)
+            IList<string> tokens = new List<string>((cells.Count));
+            foreach (Cell cell in cells)
             {
-                String token = node.GetTokenString();
-                if (node.IsLeaf())
+                string token = cell.TokenString;
+                if (cell.IsLeaf())
                 {
-                    tokens.Add(token + (char)Node.LEAF_BYTE);
+                    tokens.Add(token + (char)Cell.LEAF_BYTE);
                 }
                 else
                 {
@@ -271,6 +322,5 @@ namespace Lucene.Net.Spatial.Prefix.Tree
             }
             return tokens;
         }
-
     }
 }


[3/3] lucenenet git commit: Some code fixes to Spatial, merging in before re-porting from 4.8.0

Posted by sy...@apache.org.
Some code fixes to Spatial, merging in before re-porting from 4.8.0

Closes #20 and closes #12


Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/dc71f60d
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/dc71f60d
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/dc71f60d

Branch: refs/heads/master
Commit: dc71f60de96d092e6de3a8a5743f5cb75427346c
Parents: 8223d54
Author: Itamar Syn-Hershko <it...@code972.com>
Authored: Sun Mar 8 23:51:19 2015 +0200
Committer: Itamar Syn-Hershko <it...@code972.com>
Committed: Sun Mar 8 23:51:19 2015 +0200

----------------------------------------------------------------------
 src/Lucene.Net.Spatial/BBox/AreaSimilarity.cs   |  12 +-
 src/Lucene.Net.Spatial/BBox/BBoxSimilarity.cs   |   2 +-
 .../BBox/BBoxSimilarityValueSource.cs           | 102 ++--
 src/Lucene.Net.Spatial/BBox/BBoxStrategy.cs     |  48 +-
 .../BBox/DistanceSimilarity.cs                  |   4 +-
 src/Lucene.Net.Spatial/DisjointSpatialFilter.cs | 144 +++++
 .../Prefix/AbstractPrefixTreeFilter.cs          | 134 +++++
 .../Prefix/AbstractVisitingPrefixTreeFilter.cs  | 531 +++++++++++++++++++
 .../Prefix/ContainsPrefixTreeFilter.cs          | 364 +++++++++++++
 .../Prefix/IntersectsPrefixTreeFilter.cs        | 125 +++++
 .../Prefix/PointPrefixTreeFieldCacheProvider.cs |  43 +-
 .../Prefix/PrefixTreeStrategy.cs                | 213 +++++---
 .../Prefix/RecursivePrefixTreeStrategy.cs       |  91 +++-
 .../Prefix/TermQueryPrefixTreeStrategy.cs       |  52 +-
 src/Lucene.Net.Spatial/Prefix/Tree/Cell.cs      | 329 ++++++++++++
 .../Prefix/Tree/GeohashPrefixTree.cs            | 167 +++---
 src/Lucene.Net.Spatial/Prefix/Tree/Node.cs      |   6 +-
 .../Prefix/Tree/QuadPrefixTree.cs               | 343 ++++++------
 .../Prefix/Tree/SpatialPrefixTree.cs            | 360 +++++++------
 .../Prefix/Tree/SpatialPrefixTreeFactory.cs     | 109 ++--
 .../Prefix/WithinPrefixTreeFilter.cs            | 297 +++++++++++
 src/Lucene.Net.Spatial/Queries/SpatialArgs.cs   |   4 +-
 .../Queries/SpatialArgsParser.cs                |   8 +-
 .../Queries/SpatialOperation.cs                 |   8 +-
 .../Queries/UnsupportedSpatialOperation.cs      |   5 +-
 src/Lucene.Net.Spatial/SpatialStrategy.cs       |  21 +-
 .../Util/CachingDoubleValueSource.cs            |  79 +--
 .../Util/CompatibilityExtensions.cs             | 161 +-----
 src/Lucene.Net.Spatial/Util/FunctionQuery.cs    |   2 +-
 .../Util/ReciprocalFloatFunction.cs             |   4 +-
 src/Lucene.Net.Spatial/Util/ShapeFieldCache.cs  |   2 +-
 .../Util/ShapeFieldCacheDistanceValueSource.cs  |  22 +-
 .../Util/ShapeFieldCacheProvider.cs             |  87 +--
 .../Util/ValueSourceFilter.cs                   |  15 +-
 .../Vector/DistanceValueSource.cs               |  99 ++--
 .../Vector/PointVectorStrategy.cs               |  52 +-
 36 files changed, 3083 insertions(+), 962 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/BBox/AreaSimilarity.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/BBox/AreaSimilarity.cs b/src/Lucene.Net.Spatial/BBox/AreaSimilarity.cs
index fde6e10..88c8aaa 100644
--- a/src/Lucene.Net.Spatial/BBox/AreaSimilarity.cs
+++ b/src/Lucene.Net.Spatial/BBox/AreaSimilarity.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -49,9 +49,9 @@ namespace Lucene.Net.Spatial.BBox
     /// </summary>
     public class AreaSimilarity : BBoxSimilarity
     {
-       /*
-        * Properties associated with the query envelope
-        */
+        /*
+         * Properties associated with the query envelope
+         */
         private readonly Rectangle queryExtent;
         private readonly double queryArea;
 
@@ -188,7 +188,7 @@ namespace Lucene.Net.Spatial.BBox
                     //        sb.append("\n  queryFactor=").append(queryFactor).append(" targetFactor=").append(targetFactor);
                     //        sb.append(" (queryPower=").append(queryPower).append(" targetPower=").append(targetPower).append(")");
 
-                    exp.Value = (float) score;
+                    exp.Value = (float)score;
                     exp.Description = GetType().Name;
 
                     Explanation e = null;
@@ -226,6 +226,6 @@ namespace Lucene.Net.Spatial.BBox
         public override int GetHashCode()
         {
             return GetDelimiterQueryParameters().GetHashCode();
-        } 
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/BBox/BBoxSimilarity.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/BBox/BBoxSimilarity.cs b/src/Lucene.Net.Spatial/BBox/BBoxSimilarity.cs
index a18baa4..6636961 100644
--- a/src/Lucene.Net.Spatial/BBox/BBoxSimilarity.cs
+++ b/src/Lucene.Net.Spatial/BBox/BBoxSimilarity.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/BBox/BBoxSimilarityValueSource.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/BBox/BBoxSimilarityValueSource.cs b/src/Lucene.Net.Spatial/BBox/BBoxSimilarityValueSource.cs
index 6939274..fee54ad 100644
--- a/src/Lucene.Net.Spatial/BBox/BBoxSimilarityValueSource.cs
+++ b/src/Lucene.Net.Spatial/BBox/BBoxSimilarityValueSource.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -15,19 +15,19 @@
  * limitations under the License.
  */
 
+using System.Collections.Generic;
 using Lucene.Net.Index;
 using Lucene.Net.Search;
 using Lucene.Net.Search.Function;
-using Lucene.Net.Spatial.Util;
+using Lucene.Net.Util;
 using Spatial4n.Core.Shapes;
-using Spatial4n.Core.Shapes.Impl;
 
 namespace Lucene.Net.Spatial.BBox
 {
     public class BBoxSimilarityValueSource : ValueSource
     {
-        private readonly BBoxStrategy strategy;
         private readonly BBoxSimilarity similarity;
+        private readonly BBoxStrategy strategy;
 
         public BBoxSimilarityValueSource(BBoxStrategy strategy, BBoxSimilarity similarity)
         {
@@ -35,55 +35,79 @@ namespace Lucene.Net.Spatial.BBox
             this.similarity = similarity;
         }
 
-        private class BBoxSimilarityValueSourceDocValues : DocValues
+        public override string Description
+        {
+            get { return "BBoxSimilarityValueSource(" + similarity + ")"; }
+        }
+
+        public override FunctionValues GetValues(IDictionary<object, object> context, AtomicReaderContext readerContext)
+        {
+            return new BBoxSimilarityValueSourceFunctionValue(readerContext.AtomicReader, this);
+        }
+
+        public override bool Equals(object o)
+        {
+            var other = o as BBoxSimilarityValueSource;
+            if (other == null) return false;
+            return similarity.Equals(other.similarity);
+        }
+
+        public override int GetHashCode()
+        {
+            return typeof(BBoxSimilarityValueSource).GetHashCode() + similarity.GetHashCode();
+        }
+
+        #region Nested type: BBoxSimilarityValueSourceFunctionValue
+
+        private class BBoxSimilarityValueSourceFunctionValue : FunctionValues
         {
             private readonly BBoxSimilarityValueSource _enclosingInstance;
+            private readonly FieldCache.Doubles maxX, maxY;
+            private readonly FieldCache.Doubles minX, minY;
             private readonly Rectangle rect;
-            private readonly double[] minX;
-            private readonly double[] minY;
-            private readonly double[] maxX;
-            private readonly double[] maxY;
 
-            private readonly IBits validMinX, validMaxX;
+            private readonly IBits validMaxX;
+            private readonly IBits validMinX;
 
-            public BBoxSimilarityValueSourceDocValues(IndexReader reader, BBoxSimilarityValueSource enclosingInstance)
+            public BBoxSimilarityValueSourceFunctionValue(AtomicReader reader,
+                                                          BBoxSimilarityValueSource enclosingInstance)
             {
                 _enclosingInstance = enclosingInstance;
-                rect = _enclosingInstance.strategy.GetSpatialContext().MakeRectangle(0, 0, 0, 0); //reused
+                rect = _enclosingInstance.strategy.SpatialContext.MakeRectangle(0, 0, 0, 0); //reused
 
-                minX = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_minX/*, true*/);
-                minY = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_minY/*, true*/);
-                maxX = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_maxX/*, true*/);
-                maxY = FieldCache_Fields.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_maxY/*, true*/);
+                minX = FieldCache.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_minX, true);
+                minY = FieldCache.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_minY, true);
+                maxX = FieldCache.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_maxX, true);
+                maxY = FieldCache.DEFAULT.GetDoubles(reader, enclosingInstance.strategy.field_maxY, true);
 
-                validMinX = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.field_minX);
-                validMaxX = FieldCache_Fields.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.field_maxX);
+                validMinX = FieldCache.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.field_minX);
+                validMaxX = FieldCache.DEFAULT.GetDocsWithField(reader, enclosingInstance.strategy.field_maxX);
             }
 
             public override float FloatVal(int doc)
             {
                 // make sure it has minX and area
-                if (validMinX.Get(doc) && validMaxX.Get(doc))
+                if (validMinX[doc] && validMaxX[doc])
                 {
                     rect.Reset(
-                        minX[doc], maxX[doc],
-                        minY[doc], maxY[doc]);
-                    return (float) _enclosingInstance.similarity.Score(rect, null);
+                        minX.Get(doc), maxX.Get(doc),
+                        minY.Get(doc), maxY.Get(doc));
+                    return (float)_enclosingInstance.similarity.Score(rect, null);
                 }
                 else
                 {
-                    return (float) _enclosingInstance.similarity.Score(null, null);
+                    return (float)_enclosingInstance.similarity.Score(null, null);
                 }
             }
 
             public override Explanation Explain(int doc)
             {
                 // make sure it has minX and area
-                if (validMinX.Get(doc) && validMaxX.Get(doc))
+                if (validMinX[doc] && validMaxX[doc])
                 {
                     rect.Reset(
-                        minX[doc], maxX[doc],
-                        minY[doc], maxY[doc]);
+                        minX.Get(doc), maxX.Get(doc),
+                        minY.Get(doc), maxY.Get(doc));
                     var exp = new Explanation();
                     _enclosingInstance.similarity.Score(rect, exp);
                     return exp;
@@ -93,30 +117,10 @@ namespace Lucene.Net.Spatial.BBox
 
             public override string ToString(int doc)
             {
-                return _enclosingInstance.Description() + "=" + FloatVal(doc);
+                return _enclosingInstance.Description + "=" + FloatVal(doc);
             }
         }
 
-        public override DocValues GetValues(IndexReader reader)
-        {
-            return new BBoxSimilarityValueSourceDocValues(reader, this);
-        }
-
-        public override string Description()
-        {
-            return "BBoxSimilarityValueSource(" + similarity + ")";
-        }
-
-        public override bool Equals(object o)
-        {
-            var other = o as BBoxSimilarityValueSource;
-            if (other == null) return false;
-            return similarity.Equals(other.similarity);
-        }
-
-        public override int GetHashCode()
-        {
-            return typeof(BBoxSimilarityValueSource).GetHashCode() + similarity.GetHashCode();
-        }
+        #endregion
     }
-}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/BBox/BBoxStrategy.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/BBox/BBoxStrategy.cs b/src/Lucene.Net.Spatial/BBox/BBoxStrategy.cs
index f14af08..d2acf1a 100644
--- a/src/Lucene.Net.Spatial/BBox/BBoxStrategy.cs
+++ b/src/Lucene.Net.Spatial/BBox/BBoxStrategy.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -36,9 +36,9 @@ namespace Lucene.Net.Spatial.BBox
         public static String SUFFIX_XDL = "__xdl";
 
         /*
-         * The Bounding Box gets stored as four fields for x/y min/max and a flag
-         * that says if the box crosses the dateline (xdl).
-         */
+		 * The Bounding Box gets stored as four fields for x/y min/max and a flag
+		 * that says if the box crosses the dateline (xdl).
+		 */
         public readonly String field_bbox;
         public readonly String field_minX;
         public readonly String field_minY;
@@ -72,7 +72,7 @@ namespace Lucene.Net.Spatial.BBox
         // Indexing
         //---------------------------------
 
-        public override AbstractField[] CreateIndexableFields(Shape shape)
+        public override Field[] CreateIndexableFields(Shape shape)
         {
             var rect = shape as Rectangle;
             if (rect != null)
@@ -80,29 +80,28 @@ namespace Lucene.Net.Spatial.BBox
             throw new InvalidOperationException("Can only index Rectangle, not " + shape);
         }
 
-        public AbstractField[] CreateIndexableFields(Rectangle bbox)
+        public Field[] CreateIndexableFields(Rectangle bbox)
         {
-            var fields = new AbstractField[5];
+            var fields = new Field[5];
             fields[0] = DoubleField(field_minX, bbox.GetMinX());
             fields[1] = DoubleField(field_maxX, bbox.GetMaxX());
             fields[2] = DoubleField(field_minY, bbox.GetMinY());
             fields[3] = DoubleField(field_maxY, bbox.GetMaxY());
-            fields[4] = new Field(field_xdl, bbox.GetCrossesDateLine() ? "T" : "F", Field.Store.NO,
-                                  Field.Index.NOT_ANALYZED_NO_NORMS) {OmitNorms = true, OmitTermFreqAndPositions = true};
+            fields[4] = new StringField(field_xdl, bbox.GetCrossesDateLine() ? "T" : "F", Field.Store.NO);
             return fields;
         }
 
-        private AbstractField DoubleField(string field, double value)
+        private Field DoubleField(string field, double value)
         {
-            var f = new NumericField(field, precisionStep, Field.Store.NO, true)
-                        {OmitNorms = true, OmitTermFreqAndPositions = true};
-            f.SetDoubleValue(value);
-            return f;
+            return new DoubleField(field, value, new FieldType(Documents.DoubleField.TYPE_NOT_STORED)
+                                                     {
+                                                         NumericPrecisionStep = precisionStep
+                                                     });
         }
 
         public override ValueSource MakeDistanceValueSource(Point queryPoint)
         {
-            return new BBoxSimilarityValueSource(this, new DistanceSimilarity(this.GetSpatialContext(), queryPoint));
+            return new BBoxSimilarityValueSource(this, new DistanceSimilarity(this.SpatialContext, queryPoint));
         }
 
         public ValueSource MakeBBoxAreaSimilarityValueSource(Rectangle queryBox)
@@ -315,14 +314,14 @@ namespace Lucene.Net.Spatial.BBox
             Query qMinY = NumericRangeQuery.NewDoubleRange(field_minY, precisionStep, bbox.GetMinY(), bbox.GetMinY(), true, true);
             Query qMaxX = NumericRangeQuery.NewDoubleRange(field_maxX, precisionStep, bbox.GetMaxX(), bbox.GetMaxX(), true, true);
             Query qMaxY = NumericRangeQuery.NewDoubleRange(field_maxY, precisionStep, bbox.GetMaxY(), bbox.GetMaxY(), true, true);
-            
+
             var bq = new BooleanQuery
-                         {
-                             {qMinX, Occur.MUST},
-                             {qMinY, Occur.MUST},
-                             {qMaxX, Occur.MUST},
-                             {qMaxY, Occur.MUST}
-                         };
+			         	{
+			         		{qMinX, Occur.MUST},
+			         		{qMinY, Occur.MUST},
+			         		{qMaxX, Occur.MUST},
+			         		{qMaxY, Occur.MUST}
+			         	};
             return bq;
         }
 
@@ -345,7 +344,7 @@ namespace Lucene.Net.Spatial.BBox
             Query qIsNonXDL = this.MakeXDL(false);
             Query qIsXDL = this.MakeXDL(true);
             Query qHasEnv = this.MakeQuery(new Query[] { qIsNonXDL, qIsXDL }, Occur.SHOULD);
-            var qNotDisjoint = new BooleanQuery {{qHasEnv, Occur.MUST}, {qDisjoint, Occur.MUST_NOT}};
+            var qNotDisjoint = new BooleanQuery { { qHasEnv, Occur.MUST }, { qDisjoint, Occur.MUST_NOT } };
 
             //Query qDisjoint = makeDisjoint();
             //BooleanQuery qNotDisjoint = new BooleanQuery();
@@ -477,8 +476,7 @@ namespace Lucene.Net.Spatial.BBox
          */
         public Query MakeXDL(bool crossedDateLine, Query query)
         {
-            var bq = new BooleanQuery
-                         {{this.MakeXDL(crossedDateLine), Occur.MUST}, {query, Occur.MUST}};
+            var bq = new BooleanQuery { { this.MakeXDL(crossedDateLine), Occur.MUST }, { query, Occur.MUST } };
             return bq;
         }
     }

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/BBox/DistanceSimilarity.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/BBox/DistanceSimilarity.cs b/src/Lucene.Net.Spatial/BBox/DistanceSimilarity.cs
index 9b7c88b..a9fe4b1 100644
--- a/src/Lucene.Net.Spatial/BBox/DistanceSimilarity.cs
+++ b/src/Lucene.Net.Spatial/BBox/DistanceSimilarity.cs
@@ -1,4 +1,4 @@
-/*
+/*
  * 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.
@@ -52,7 +52,7 @@ namespace Lucene.Net.Spatial.BBox
             }
             if (exp != null)
             {
-                exp.Value = (float) score;
+                exp.Value = (float)score;
                 exp.Description = GetType().Name;
                 exp.AddDetail(new Explanation(-1f, "" + queryPoint));
                 exp.AddDetail(new Explanation(-1f, "" + indexRect));

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/DisjointSpatialFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/DisjointSpatialFilter.cs b/src/Lucene.Net.Spatial/DisjointSpatialFilter.cs
new file mode 100644
index 0000000..ab6fa3f
--- /dev/null
+++ b/src/Lucene.Net.Spatial/DisjointSpatialFilter.cs
@@ -0,0 +1,144 @@
+/* 
+ * 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.
+ */
+using System;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Spatial.Queries;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Spatial
+{
+    /// <summary>
+    /// A Spatial Filter implementing
+    /// <see cref="SpatialOperation.IsDisjointTo">Org.Apache.Lucene.Spatial.Query.SpatialOperation.IsDisjointTo
+    /// 	</see>
+    /// in terms
+    /// of a
+    /// <see cref="SpatialStrategy">SpatialStrategy</see>
+    /// 's support for
+    /// <see cref="SpatialOperation.Intersects">Org.Apache.Lucene.Spatial.Query.SpatialOperation.Intersects
+    /// 	</see>
+    /// .
+    /// A document is considered disjoint if it has spatial data that does not
+    /// intersect with the query shape.  Another way of looking at this is that it's
+    /// a way to invert a query shape.
+    /// </summary>
+    /// <lucene.experimental></lucene.experimental>
+    public class DisjointSpatialFilter : Filter
+    {
+        private readonly string field;
+
+        private readonly Filter intersectsFilter;
+
+        /// <param name="strategy">Needed to compute intersects</param>
+        /// <param name="args">Used in spatial intersection</param>
+        /// <param name="field">
+        /// This field is used to determine which docs have spatial data via
+        /// <see cref="Org.Apache.Lucene.Search.FieldCache.GetDocsWithField(Org.Apache.Lucene.Index.AtomicReader, string)
+        /// 	">Org.Apache.Lucene.Search.FieldCache.GetDocsWithField(Org.Apache.Lucene.Index.AtomicReader, string)
+        /// 	</see>
+        /// .
+        /// Passing null will assume all docs have spatial data.
+        /// </param>
+        public DisjointSpatialFilter(SpatialStrategy strategy, SpatialArgs args, string field
+            )
+        {
+            //maybe null
+            this.field = field;
+            // TODO consider making SpatialArgs cloneable
+            SpatialOperation origOp = args.Operation;
+            //copy so we can restore
+            args.Operation = SpatialOperation.Intersects;
+            //temporarily set to intersects
+            intersectsFilter = strategy.MakeFilter(args);
+            args.Operation = origOp;
+        }
+
+        //restore so it looks like it was
+        public override bool Equals(object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+            if (o == null || GetType() != o.GetType())
+            {
+                return false;
+            }
+            var that = (DisjointSpatialFilter)o;
+            if (field != null ? !field.Equals(that.field) : that.field != null)
+            {
+                return false;
+            }
+            if (!intersectsFilter.Equals(that.intersectsFilter))
+            {
+                return false;
+            }
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            int result = field != null ? field.GetHashCode() : 0;
+            result = 31 * result + intersectsFilter.GetHashCode();
+            return result;
+        }
+
+        /// <exception cref="System.IO.IOException"></exception>
+        public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs
+            )
+        {
+            IBits docsWithField;
+            if (field == null)
+            {
+                docsWithField = null;
+            }
+            else
+            {
+                //all docs
+                //NOTE By using the FieldCache we re-use a cache
+                // which is nice but loading it in this way might be slower than say using an
+                // intersects filter against the world bounds. So do we add a method to the
+                // strategy, perhaps?  But the strategy can't cache it.
+                docsWithField = FieldCache.DEFAULT.GetDocsWithField((context.AtomicReader), field);
+                int maxDoc = context.AtomicReader.MaxDoc;
+                if (docsWithField.Length != maxDoc)
+                {
+                    throw new InvalidOperationException("Bits length should be maxDoc (" + maxDoc + ") but wasn't: "
+                                                        + docsWithField);
+                }
+                if (docsWithField is Bits.MatchNoBits)
+                {
+                    return null;
+                }
+                else
+                {
+                    //match nothing
+                    if (docsWithField is Bits.MatchAllBits)
+                    {
+                        docsWithField = null;
+                    }
+                }
+            }
+            //all docs
+            //not so much a chain but a way to conveniently invert the Filter
+            DocIdSet docIdSet = new ChainedFilter(new[] { intersectsFilter }, ChainedFilter.ANDNOT).GetDocIdSet(context,
+                                                                                                              acceptDocs);
+            return BitsFilteredDocIdSet.Wrap(docIdSet, docsWithField);
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/AbstractPrefixTreeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/AbstractPrefixTreeFilter.cs b/src/Lucene.Net.Spatial/Prefix/AbstractPrefixTreeFilter.cs
new file mode 100644
index 0000000..efcf078
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Prefix/AbstractPrefixTreeFilter.cs
@@ -0,0 +1,134 @@
+/* 
+ * 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.
+ */
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Spatial.Prefix.Tree;
+using Lucene.Net.Util;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Prefix
+{
+    /// <summary>Base class for Lucene Filters on SpatialPrefixTree fields.</summary>
+    /// <remarks>Base class for Lucene Filters on SpatialPrefixTree fields.</remarks>
+    /// <lucene.experimental></lucene.experimental>
+    public abstract class AbstractPrefixTreeFilter : Filter
+    {
+        protected internal readonly int detailLevel;
+        protected internal readonly string fieldName;
+
+        protected internal readonly SpatialPrefixTree grid;
+        protected internal readonly Shape queryShape;
+
+        public AbstractPrefixTreeFilter(Shape queryShape, string
+                                                              fieldName, SpatialPrefixTree grid, int detailLevel)
+        {
+            //not in equals/hashCode since it's implied for a specific field
+            this.queryShape = queryShape;
+            this.fieldName = fieldName;
+            this.grid = grid;
+            this.detailLevel = detailLevel;
+        }
+
+        public override bool Equals(object o)
+        {
+            if (this == o)
+            {
+                return true;
+            }
+            if (!GetType().Equals(o.GetType()))
+            {
+                return false;
+            }
+            var that = (AbstractPrefixTreeFilter)o;
+            if (detailLevel != that.detailLevel)
+            {
+                return false;
+            }
+            if (!fieldName.Equals(that.fieldName))
+            {
+                return false;
+            }
+            if (!queryShape.Equals(that.queryShape))
+            {
+                return false;
+            }
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            int result = queryShape.GetHashCode();
+            result = 31 * result + fieldName.GetHashCode();
+            result = 31 * result + detailLevel;
+            return result;
+        }
+
+        #region Nested type: BaseTermsEnumTraverser
+
+        /// <summary>
+        /// Holds transient state and docid collecting utility methods as part of
+        /// traversing a
+        /// <see cref="TermsEnum">Lucene.Net.Index.TermsEnum</see>
+        /// .
+        /// </summary>
+        public abstract class BaseTermsEnumTraverser
+        {
+            private readonly AbstractPrefixTreeFilter _enclosing;
+            protected internal readonly AtomicReaderContext context;
+
+            protected internal readonly int maxDoc;
+            protected internal IBits acceptDocs;
+
+            protected internal DocsEnum docsEnum;
+            protected internal TermsEnum termsEnum;
+
+            /// <exception cref="System.IO.IOException"></exception>
+            public BaseTermsEnumTraverser(AbstractPrefixTreeFilter _enclosing, AtomicReaderContext
+                                                                                   context, IBits acceptDocs)
+            {
+                this._enclosing = _enclosing;
+                //remember to check for null in getDocIdSet
+                this.context = context;
+                AtomicReader reader = context.AtomicReader;
+                this.acceptDocs = acceptDocs;
+                maxDoc = reader.MaxDoc;
+                Terms terms = reader.Terms(this._enclosing.fieldName);
+                if (terms != null)
+                {
+                    termsEnum = terms.Iterator(null);
+                }
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal virtual void CollectDocs(FixedBitSet bitSet)
+            {
+                //WARN: keep this specialization in sync
+                Debug.Assert(termsEnum != null);
+                docsEnum = termsEnum.Docs(acceptDocs, docsEnum, DocsEnum.FLAG_NONE
+                    );
+                int docid;
+                while ((docid = docsEnum.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
+                {
+                    bitSet.Set(docid);
+                }
+            }
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/AbstractVisitingPrefixTreeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/AbstractVisitingPrefixTreeFilter.cs b/src/Lucene.Net.Spatial/Prefix/AbstractVisitingPrefixTreeFilter.cs
new file mode 100644
index 0000000..f187216
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Prefix/AbstractVisitingPrefixTreeFilter.cs
@@ -0,0 +1,531 @@
+/* 
+ * 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.
+ */
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Spatial.Prefix.Tree;
+using Lucene.Net.Spatial.Util;
+using Lucene.Net.Util;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Prefix
+{
+    /// <summary>
+    /// Traverses a
+    /// <see cref="Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree">Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree
+    /// 	</see>
+    /// indexed field, using the template &
+    /// visitor design patterns for subclasses to guide the traversal and collect
+    /// matching documents.
+    /// <p/>
+    /// Subclasses implement
+    /// <see cref="Lucene.Net.Search.Filter.GetDocIdSet(AtomicReaderContext, Bits)
+    /// 	">Lucene.Search.Filter.GetDocIdSet(AtomicReaderContext, Bits)
+    /// 	</see>
+    /// by instantiating a custom
+    /// <see cref="VisitorTemplate">VisitorTemplate</see>
+    /// subclass (i.e. an anonymous inner class) and implement the
+    /// required methods.
+    /// </summary>
+    /// <lucene.internal></lucene.internal>
+    public abstract class AbstractVisitingPrefixTreeFilter : AbstractPrefixTreeFilter
+    {
+        protected internal readonly int prefixGridScanLevel;
+
+        public AbstractVisitingPrefixTreeFilter(Shape queryShape
+                                                , string fieldName, SpatialPrefixTree grid, int detailLevel,
+                                                int prefixGridScanLevel
+            )
+            : base(queryShape, fieldName, grid, detailLevel)
+        {
+            //Historical note: this code resulted from a refactoring of RecursivePrefixTreeFilter,
+            // which in turn came out of SOLR-2155
+            //at least one less than grid.getMaxLevels()
+            this.prefixGridScanLevel = Math.Max(0, Math.Min(prefixGridScanLevel, grid.MaxLevels - 1));
+            Debug.Assert(detailLevel <= grid.MaxLevels);
+        }
+
+        public override bool Equals(object o)
+        {
+            if (!base.Equals(o))
+            {
+                return false;
+            }
+            //checks getClass == o.getClass & instanceof
+            var that = (AbstractVisitingPrefixTreeFilter)o;
+            if (prefixGridScanLevel != that.prefixGridScanLevel)
+            {
+                return false;
+            }
+            return true;
+        }
+
+        public override int GetHashCode()
+        {
+            int result = base.GetHashCode();
+            result = 31 * result + prefixGridScanLevel;
+            return result;
+        }
+
+        #region Nested type: VNode
+
+        /// <summary>
+        /// A Visitor Cell/Cell found via the query shape for
+        /// <see cref="VisitorTemplate">VisitorTemplate</see>
+        /// .
+        /// Sometimes these are reset(cell). It's like a LinkedList node but forms a
+        /// tree.
+        /// </summary>
+        /// <lucene.internal></lucene.internal>
+        public class VNode
+        {
+            internal readonly VNode parent;
+
+            internal Cell cell;
+            internal IEnumerator<VNode> children;
+
+            /// <summary>call reset(cell) after to set the cell.</summary>
+            /// <remarks>call reset(cell) after to set the cell.</remarks>
+            internal VNode(VNode parent)
+            {
+                //Note: The VNode tree adds more code to debug/maintain v.s. a flattened
+                // LinkedList that we used to have. There is more opportunity here for
+                // custom behavior (see preSiblings & postSiblings) but that's not
+                // leveraged yet. Maybe this is slightly more GC friendly.
+                //only null at the root
+                //null, then sometimes set, then null
+                //not null (except initially before reset())
+                // remember to call reset(cell) after
+                this.parent = parent;
+            }
+
+            internal virtual void Reset(Cell cell)
+            {
+                Debug.Assert(cell != null);
+                this.cell = cell;
+                Debug.Assert(children == null);
+            }
+        }
+
+        #endregion
+
+        #region Nested type: VisitorTemplate
+
+        /// <summary>
+        /// An abstract class designed to make it easy to implement predicates or
+        /// other operations on a
+        /// <see cref="Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree">Lucene.Net.Spatial.Prefix.Tree.SpatialPrefixTree
+        /// 	</see>
+        /// indexed field. An instance
+        /// of this class is not designed to be re-used across AtomicReaderContext
+        /// instances so simply create a new one for each call to, say a
+        /// <see cref="Lucene.Net.Search.Filter.GetDocIdSet(Lucene.Net.Index.AtomicReaderContext, Lucene.Net.Util.Bits)
+        /// 	">Lucene.Net.Search.Filter.GetDocIdSet(Lucene.Net.Index.AtomicReaderContext, Lucene.Net.Util.Bits)
+        /// 	</see>
+        /// .
+        /// The
+        /// <see cref="GetDocIdSet()">GetDocIdSet()</see>
+        /// method here starts the work. It first checks
+        /// that there are indexed terms; if not it quickly returns null. Then it calls
+        /// <see cref="Start()">Start()</see>
+        /// so a subclass can set up a return value, like an
+        /// <see cref="Lucene.Net.Util.OpenBitSet">Lucene.Net.Util.OpenBitSet</see>
+        /// . Then it starts the traversal
+        /// process, calling
+        /// <see cref="FindSubCellsToVisit(Lucene.Net.Spatial.Prefix.Tree.Cell)">FindSubCellsToVisit(Lucene.Net.Spatial.Prefix.Tree.Cell)
+        /// 	</see>
+        /// which by default finds the top cells that intersect
+        /// <code>queryShape</code>
+        /// . If
+        /// there isn't an indexed cell for a corresponding cell returned for this
+        /// method then it's short-circuited until it finds one, at which point
+        /// <see cref="Visit(Lucene.Net.Spatial.Prefix.Tree.Cell)">Visit(Lucene.Net.Spatial.Prefix.Tree.Cell)
+        /// 	</see>
+        /// is called. At
+        /// some depths, of the tree, the algorithm switches to a scanning mode that
+        /// finds calls
+        /// <see cref="VisitScanned(Lucene.Net.Spatial.Prefix.Tree.Cell)">VisitScanned(Lucene.Net.Spatial.Prefix.Tree.Cell)
+        /// 	</see>
+        /// for each leaf cell found.
+        /// </summary>
+        /// <lucene.internal></lucene.internal>
+        public abstract class VisitorTemplate : BaseTermsEnumTraverser
+        {
+            private readonly AbstractVisitingPrefixTreeFilter _enclosing;
+            private readonly BytesRef curVNodeTerm = new BytesRef();
+            protected internal readonly bool hasIndexedLeaves;
+
+            private VNode curVNode;
+
+            private Cell scanCell;
+
+            private BytesRef thisTerm;
+
+            /// <exception cref="System.IO.IOException"></exception>
+            public VisitorTemplate(AbstractVisitingPrefixTreeFilter _enclosing, AtomicReaderContext
+                                                                                    context, IBits acceptDocs,
+                                   bool hasIndexedLeaves)
+                : base(_enclosing, context, acceptDocs)
+            {
+                this._enclosing = _enclosing;
+                //if false then we can skip looking for them
+                //current pointer, derived from query shape
+                //curVNode.cell's term.
+                //the result of termsEnum.term()
+                this.hasIndexedLeaves = hasIndexedLeaves;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            public virtual DocIdSet GetDocIdSet()
+            {
+                Debug.Assert(curVNode == null, "Called more than once?");
+                if (termsEnum == null)
+                {
+                    return null;
+                }
+                //advance
+                if ((thisTerm = termsEnum.Next()) == null)
+                {
+                    return null;
+                }
+                // all done
+                curVNode = new VNode(null);
+                curVNode.Reset(_enclosing.grid.WorldCell);
+                Start();
+                AddIntersectingChildren();
+                while (thisTerm != null)
+                {
+                    //terminates for other reasons too!
+                    //Advance curVNode pointer
+                    if (curVNode.children != null)
+                    {
+                        //-- HAVE CHILDREN: DESCEND
+                        Debug.Assert(curVNode.children.MoveNext());
+                        //if we put it there then it has something
+                        PreSiblings(curVNode);
+                        curVNode = curVNode.children.Current;
+                    }
+                    else
+                    {
+                        //-- NO CHILDREN: ADVANCE TO NEXT SIBLING
+                        VNode parentVNode = curVNode.parent;
+                        while (true)
+                        {
+                            if (parentVNode == null)
+                            {
+                                goto main_break;
+                            }
+                            // all done
+                            if (parentVNode.children.MoveNext())
+                            {
+                                //advance next sibling
+                                curVNode = parentVNode.children.Current;
+                                break;
+                            }
+                            else
+                            {
+                                //reached end of siblings; pop up
+                                PostSiblings(parentVNode);
+                                parentVNode.children = null;
+                                //GC
+                                parentVNode = parentVNode.parent;
+                            }
+                        }
+                    }
+                    //Seek to curVNode's cell (or skip if termsEnum has moved beyond)
+                    curVNodeTerm.bytes = curVNode.cell.GetTokenBytes().ToSByteArray();
+                    curVNodeTerm.length = curVNodeTerm.bytes.Length;
+                    int compare = termsEnum.Comparator.Compare(thisTerm, curVNodeTerm
+                        );
+                    if (compare > 0)
+                    {
+                        // leap frog (termsEnum is beyond where we would otherwise seek)
+                        Debug.Assert(
+                            !((AtomicReader)context.Reader).Terms(_enclosing.fieldName).Iterator(null).SeekExact(
+                                curVNodeTerm, false), "should be absent"
+                            );
+                    }
+                    else
+                    {
+                        if (compare < 0)
+                        {
+                            // Seek !
+                            TermsEnum.SeekStatus seekStatus = termsEnum.SeekCeil(curVNodeTerm, true
+                                );
+                            if (seekStatus == TermsEnum.SeekStatus.END)
+                            {
+                                break;
+                            }
+                            // all done
+                            thisTerm = termsEnum.Term;
+                            if (seekStatus == TermsEnum.SeekStatus.NOT_FOUND)
+                            {
+                                continue;
+                            }
+                        }
+                        // leap frog
+                        // Visit!
+                        bool descend = Visit(curVNode.cell);
+                        //advance
+                        if ((thisTerm = termsEnum.Next()) == null)
+                        {
+                            break;
+                        }
+                        // all done
+                        if (descend)
+                        {
+                            AddIntersectingChildren();
+                        }
+                    }
+                    ;
+                }
+            main_break:
+                ;
+                //main loop
+                return Finish();
+            }
+
+            /// <summary>
+            /// Called initially, and whenever
+            /// <see cref="Visit(Lucene.Net.Spatial.Prefix.Tree.Cell)">Visit(Lucene.Net.Spatial.Prefix.Tree.Cell)
+            /// 	</see>
+            /// returns true.
+            /// </summary>
+            /// <exception cref="System.IO.IOException"></exception>
+            private void AddIntersectingChildren()
+            {
+                Debug.Assert(thisTerm != null);
+                Cell cell = curVNode.cell;
+                if (cell.Level >= _enclosing.detailLevel)
+                {
+                    throw new InvalidOperationException("Spatial logic error");
+                }
+                //Check for adjacent leaf (happens for indexed non-point shapes)
+                if (hasIndexedLeaves && cell.Level != 0)
+                {
+                    //If the next indexed term just adds a leaf marker ('+') to cell,
+                    // then add all of those docs
+                    Debug.Assert(StringHelper.StartsWith(thisTerm, curVNodeTerm
+                                     ));
+                    scanCell = _enclosing.grid.GetCell(thisTerm.bytes.ToByteArray(), thisTerm.offset
+                                                       , thisTerm.length, scanCell);
+                    if (scanCell.Level == cell.Level && scanCell.IsLeaf())
+                    {
+                        VisitLeaf(scanCell);
+                        //advance
+                        if ((thisTerm = termsEnum.Next()) == null)
+                        {
+                            return;
+                        }
+                    }
+                }
+                // all done
+                //Decide whether to continue to divide & conquer, or whether it's time to
+                // scan through terms beneath this cell.
+                // Scanning is a performance optimization trade-off.
+                //TODO use termsEnum.docFreq() as heuristic
+                bool scan = cell.Level >= _enclosing.prefixGridScanLevel;
+                //simple heuristic
+                if (!scan)
+                {
+                    //Divide & conquer (ultimately termsEnum.seek())
+                    IEnumerator<Cell> subCellsIter = FindSubCellsToVisit(cell);
+                    if (!subCellsIter.MoveNext())
+                    {
+                        //not expected
+                        return;
+                    }
+                    curVNode.children = new VNodeCellIterator
+                        (this, subCellsIter, new VNode(curVNode));
+                }
+                else
+                {
+                    //Scan (loop of termsEnum.next())
+                    Scan(_enclosing.detailLevel);
+                }
+            }
+
+            /// <summary>
+            /// Called when doing a divide & conquer to find the next intersecting cells
+            /// of the query shape that are beneath
+            /// <code>cell</code>
+            /// .
+            /// <code>cell</code>
+            /// is
+            /// guaranteed to have an intersection and thus this must return some number
+            /// of nodes.
+            /// </summary>
+            protected internal virtual IEnumerator<Cell> FindSubCellsToVisit(Cell cell)
+            {
+                return cell.GetSubCells(_enclosing.queryShape).GetEnumerator();
+            }
+
+            /// <summary>
+            /// Scans (
+            /// <code>termsEnum.next()</code>
+            /// ) terms until a term is found that does
+            /// not start with curVNode's cell. If it finds a leaf cell or a cell at
+            /// level
+            /// <code>scanDetailLevel</code>
+            /// then it calls
+            /// <see cref="VisitScanned(Lucene.Net.Spatial.Prefix.Tree.Cell)">VisitScanned(Lucene.Net.Spatial.Prefix.Tree.Cell)
+            /// 	</see>
+            /// .
+            /// </summary>
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal virtual void Scan(int scanDetailLevel)
+            {
+                for (;
+                    thisTerm != null && StringHelper.StartsWith(thisTerm, curVNodeTerm
+                                            );
+                    thisTerm = termsEnum.Next())
+                {
+                    scanCell = _enclosing.grid.GetCell(thisTerm.bytes.ToByteArray(), thisTerm.offset
+                                                       , thisTerm.length, scanCell);
+                    int termLevel = scanCell.Level;
+                    if (termLevel > scanDetailLevel)
+                    {
+                        continue;
+                    }
+                    if (termLevel == scanDetailLevel || scanCell.IsLeaf())
+                    {
+                        VisitScanned(scanCell);
+                    }
+                }
+            }
+
+            /// <summary>Called first to setup things.</summary>
+            /// <remarks>Called first to setup things.</remarks>
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal abstract void Start();
+
+            /// <summary>Called last to return the result.</summary>
+            /// <remarks>Called last to return the result.</remarks>
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal abstract DocIdSet Finish();
+
+            /// <summary>
+            /// Visit an indexed cell returned from
+            /// <see cref="FindSubCellsToVisit(Lucene.Net.Spatial.Prefix.Tree.Cell)">FindSubCellsToVisit(Lucene.Net.Spatial.Prefix.Tree.Cell)
+            /// 	</see>
+            /// .
+            /// </summary>
+            /// <param name="cell">An intersecting cell.</param>
+            /// <returns>
+            /// true to descend to more levels. It is an error to return true
+            /// if cell.level == detailLevel
+            /// </returns>
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal abstract bool Visit(Cell cell);
+
+            /// <summary>Called after visit() returns true and an indexed leaf cell is found.</summary>
+            /// <remarks>
+            /// Called after visit() returns true and an indexed leaf cell is found. An
+            /// indexed leaf cell means associated documents generally won't be found at
+            /// further detail levels.
+            /// </remarks>
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal abstract void VisitLeaf(Cell cell);
+
+            /// <summary>The cell is either indexed as a leaf or is the last level of detail.</summary>
+            /// <remarks>
+            /// The cell is either indexed as a leaf or is the last level of detail. It
+            /// might not even intersect the query shape, so be sure to check for that.
+            /// </remarks>
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal abstract void VisitScanned(Cell cell);
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal virtual void PreSiblings(VNode vNode)
+            {
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal virtual void PostSiblings(VNode vNode)
+            {
+            }
+
+            #region Nested type: VNodeCellIterator
+
+            /// <summary>
+            /// Used for
+            /// <see cref="VNode.children">VNode.children</see>
+            /// .
+            /// </summary>
+            private class VNodeCellIterator : IEnumerator<VNode>
+            {
+                private readonly VisitorTemplate _enclosing;
+                internal readonly IEnumerator<Cell> cellIter;
+
+                private readonly VNode vNode;
+
+                internal VNodeCellIterator(VisitorTemplate _enclosing, IEnumerator<Cell> cellIter, VNode vNode)
+                {
+                    this._enclosing = _enclosing;
+                    //term loop
+                    this.cellIter = cellIter;
+                    this.vNode = vNode;
+                }
+
+                //it always removes
+
+                #region IEnumerator<VNode> Members
+
+                public void Dispose()
+                {
+                    cellIter.Dispose();
+                }
+
+                public bool MoveNext()
+                {
+                    return cellIter.MoveNext();
+                }
+
+                public void Reset()
+                {
+                    cellIter.Reset();
+                }
+
+                public VNode Current
+                {
+                    get
+                    {
+                        Debug.Assert(cellIter.Current != null);
+                        vNode.Reset(cellIter.Current);
+                        return vNode;
+                    }
+                }
+
+                object IEnumerator.Current
+                {
+                    get { return Current; }
+                }
+
+                #endregion
+            }
+
+            #endregion
+
+            //class VisitorTemplate
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/ContainsPrefixTreeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/ContainsPrefixTreeFilter.cs b/src/Lucene.Net.Spatial/Prefix/ContainsPrefixTreeFilter.cs
new file mode 100644
index 0000000..bd13b79
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Prefix/ContainsPrefixTreeFilter.cs
@@ -0,0 +1,364 @@
+/* 
+ * 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.
+ */
+using System;
+using System.Collections.Generic;
+using Lucene.Net.Spatial.Queries;
+using Lucene.Net.Spatial.Util;
+using Spatial4n.Core.Shapes;
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Spatial.Prefix;
+using Lucene.Net.Spatial.Prefix.Tree;
+using Lucene.Net.Util;
+
+namespace Lucene.Net.Spatial.Prefix
+{
+    /// <summary>
+    /// Finds docs where its indexed shape
+    /// <see cref="SpatialOperation.Contains">CONTAINS</see>
+    /// the query shape. For use on
+    /// <see cref="RecursivePrefixTreeStrategy">RecursivePrefixTreeStrategy</see>
+    /// .
+    /// </summary>
+    /// <lucene.experimental></lucene.experimental>
+    public class ContainsPrefixTreeFilter : AbstractPrefixTreeFilter
+    {
+        public ContainsPrefixTreeFilter(Shape queryShape, string
+             fieldName, SpatialPrefixTree grid, int detailLevel)
+            : base(queryShape, fieldName, grid, detailLevel)
+        {
+        }
+
+        /// <exception cref="System.IO.IOException"></exception>
+        public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs
+            )
+        {
+            return new ContainsVisitor(this, context, acceptDocs).Visit(grid.WorldCell, acceptDocs);
+        }
+
+        private class ContainsVisitor : BaseTermsEnumTraverser
+        {
+            /// <exception cref="System.IO.IOException"></exception>
+            public ContainsVisitor(ContainsPrefixTreeFilter _enclosing, AtomicReaderContext context
+                , IBits acceptDocs)
+                : base(_enclosing, context, acceptDocs)
+            {
+                this._enclosing = _enclosing;
+            }
+
+            internal BytesRef termBytes = new BytesRef();
+
+            internal Cell nextCell;
+
+            //see getLeafDocs
+            /// <summary>This is the primary algorithm; recursive.</summary>
+            /// <remarks>This is the primary algorithm; recursive.  Returns null if finds none.</remarks>
+            /// <exception cref="System.IO.IOException"></exception>
+            internal SmallDocSet Visit(Cell cell, IBits acceptContains
+                )
+            {
+                if (termsEnum == null)
+                {
+                    //signals all done
+                    return null;
+                }
+                //Leaf docs match all query shape
+                SmallDocSet leafDocs = GetLeafDocs(cell, acceptContains);
+                // Get the AND of all child results
+                SmallDocSet combinedSubResults = null;
+                ICollection<Cell> subCells = cell.GetSubCells(_enclosing.queryShape);
+                foreach (Cell subCell in subCells)
+                {
+                    if (!SeekExact(subCell))
+                    {
+                        combinedSubResults = null;
+                    }
+                    else
+                    {
+                        if (subCell.Level == _enclosing.detailLevel)
+                        {
+                            combinedSubResults = GetDocs(subCell, acceptContains);
+                        }
+                        else
+                        {
+                            if (subCell.GetShapeRel() == SpatialRelation.WITHIN)
+                            {
+                                combinedSubResults = GetLeafDocs(subCell, acceptContains);
+                            }
+                            else
+                            {
+                                combinedSubResults = Visit(subCell, acceptContains);
+                            }
+                        }
+                    }
+                    //recursion
+                    if (combinedSubResults == null)
+                    {
+                        break;
+                    }
+                    acceptContains = combinedSubResults;
+                }
+                //has the 'AND' effect on next iteration
+                // Result: OR the leaf docs with AND of all child results
+                if (combinedSubResults != null)
+                {
+                    if (leafDocs == null)
+                    {
+                        return combinedSubResults;
+                    }
+                    return leafDocs.Union(combinedSubResults);
+                }
+                return leafDocs;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            private bool SeekExact(Cell cell)
+            {
+                System.Diagnostics.Debug.Assert(new BytesRef(cell.GetTokenBytes().ToSByteArray()).CompareTo(this
+                    .termBytes) > 0);
+                this.termBytes.bytes = cell.GetTokenBytes().ToSByteArray();
+                this.termBytes.length = this.termBytes.bytes.Length;
+                return this.termsEnum.SeekExact(this.termBytes, cell.Level <= 2);
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            private ContainsPrefixTreeFilter.SmallDocSet GetDocs(Cell cell, IBits acceptContains
+                )
+            {
+                System.Diagnostics.Debug.Assert(new BytesRef(cell.GetTokenBytes().ToSByteArray()).Equals(this.termBytes
+                    ));
+                return this.CollectDocs(acceptContains);
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            private ContainsPrefixTreeFilter.SmallDocSet GetLeafDocs(Cell leafCell, IBits acceptContains)
+            {
+                System.Diagnostics.Debug.Assert(new BytesRef(leafCell.GetTokenBytes().ToSByteArray()).Equals(this
+                    .termBytes));
+                BytesRef nextTerm = this.termsEnum.Next();
+                if (nextTerm == null)
+                {
+                    this.termsEnum = null;
+                    //signals all done
+                    return null;
+                }
+                this.nextCell = this._enclosing.grid.GetCell(nextTerm.bytes.ToByteArray(), nextTerm.offset, nextTerm
+                    .length, this.nextCell);
+                if (this.nextCell.Level == leafCell.Level && this.nextCell.IsLeaf())
+                {
+                    return this.CollectDocs(acceptContains);
+                }
+                else
+                {
+                    return null;
+                }
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            private ContainsPrefixTreeFilter.SmallDocSet CollectDocs(IBits acceptContains)
+            {
+                ContainsPrefixTreeFilter.SmallDocSet set = null;
+                this.docsEnum = this.termsEnum.Docs(acceptContains, this.docsEnum, DocsEnum.FLAG_NONE
+                    );
+                int docid;
+                while ((docid = this.docsEnum.NextDoc()) != DocIdSetIterator.NO_MORE_DOCS)
+                {
+                    if (set == null)
+                    {
+                        int size = this.termsEnum.DocFreq;
+                        if (size <= 0)
+                        {
+                            size = 16;
+                        }
+                        set = new ContainsPrefixTreeFilter.SmallDocSet(size);
+                    }
+                    set.Set(docid);
+                }
+                return set;
+            }
+
+            private readonly ContainsPrefixTreeFilter _enclosing;
+            //class ContainsVisitor
+        }
+
+        /// <summary>A hash based mutable set of docIds.</summary>
+        /// <remarks>
+        /// A hash based mutable set of docIds. If this were Solr code then we might
+        /// use a combination of HashDocSet and SortedIntDocSet instead.
+        /// </remarks>
+        private class SmallDocSet : DocIdSet, Lucene.Net.Util.IBits
+        {
+            private readonly SentinelIntSet intSet;
+
+            private int maxInt = 0;
+
+            public SmallDocSet(int size)
+            {
+                intSet = new SentinelIntSet(size, -1);
+            }
+
+            public virtual void Set(int index)
+            {
+                intSet.Put(index);
+                if (index > maxInt)
+                {
+                    maxInt = index;
+                }
+            }
+
+            /// <summary>Largest docid.</summary>
+            /// <remarks>Largest docid.</remarks>
+            public int Length
+            {
+                get
+                {
+                    return maxInt;
+                }
+            }
+
+            /// <summary>Number of docids.</summary>
+            /// <remarks>Number of docids.</remarks>
+            public virtual int Size()
+            {
+                return intSet.Size;
+            }
+
+            /// <summary>NOTE: modifies and returns either "this" or "other"</summary>
+            public virtual ContainsPrefixTreeFilter.SmallDocSet Union(ContainsPrefixTreeFilter.SmallDocSet
+                 other)
+            {
+                ContainsPrefixTreeFilter.SmallDocSet bigger;
+                ContainsPrefixTreeFilter.SmallDocSet smaller;
+                if (other.intSet.Size > this.intSet.Size)
+                {
+                    bigger = other;
+                    smaller = this;
+                }
+                else
+                {
+                    bigger = this;
+                    smaller = other;
+                }
+                //modify bigger
+                foreach (int v in smaller.intSet.keys)
+                {
+                    if (v == smaller.intSet.emptyVal)
+                    {
+                        continue;
+                    }
+                    bigger.Set(v);
+                }
+                return bigger;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            public override Lucene.Net.Util.IBits Bits
+            {
+                get
+                {
+                    //if the # of docids is super small, return null since iteration is going
+                    // to be faster
+                    return Size() > 4 ? this : null;
+                }
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            public override DocIdSetIterator Iterator()
+            {
+                if (Size() == 0)
+                {
+                    return null;
+                }
+                //copy the unsorted values to a new array then sort them
+                int d = 0;
+                int[] docs = new int[intSet.Size];
+                foreach (int v in intSet.keys)
+                {
+                    if (v == intSet.emptyVal)
+                    {
+                        continue;
+                    }
+                    docs[d++] = v;
+                }
+                System.Diagnostics.Debug.Assert(d == intSet.Size);
+                int size = d;
+                //sort them
+                Array.Sort(docs, 0, size);
+                return new _DocIdSetIterator_225(size, docs);
+            }
+
+            private sealed class _DocIdSetIterator_225 : DocIdSetIterator
+            {
+                public _DocIdSetIterator_225(int size, int[] docs)
+                {
+                    this.size = size;
+                    this.docs = docs;
+                    this.idx = -1;
+                }
+
+                internal int idx;
+
+                public override int DocID
+                {
+                    get
+                    {
+                        if (this.idx >= 0 && this.idx < size)
+                        {
+                            return docs[this.idx];
+                        }
+                        else
+                        {
+                            return -1;
+                        }
+                    }
+                }
+
+                /// <exception cref="System.IO.IOException"></exception>
+                public override int NextDoc()
+                {
+                    if (++this.idx < size)
+                    {
+                        return docs[this.idx];
+                    }
+                    return DocIdSetIterator.NO_MORE_DOCS;
+                }
+
+                /// <exception cref="System.IO.IOException"></exception>
+                public override int Advance(int target)
+                {
+                    //for this small set this is likely faster vs. a binary search
+                    // into the sorted array
+                    return this.SlowAdvance(target);
+                }
+
+                public override long Cost
+                {
+                    get { return size; }
+                }
+
+                private readonly int size;
+
+                private readonly int[] docs;
+            }
+            //class SmallDocSet
+            public bool this[int index]
+            {
+                get { return intSet.Exists(index); }
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/IntersectsPrefixTreeFilter.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/IntersectsPrefixTreeFilter.cs b/src/Lucene.Net.Spatial/Prefix/IntersectsPrefixTreeFilter.cs
new file mode 100644
index 0000000..7201167
--- /dev/null
+++ b/src/Lucene.Net.Spatial/Prefix/IntersectsPrefixTreeFilter.cs
@@ -0,0 +1,125 @@
+/* 
+ * 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.
+ */
+using Lucene.Net.Index;
+using Lucene.Net.Search;
+using Lucene.Net.Spatial.Prefix.Tree;
+using Lucene.Net.Util;
+using Spatial4n.Core.Shapes;
+
+namespace Lucene.Net.Spatial.Prefix
+{
+    /// <summary>
+    /// A Filter matching documents that have an
+    /// <see cref="SpatialRelation.Intersects">SpatialRelation.Intersects
+    /// 	</see>
+    /// (i.e. not DISTINCT) relationship with a provided query shape.
+    /// </summary>
+    /// <lucene.internal></lucene.internal>
+    public class IntersectsPrefixTreeFilter : AbstractVisitingPrefixTreeFilter
+    {
+        private readonly bool hasIndexedLeaves;
+
+        public IntersectsPrefixTreeFilter(Shape queryShape, string
+                                                                fieldName, SpatialPrefixTree grid, int detailLevel,
+                                          int prefixGridScanLevel, bool
+                                                                       hasIndexedLeaves)
+            : base(queryShape, fieldName, grid, detailLevel, prefixGridScanLevel)
+        {
+            this.hasIndexedLeaves = hasIndexedLeaves;
+        }
+
+        public override bool Equals(object o)
+        {
+            return base.Equals(o) && hasIndexedLeaves == ((IntersectsPrefixTreeFilter)o).hasIndexedLeaves;
+        }
+
+        /// <exception cref="System.IO.IOException"></exception>
+        public override DocIdSet GetDocIdSet(AtomicReaderContext context, IBits acceptDocs
+            )
+        {
+            return new _VisitorTemplate_55(this, context, acceptDocs, hasIndexedLeaves).GetDocIdSet
+                ();
+        }
+
+        #region Nested type: _VisitorTemplate_55
+
+        private sealed class _VisitorTemplate_55 : VisitorTemplate
+        {
+            private readonly IntersectsPrefixTreeFilter _enclosing;
+            private FixedBitSet results;
+
+            public _VisitorTemplate_55(IntersectsPrefixTreeFilter _enclosing, AtomicReaderContext
+                                                                                  baseArg1, IBits baseArg2,
+                                       bool baseArg3)
+                : base(_enclosing, baseArg1, baseArg2, baseArg3)
+            {
+                this._enclosing = _enclosing;
+            }
+
+            protected internal override void Start()
+            {
+                results = new FixedBitSet(maxDoc);
+            }
+
+            protected internal override DocIdSet Finish()
+            {
+                return results;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal override bool Visit(Cell cell)
+            {
+                if (cell.GetShapeRel() == SpatialRelation.WITHIN || cell.Level == _enclosing
+                                                                                      .detailLevel)
+                {
+                    CollectDocs(results);
+                    return false;
+                }
+                return true;
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal override void VisitLeaf(Cell cell)
+            {
+                CollectDocs(results);
+            }
+
+            /// <exception cref="System.IO.IOException"></exception>
+            protected internal override void VisitScanned(Cell cell)
+            {
+                Shape cShape;
+                //if this cell represents a point, use the cell center vs the box
+                // TODO this behavior is debatable; might want to be configurable
+                // (points never have isLeaf())
+                if (cell.Level == _enclosing.grid.MaxLevels && !cell.IsLeaf())
+                {
+                    cShape = cell.GetCenter();
+                }
+                else
+                {
+                    cShape = cell.GetShape();
+                }
+                if (_enclosing.queryShape.Relate(cShape).Intersects())
+                {
+                    CollectDocs(results);
+                }
+            }
+        }
+
+        #endregion
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/lucenenet/blob/dc71f60d/src/Lucene.Net.Spatial/Prefix/PointPrefixTreeFieldCacheProvider.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net.Spatial/Prefix/PointPrefixTreeFieldCacheProvider.cs b/src/Lucene.Net.Spatial/Prefix/PointPrefixTreeFieldCacheProvider.cs
index 5a7c554..acf6f96 100644
--- a/src/Lucene.Net.Spatial/Prefix/PointPrefixTreeFieldCacheProvider.cs
+++ b/src/Lucene.Net.Spatial/Prefix/PointPrefixTreeFieldCacheProvider.cs
@@ -1,51 +1,64 @@
-/*
+/* 
  * 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
- *
+ * 
+ * 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.
  */
-
 using System;
 using Lucene.Net.Index;
 using Lucene.Net.Spatial.Prefix.Tree;
 using Lucene.Net.Spatial.Util;
+using Lucene.Net.Util;
 using Spatial4n.Core.Shapes;
 
 namespace Lucene.Net.Spatial.Prefix
 {
     /// <summary>
-    /// Implementation of {@link ShapeFieldCacheProvider} designed for {@link PrefixTreeStrategy}s.
-    /// 
+    /// Implementation of
+    /// <see cref="Lucene.Net.Spatial.Util.ShapeFieldCacheProvider{T}">Lucene.Net.Spatial.Util.ShapeFieldCacheProvider&lt;T&gt;
+    /// 	</see>
+    /// designed for
+    /// <see cref="PrefixTreeStrategy">PrefixTreeStrategy</see>
+    /// s.
     /// Note, due to the fragmented representation of Shapes in these Strategies, this implementation
-    /// can only retrieve the central {@link Point} of the original Shapes.
+    /// can only retrieve the central
+    /// <see cref="Point">Point</see>
+    /// of the original Shapes.
     /// </summary>
+    /// <lucene.internal></lucene.internal>
     public class PointPrefixTreeFieldCacheProvider : ShapeFieldCacheProvider<Point>
     {
-        readonly SpatialPrefixTree grid; //
+        internal readonly SpatialPrefixTree grid;
 
-        public PointPrefixTreeFieldCacheProvider(SpatialPrefixTree grid, String shapeField, int defaultSize)
+        public PointPrefixTreeFieldCacheProvider(SpatialPrefixTree grid, string shapeField
+            , int defaultSize)
             : base(shapeField, defaultSize)
         {
+            //
             this.grid = grid;
         }
 
-        //A kluge that this is a field
-        private Node scanCell = null;
+        private Cell scanCell = null;
 
-        protected override Point ReadShape(Term term)
+        //re-used in readShape to save GC
+        protected internal override Point ReadShape(BytesRef term)
         {
-            scanCell = grid.GetNode(term.Text, scanCell);
-            return scanCell.IsLeaf() ? scanCell.GetShape().GetCenter() : null;
+            scanCell = grid.GetCell(term.bytes.ToByteArray(), term.offset, term.length, scanCell);
+            if (scanCell.Level == grid.MaxLevels && !scanCell.IsLeaf())
+            {
+                return scanCell.GetCenter();
+            }
+            return null;
         }
     }
 }