Lucene.Net  3.0.3
Lucene.Net is a .NET port of the Java Lucene Indexing Library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Properties
PointVectorStrategy.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 Lucene.Net.Documents;
20 using Lucene.Net.Search;
21 using Lucene.Net.Search.Function;
22 using Lucene.Net.Spatial.Queries;
23 using Lucene.Net.Spatial.Util;
24 using Spatial4n.Core.Context;
25 using Spatial4n.Core.Shapes;
26 
27 namespace Lucene.Net.Spatial.Vector
28 {
39  public class PointVectorStrategy : SpatialStrategy
40  {
41  public static String SUFFIX_X = "__x";
42  public static String SUFFIX_Y = "__y";
43 
44  private readonly String fieldNameX;
45  private readonly String fieldNameY;
46 
47  public int precisionStep = 8; // same as solr default
48 
49  public PointVectorStrategy(SpatialContext ctx, String fieldNamePrefix)
50  : base(ctx, fieldNamePrefix)
51  {
52  this.fieldNameX = fieldNamePrefix + SUFFIX_X;
53  this.fieldNameY = fieldNamePrefix + SUFFIX_Y;
54  }
55 
56  public void SetPrecisionStep(int p)
57  {
58  precisionStep = p;
59  if (precisionStep <= 0 || precisionStep >= 64)
60  precisionStep = int.MaxValue;
61  }
62 
63  public string GetFieldNameX()
64  {
65  return fieldNameX;
66  }
67 
68  public string GetFieldNameY()
69  {
70  return fieldNameY;
71  }
72 
73  public override AbstractField[] CreateIndexableFields(Shape shape)
74  {
75  var point = shape as Point;
76  if (point != null)
77  return CreateIndexableFields(point);
78 
79  throw new InvalidOperationException("Can only index Point, not " + shape);
80  }
81 
82  public AbstractField[] CreateIndexableFields(Point point)
83  {
84  var f = new AbstractField[2];
85 
86  var f0 = new NumericField(fieldNameX, precisionStep, Field.Store.NO, true)
87  {OmitNorms = true, OmitTermFreqAndPositions = true};
88  f0.SetDoubleValue(point.GetX());
89  f[0] = f0;
90 
91  var f1 = new NumericField(fieldNameY, precisionStep, Field.Store.NO, true)
92  {OmitNorms = true, OmitTermFreqAndPositions = true};
93  f1.SetDoubleValue(point.GetY());
94  f[1] = f1;
95 
96  return f;
97  }
98 
99  public override ValueSource MakeDistanceValueSource(Point queryPoint)
100  {
101  return new DistanceValueSource(this, queryPoint);
102  }
103 
104  public override ConstantScoreQuery MakeQuery(SpatialArgs args)
105  {
106  if (!SpatialOperation.Is(args.Operation,
109  throw new UnsupportedSpatialOperation(args.Operation);
110 
111  Shape shape = args.Shape;
112  var bbox = shape as Rectangle;
113  if (bbox != null)
114  return new ConstantScoreQuery(new QueryWrapperFilter(MakeWithin(bbox)));
115 
116  var circle = shape as Circle;
117  if (circle != null)
118  {
119  bbox = circle.GetBoundingBox();
120  var vsf = new ValueSourceFilter(
121  new QueryWrapperFilter(MakeWithin(bbox)),
122  MakeDistanceValueSource(circle.GetCenter()),
123  0,
124  circle.GetRadius());
125  return new ConstantScoreQuery(vsf);
126  }
127 
128  throw new InvalidOperationException("Only Rectangles and Circles are currently supported, " +
129  "found [" + shape.GetType().Name + "]"); //TODO
130  }
131 
132  //TODO this is basically old code that hasn't been verified well and should probably be removed
133  public Query MakeQueryDistanceScore(SpatialArgs args)
134  {
135  // For starters, just limit the bbox
136  var shape = args.Shape;
137  if (!(shape is Rectangle || shape is Circle))
138  throw new InvalidOperationException("Only Rectangles and Circles are currently supported, found ["
139  + shape.GetType().Name + "]");//TODO
140 
141  Rectangle bbox = shape.GetBoundingBox();
142  if (bbox.GetCrossesDateLine())
143  {
144  throw new InvalidOperationException("Crossing dateline not yet supported");
145  }
146 
147  ValueSource valueSource = null;
148 
149  Query spatial = null;
150  SpatialOperation op = args.Operation;
151 
152  if (SpatialOperation.Is(op,
155  {
156  spatial = MakeWithin(bbox);
157  }
158  else if (SpatialOperation.Is(op,
161  {
162  spatial = MakeWithin(bbox);
163  var circle = args.Shape as Circle;
164  if (circle != null)
165  {
166  // Make the ValueSource
167  valueSource = MakeDistanceValueSource(shape.GetCenter());
168 
169  var vsf = new ValueSourceFilter(
170  new QueryWrapperFilter(spatial), valueSource, 0, circle.GetRadius());
171 
172  spatial = new FilteredQuery(new MatchAllDocsQuery(), vsf);
173  }
174  }
175  else if (op == SpatialOperation.IsDisjointTo)
176  {
177  spatial = MakeDisjoint(bbox);
178  }
179 
180  if (spatial == null)
181  {
182  throw new UnsupportedSpatialOperation(args.Operation);
183  }
184 
185  if (valueSource != null)
186  {
187  valueSource = new CachingDoubleValueSource(valueSource);
188  }
189  else
190  {
191  valueSource = MakeDistanceValueSource(shape.GetCenter());
192  }
193  Query spatialRankingQuery = new FunctionQuery(valueSource);
194  var bq = new BooleanQuery();
195  bq.Add(spatial, Occur.MUST);
196  bq.Add(spatialRankingQuery, Occur.MUST);
197  return bq;
198 
199  }
200 
201  public override Filter MakeFilter(SpatialArgs args)
202  {
203  //unwrap the CSQ from makeQuery
204  ConstantScoreQuery csq = MakeQuery(args);
205  Filter filter = csq.Filter;
206  if (filter != null)
207  return filter;
208  else
209  return new QueryWrapperFilter(csq);
210  }
211 
216  private Query MakeWithin(Rectangle bbox)
217  {
218  var bq = new BooleanQuery();
219  const Occur MUST = Occur.MUST;
220  if (bbox.GetCrossesDateLine())
221  {
222  //use null as performance trick since no data will be beyond the world bounds
223  bq.Add(RangeQuery(fieldNameX, null /*-180*/, bbox.GetMaxX()), Occur.SHOULD);
224  bq.Add(RangeQuery(fieldNameX, bbox.GetMinX(), null /*+180*/), Occur.SHOULD);
225  bq.MinimumNumberShouldMatch = 1; //must match at least one of the SHOULD
226  }
227  else
228  {
229  bq.Add(RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX()), MUST);
230  }
231  bq.Add(RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY()), MUST);
232  return bq;
233  }
234 
235  private NumericRangeQuery<Double> RangeQuery(String fieldName, double? min, double? max)
236  {
237  return NumericRangeQuery.NewDoubleRange(
238  fieldName,
239  precisionStep,
240  min,
241  max,
242  true,
243  true); //inclusive
244  }
245 
250  private Query MakeDisjoint(Rectangle bbox)
251  {
252  if (bbox.GetCrossesDateLine())
253  throw new InvalidOperationException("MakeDisjoint doesn't handle dateline cross");
254  Query qX = RangeQuery(fieldNameX, bbox.GetMinX(), bbox.GetMaxX());
255  Query qY = RangeQuery(fieldNameY, bbox.GetMinY(), bbox.GetMaxY());
256  var bq = new BooleanQuery {{qX, Occur.MUST_NOT}, {qY, Occur.MUST_NOT}};
257  return bq;
258  }
259  }
260 }