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<T></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 >=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">>= 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>> 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 > 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<T>
+ /// </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;
}
}
}