Lucene.Net  3.0.3
Lucene.Net is a port of the Lucene search engine library, written in C# and targeted at .NET runtime users.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties Pages
QuadPrefixTree.cs
Go to the documentation of this file.
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 using System;
19 using System.Collections.Generic;
20 using System.Diagnostics;
21 using System.Text;
22 using Spatial4n.Core.Context;
23 using Spatial4n.Core.Shapes;
24 
25 namespace Lucene.Net.Spatial.Prefix.Tree
26 {
27  /// <summary>
28  /// Implementation of {@link SpatialPrefixTree} which uses a quad tree
29  /// (http://en.wikipedia.org/wiki/Quadtree)
30  /// </summary>
32  {
33  /// <summary>
34  /// Factory for creating {@link QuadPrefixTree} instances with useful defaults
35  /// </summary>
37  {
38  protected override int GetLevelForDistance(double degrees)
39  {
40  var grid = new QuadPrefixTree(ctx, MAX_LEVELS_POSSIBLE);
41  return grid.GetLevelForDistance(degrees);
42  }
43 
44  protected override SpatialPrefixTree NewSPT()
45  {
46  return new QuadPrefixTree(ctx, maxLevels != null ? maxLevels.Value : MAX_LEVELS_POSSIBLE);
47  }
48  }
49 
50  public static readonly int MAX_LEVELS_POSSIBLE = 50;//not really sure how big this should be
51 
52  public static readonly int DEFAULT_MAX_LEVELS = 12;
53  private double xmin;
54  private double xmax;
55  private double ymin;
56  private double ymax;
57  private double xmid;
58  private double ymid;
59 
60  private double gridW;
61  private double gridH;
62 
63  double[] levelW;
64  double[] levelH;
65  int[] levelS; // side
66  int[] levelN; // number
67 
68  public QuadPrefixTree(SpatialContext ctx, Rectangle bounds, int maxLevels)
69  : base(ctx, maxLevels)
70  {
71  Init(ctx, bounds, maxLevels);
72  }
73 
74  public QuadPrefixTree(SpatialContext ctx)
75  : base(ctx, DEFAULT_MAX_LEVELS)
76  {
77  Init(ctx, ctx.GetWorldBounds(), DEFAULT_MAX_LEVELS);
78  }
79 
80  public QuadPrefixTree(SpatialContext ctx, int maxLevels)
81  : base(ctx, maxLevels)
82  {
83  Init(ctx, ctx.GetWorldBounds(), maxLevels);
84  }
85 
86  protected void Init(SpatialContext ctx, Rectangle bounds, int maxLevels)
87  {
88  this.xmin = bounds.GetMinX();
89  this.xmax = bounds.GetMaxX();
90  this.ymin = bounds.GetMinY();
91  this.ymax = bounds.GetMaxY();
92 
93  levelW = new double[maxLevels];
94  levelH = new double[maxLevels];
95  levelS = new int[maxLevels];
96  levelN = new int[maxLevels];
97 
98  gridW = xmax - xmin;
99  gridH = ymax - ymin;
100  xmid = xmin + gridW / 2.0;
101  ymid = ymin + gridH / 2.0;
102  levelW[0] = gridW / 2.0;
103  levelH[0] = gridH / 2.0;
104  levelS[0] = 2;
105  levelN[0] = 4;
106 
107  for (int i = 1; i < levelW.Length; i++)
108  {
109  levelW[i] = levelW[i - 1] / 2.0;
110  levelH[i] = levelH[i - 1] / 2.0;
111  levelS[i] = levelS[i - 1] * 2;
112  levelN[i] = levelN[i - 1] * 4;
113  }
114 
115  }
116 
117  public override int GetLevelForDistance(double dist)
118  {
119  if (dist == 0)//short circuit
120  return maxLevels;
121  for (int i = 0; i < maxLevels - 1; i++)
122  {
123  //note: level[i] is actually a lookup for level i+1
124  if (dist > levelW[i] && dist > levelH[i])
125  {
126  return i + 1;
127  }
128  }
129  return maxLevels;
130  }
131 
132  protected override Node GetNode(Point p, int level)
133  {
134  var cells = new List<Node>(1);
135  Build(xmid, ymid, 0, cells, new StringBuilder(), ctx.MakePoint(p.GetX(), p.GetY()), level);
136  return cells[0];//note cells could be longer if p on edge
137  }
138 
139  public override Node GetNode(string token)
140  {
141  return new QuadCell(token, this);
142  }
143 
144  public override Node GetNode(byte[] bytes, int offset, int len)
145  {
146  throw new System.NotImplementedException();
147  }
148 
149  public override IList<Node> GetNodes(Shape shape, int detailLevel, bool inclParents)
150  {
151  var point = shape as Point;
152  if (point != null)
153  return base.GetNodesAltPoint(point, detailLevel, inclParents);
154  else
155  return base.GetNodes(shape, detailLevel, inclParents);
156  }
157 
158  private void Build(double x, double y, int level, List<Node> matches, StringBuilder str, Shape shape, int maxLevel)
159  {
160  Debug.Assert(str.Length == level);
161  double w = levelW[level] / 2;
162  double h = levelH[level] / 2;
163 
164  // Z-Order
165  // http://en.wikipedia.org/wiki/Z-order_%28curve%29
166  CheckBattenberg('A', x - w, y + h, level, matches, str, shape, maxLevel);
167  CheckBattenberg('B', x + w, y + h, level, matches, str, shape, maxLevel);
168  CheckBattenberg('C', x - w, y - h, level, matches, str, shape, maxLevel);
169  CheckBattenberg('D', x + w, y - h, level, matches, str, shape, maxLevel);
170 
171  // possibly consider hilbert curve
172  // http://en.wikipedia.org/wiki/Hilbert_curve
173  // http://blog.notdot.net/2009/11/Damn-Cool-Algorithms-Spatial-indexing-with-Quadtrees-and-Hilbert-Curves
174  // if we actually use the range property in the query, this could be useful
175  }
176 
177  private void CheckBattenberg(
178  char c,
179  double cx,
180  double cy,
181  int level,
182  List<Node> matches,
183  StringBuilder str,
184  Shape shape,
185  int maxLevel)
186  {
187  Debug.Assert(str.Length == level);
188  double w = levelW[level] / 2;
189  double h = levelH[level] / 2;
190 
191  int strlen = str.Length;
192  Rectangle rectangle = ctx.MakeRectangle(cx - w, cx + w, cy - h, cy + h);
193  SpatialRelation v = shape.Relate(rectangle);
194  if (SpatialRelation.CONTAINS == v)
195  {
196  str.Append(c);
197  //str.append(SpatialPrefixGrid.COVER);
198  matches.Add(new QuadCell(str.ToString(), v.Transpose(), this));
199  }
200  else if (SpatialRelation.DISJOINT == v)
201  {
202  // nothing
203  }
204  else
205  { // SpatialRelation.WITHIN, SpatialRelation.INTERSECTS
206  str.Append(c);
207 
208  int nextLevel = level + 1;
209  if (nextLevel >= maxLevel)
210  {
211  //str.append(SpatialPrefixGrid.INTERSECTS);
212  matches.Add(new QuadCell(str.ToString(), v.Transpose(), this));
213  }
214  else
215  {
216  Build(cx, cy, nextLevel, matches, str, shape, maxLevel);
217  }
218  }
219  str.Length = strlen;
220  }
221 
222  public class QuadCell : Node
223  {
224 
225  public QuadCell(String token, QuadPrefixTree enclosingInstance)
226  : base(enclosingInstance, token)
227  {
228  }
229 
230  public QuadCell(String token, SpatialRelation shapeRel, QuadPrefixTree enclosingInstance)
231  : base(enclosingInstance, token)
232  {
233  this.shapeRel = shapeRel;
234  }
235 
236  public override void Reset(string newToken)
237  {
238  base.Reset(newToken);
239  shape = null;
240  }
241 
242  public override IList<Node> GetSubCells()
243  {
244  var tree = (QuadPrefixTree)spatialPrefixTree;
245  var cells = new List<Node>(4)
246  {
247  new QuadCell(GetTokenString() + "A", tree),
248  new QuadCell(GetTokenString() + "B", tree),
249  new QuadCell(GetTokenString() + "C", tree),
250  new QuadCell(GetTokenString() + "D", tree)
251  };
252  return cells;
253  }
254 
255  public override int GetSubCellsSize()
256  {
257  return 4;
258  }
259 
260  public override Node GetSubCell(Point p)
261  {
262  return ((QuadPrefixTree)spatialPrefixTree).GetNode(p, GetLevel() + 1); //not performant!
263  }
264 
265  private Shape shape;//cache
266 
267  public override Shape GetShape()
268  {
269  if (shape == null)
270  shape = MakeShape();
271  return shape;
272  }
273 
274  private Rectangle MakeShape()
275  {
276  String token = GetTokenString();
277  var tree = ((QuadPrefixTree)spatialPrefixTree);
278  double xmin = tree.xmin;
279  double ymin = tree.ymin;
280 
281  for (int i = 0; i < token.Length; i++)
282  {
283  char c = token[i];
284  if ('A' == c || 'a' == c)
285  {
286  ymin += tree.levelH[i];
287  }
288  else if ('B' == c || 'b' == c)
289  {
290  xmin += tree.levelW[i];
291  ymin += tree.levelH[i];
292  }
293  else if ('C' == c || 'c' == c)
294  {
295  // nothing really
296  }
297  else if ('D' == c || 'd' == c)
298  {
299  xmin += tree.levelW[i];
300  }
301  else
302  {
303  throw new Exception("unexpected char: " + c);
304  }
305  }
306  int len = token.Length;
307  double width, height;
308  if (len > 0)
309  {
310  width = tree.levelW[len - 1];
311  height = tree.levelH[len - 1];
312  }
313  else
314  {
315  width = tree.gridW;
316  height = tree.gridH;
317  }
318  return spatialPrefixTree.ctx.MakeRectangle(xmin, xmin + width, ymin, ymin + height);
319  }
320  }//QuadCell
321 
322  }
323 }