1 // Copyright 2013 Google Inc. All Rights Reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS-IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 //
15 
16 // Original Author: ericv@google.com (Eric Veach)
17 // Converted to D:  madric@gmail.com (Vijay Nayar)
18 //
19 // See S2ClosestPointQuery (defined below) for an overview.
20 
21 module s2.s2closest_point_query;
22 
23 import s2.s1angle;
24 import s2.s1chord_angle;
25 import s2.s2cell;
26 import s2.s2closest_point_query_base;
27 import s2.s2min_distance_targets;
28 import s2.s2point;
29 import s2.s2point_index;
30 import s2.s2shape_index;
31 
32 /**
33  * Options that control the set of points returned.  Note that by default
34  * *all* points are returned, so you will always want to set either the
35  * max_points() option or the max_distance() option (or both).
36  *
37  * This class is also available as S2ClosestPointQuery<Data>::Options.
38  * (It is defined here to avoid depending on the "Data" template argument.)
39  */
40 class S2ClosestPointQueryOptions : S2ClosestPointQueryBaseOptions!S2MinDistance {
41 public:
42   alias Distance = S2MinDistance;
43   alias Base = S2ClosestPointQueryBaseOptions!Distance;
44 
45   // See S2ClosestPointQueryBaseOptions for the full set of options.
46 
47   /**
48    * Specifies that only points whose distance to the target is less than
49    * "max_distance" should be returned.
50    *
51    * Note that points whose distance is exactly equal to "max_distance" are
52    * not returned.  Normally this doesn't matter, because distances are not
53    * computed exactly in the first place, but if such points are needed then
54    * see set_inclusive_max_distance() below.
55    *
56    * DEFAULT: Distance::Infinity()
57    */
58   void setMaxDistance(S1ChordAngle max_distance) {
59     super.setMaxDistance(Distance(max_distance));
60   }
61 
62   // Avoid hiding the base class method setMaxDistance(S1ChordAngle).
63   alias setMaxDistance = Base.setMaxDistance;
64 
65   /**
66    * Like set_max_distance(), except that points whose distance is exactly
67    * equal to "max_distance" are also returned.  Equivalent to calling
68    * set_max_distance(max_distance.Successor()).
69    */
70   void setInclusiveMaxDistance(S1ChordAngle max_distance) {
71     setMaxDistance(max_distance.successor());
72   }
73 
74   /**
75    * Like set_inclusive_max_distance(), except that "max_distance" is also
76    * increased by the maximum error in the distance calculation.  This ensures
77    * that all points whose true distance is less than or equal to
78    * "max_distance" will be returned (along with some points whose true
79    * distance is slightly greater).
80    *
81    * Algorithms that need to do exact distance comparisons can use this
82    * option to find a set of candidate points that can then be filtered
83    * further (e.g., using s2pred::CompareDistance).
84    */
85   void setConservativeMaxDistance(S1ChordAngle max_distance) {
86     import s2.s2edge_distances : getUpdateMinDistanceMaxError;
87     setMaxDistance(
88         Distance(max_distance.plusError(getUpdateMinDistanceMaxError(max_distance)).successor()));
89   }
90 
91   /**
92    * Versions of set_max_distance that take an S1Angle argument.  (Note that
93    * these functions require a conversion, and that the S1ChordAngle versions
94    * are preferred.)
95    */
96   void setMaxDistance(S1Angle max_distance) {
97     super.setMaxDistance(Distance(S1ChordAngle(max_distance)));
98   }
99 
100   void setInclusiveMaxDistance(S1Angle max_distance) {
101     setInclusiveMaxDistance(S1ChordAngle(max_distance));
102   }
103 
104   void setConservativeMaxDistance(S1Angle max_distance) {
105     setConservativeMaxDistance(S1ChordAngle(max_distance));
106   }
107 
108   /// See S2ClosestPointQueryBaseOptions for documentation.
109   // using Base::set_max_error;              // S1Chordangle version
110   void setMaxError(S1Angle max_error) {
111     super.setMaxError(S1ChordAngle(max_error));
112   }
113 
114   /// Inherited options (see s2closest_point_query_base.h for details):
115   // using Base::set_max_points;
116   // using Base::set_region;
117   // using Base::set_use_brute_force;
118 }
119 
120 /**
121  * S2ClosestPointQueryTarget represents the geometry to which the distance is
122  * measured.  There are subtypes for measuring the distance to a point, an
123  * edge, an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry).
124  */
125 alias S2ClosestPointQueryTarget = S2MinDistanceTarget;
126 
127 /**
128  * Target subtype that computes the closest distance to a point.
129  *
130  * This class is also available as S2ClosestPointQuery<Data>::PointTarget.
131  * (It is defined here to avoid depending on the "Data" template argument.)
132  */
133 final class S2ClosestPointQueryPointTarget : S2MinDistancePointTarget {
134 public:
135   this(in S2Point point) {
136     super(point);
137   }
138 
139   override
140   int maxBruteForceIndexSize() const {
141     // Using BM_FindClosest (which finds the single closest point), the
142     // break-even points are approximately X, Y, and Z points for grid,
143     // fractal, and regular loop geometry respectively.
144     //
145     // TODO(ericv): Adjust using benchmarks.
146     return 150;
147   }
148 }
149 
150 /**
151  * Target subtype that computes the closest distance to an edge.
152  *
153  * This class is also available as S2ClosestPointQuery<Data>::EdgeTarget.
154  * (It is defined here to avoid depending on the "Data" template argument.)
155  */
156 final class S2ClosestPointQueryEdgeTarget : S2MinDistanceEdgeTarget {
157 public:
158   this(in S2Point a, in S2Point b) {
159     super(a, b);
160   }
161 
162   override
163   int maxBruteForceIndexSize() const {
164     // Using BM_FindClosestToEdge (which finds the single closest point), the
165     // break-even points are approximately X, Y, and Z points for grid,
166     // fractal, and regular loop geometry respectively.
167     //
168     // TODO(ericv): Adjust using benchmarks.
169     return 100;
170   }
171 }
172 
173 /**
174  * Target subtype that computes the closest distance to an S2Cell
175  * (including the interior of the cell).
176  *
177  * This class is also available as S2ClosestPointQuery<Data>::CellTarget.
178  * (It is defined here to avoid depending on the "Data" template argument.)
179  */
180 final class S2ClosestPointQueryCellTarget : S2MinDistanceCellTarget {
181  public:
182   this(in S2Cell cell) {
183     super(cell);
184   }
185 
186   override
187   int maxBruteForceIndexSize() const {
188     // Using BM_FindClosestToCell (which finds the single closest point), the
189     // break-even points are approximately X, Y, and Z points for grid,
190     // fractal, and regular loop geometry respectively.
191     //
192     // TODO(ericv): Adjust using benchmarks.
193     return 50;
194   }
195 }
196 
197 /**
198  * Target subtype that computes the closest distance to an S2ShapeIndex
199  * (an arbitrary collection of points, polylines, and/or polygons).
200  *
201  * By default, distances are measured to the boundary and interior of
202  * polygons in the S2ShapeIndex rather than to polygon boundaries only.
203  * If you wish to change this behavior, you may call
204  *
205  *   target.set_include_interiors(false);
206  *
207  * (see S2MinDistanceShapeIndexTarget for details).
208  *
209  * This class is also available as S2ClosestPointQuery<Data>::ShapeIndexTarget.
210  * (It is defined here to avoid depending on the "Data" template argument.)
211  */
212 final class S2ClosestPointQueryShapeIndexTarget : S2MinDistanceShapeIndexTarget {
213 public:
214   this(S2ShapeIndex index) {
215     super(index);
216   }
217 
218   override
219   int maxBruteForceIndexSize() const {
220     // For BM_FindClosestToSameSizeAbuttingIndex (which uses a nearby
221     // S2ShapeIndex target of similar complexity), the break-even points are
222     // approximately X, Y, and Z points for grid, fractal, and regular loop
223     // geometry respectively.
224     //
225     // TODO(ericv): Adjust using benchmarks.
226     return 30;
227   }
228 }
229 
230 /**
231  * Given a set of points stored in an S2PointIndex, S2ClosestPointQuery
232  * provides methods that find the closest point(s) to a given query point
233  * or query edge.  Example usage:
234  *
235  * void Test(const vector<S2Point>& index_points,
236  *           const vector<S2Point>& target_points) {
237  *   // The template argument allows auxiliary data to be attached to each
238  *   // point (in this case, the array index).
239  *   S2PointIndex<int> index;
240  *   for (const S2Point& point : index_points) {
241  *     index.Add(point, i);
242  *   }
243  *   S2ClosestPointQuery<int> query(&index);
244  *   query.mutable_options()->set_max_points(5);
245  *   for (const S2Point& target_point : target_points) {
246  *     S2ClosestPointQueryPointTarget target(target_point);
247  *     for (const auto& result : query.FindClosestPoints(&target)) {
248  *       // The Result class contains the following methods:
249  *       //   distance() is the distance to the target.
250  *       //   point() is the indexed point.
251  *       //   data() is the auxiliary data.
252  *       DoSomething(target_point, result);
253  *     }
254  *   }
255  * }
256  *
257  * You can find either the k closest points, or all points within a given
258  * radius, or both (i.e., the k closest points up to a given maximum radius).
259  * E.g. to find all the points within 5 kilometers, call
260  *
261  *   query.mutable_options()->set_max_distance(
262  *       S2Earth::ToAngle(util::units::Kilometers(5)));
263  *
264  * By default *all* points are returned, so you should always specify either
265  * max_points() or max_distance() or both.  There is also a FindClosestPoint()
266  * convenience method that returns only the closest point.
267  *
268  * You can restrict the results to an arbitrary S2Region, for example:
269  *
270  *   S2LatLngRect rect(...);
271  *   query.set_region(&rect);  // Does *not* take ownership.
272  *
273  * To find the closest points to a query edge rather than a point, use:
274  *
275  *   S2ClosestPointQueryEdgeTarget target(v0, v1);
276  *   query.FindClosestPoints(&target);
277  *
278  * The implementation is designed to be fast for both small and large
279  * point sets.
280  */
281 class S2ClosestPointQuery(Data) {
282 public:
283   // See S2ClosestPointQueryBase for full documentation.
284 
285   alias Index = S2PointIndex!Data;
286   alias PointData = Index.PointData;
287 
288   // S2MinDistance is a thin wrapper around S1ChordAngle that implements the
289   // Distance concept required by S2ClosestPointQueryBase.
290   alias Distance = S2MinDistance;
291   alias Base = S2ClosestPointQueryBase!(Distance, Data);
292 
293   /**
294    * Each "Result" object represents a closest point.  Here are its main
295    * methods (see S2ClosestPointQueryBase::Result for details):
296    *
297    *   // The distance from the target to this point.
298    *   S1ChordAngle distance() const;
299    *
300    *   // The point itself.
301    *   const S2Point& point() const;
302    *
303    *   // The client-specified data associated with this point.
304    *   const Data& data() const;
305    */
306   alias Result = Base.Result;
307 
308   alias Options = S2ClosestPointQueryOptions;
309 
310   // The available target types (see definitions above).
311   alias Target = S2ClosestPointQueryTarget;
312   alias PointTarget = S2ClosestPointQueryPointTarget;
313   alias EdgeTarget = S2ClosestPointQueryEdgeTarget;
314   alias CellTarget = S2ClosestPointQueryCellTarget;
315   alias ShapeIndexTarget = S2ClosestPointQueryShapeIndexTarget;
316 
317   /// Default constructor; requires initialize() to be called.
318   this() {
319     _base = new Base();
320   }
321 
322   /**
323    * Convenience constructor that calls Init().  Options may be specified here
324    * or changed at any time using the mutable_options() accessor method.
325    */
326   this(Index index, Options options = null) {
327     this();
328     if (options is null)
329       options = new Options();
330     initialize(index, options);
331   }
332 
333   /**
334    * Initializes the query.  Options may be specified here or changed at any
335    * time using the mutable_options() accessor method.
336    *
337    * REQUIRES: "index" must persist for the lifetime of this object.
338    * REQUIRES: ReInit() must be called if "index" is modified.
339    */
340   void initialize(Index index, Options options) {
341     if (options is null)
342       options = new Options();
343 
344     _options = options;
345     _base.initialize(index);
346   }
347 
348   /**
349    * Reinitializes the query.  This method must be called whenever the
350    * underlying index is modified.
351    */
352   void reInitialize() {
353     _base.reInitialize();
354   }
355 
356   /// Returns a reference to the underlying S2PointIndex.
357   const(Index) index() const {
358     return _base.index();
359   }
360 
361   /// Returns the query options.  Options can be modifed between queries.
362   const(Options) options() const {
363     return _options;
364   }
365 
366   Options mutableOptions() {
367     return _options;
368   }
369 
370   /**
371    * Returns the closest points to the given target that satisfy the given
372    * options.  This method may be called multiple times.
373    */
374   Result[] findClosestPoints(Target target) {
375     return _base.findClosestPoints(target, _options);
376   }
377 
378   /**
379    * This version can be more efficient when this method is called many times,
380    * since it does not require allocating a new vector on each call.
381    */
382   void findClosestPoints(Target target, ref Result[] results) {
383     _base.findClosestPoints(target, _options, results);
384   }
385 
386   //////////////////////// Convenience Methods ////////////////////////
387 
388   /**
389    * Returns the closest point to the target.  If no point satisfies the search
390    * criteria, then a Result object with distance() == Infinity() and
391    * is_empty() == true is returned.
392    */
393   Result findClosestPoint(Target target) {
394     static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here");
395     Options tmp_options = _options.dup;
396     tmp_options.setMaxPoints(1);
397     return _base.findClosestPoint(target, tmp_options);
398   }
399 
400   /**
401    * Returns the minimum distance to the target.  If the index or target is
402    * empty, returns S1ChordAngle::Infinity().
403    *
404    * Use IsDistanceLess() if you only want to compare the distance against a
405    * threshold value, since it is often much faster.
406    */
407   S1ChordAngle getDistance(Target target) {
408     return findClosestPoint(target).distance.s1ChordAngle;
409   }
410 
411   /**
412    * Returns true if the distance to "target" is less than "limit".
413    *
414    * This method is usually much faster than GetDistance(), since it is much
415    * less work to determine whether the minimum distance is above or below a
416    * threshold than it is to calculate the actual minimum distance.
417    */
418   bool isDistanceLess(Target target, S1ChordAngle limit) {
419     static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here");
420     Options tmp_options = _options.dup;
421     tmp_options.setMaxPoints(1);
422     tmp_options.setMaxDistance(limit);
423     tmp_options.setMaxError(S1ChordAngle.straight().toS1Angle());
424     return !_base.findClosestPoint(target, tmp_options).isEmpty();
425   }
426 
427   // TODO: Resume here.
428 
429   /**
430    * Like IsDistanceLess(), but also returns true if the distance to "target"
431    * is exactly equal to "limit".
432    */
433   bool isDistanceLessOrEqual(Target target, S1ChordAngle limit) {
434     static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here");
435     Options tmp_options = _options.dup;
436     tmp_options.setMaxPoints(1);
437     tmp_options.setInclusiveMaxDistance(limit);
438     tmp_options.setMaxError(S1ChordAngle.straight().toS1Angle());
439     return !_base.findClosestPoint(target, tmp_options).isEmpty();
440   }
441 
442   /**
443    * Like IsDistanceLessOrEqual(), except that "limit" is increased by the
444    * maximum error in the distance calculation.  This ensures that this
445    * function returns true whenever the true, exact distance is less than
446    * or equal to "limit".
447    *
448    * For example, suppose that we want to test whether two geometries might
449    * intersect each other after they are snapped together using S2Builder
450    * (using the IdentitySnapFunction with a given "snap_radius").  Since
451    * S2Builder uses exact distance predicates (s2predicates.h), we need to
452    * measure the distance between the two geometries conservatively.  If the
453    * distance is definitely greater than "snap_radius", then the geometries
454    * are guaranteed to not intersect after snapping.
455    */
456   bool isConservativeDistanceLessOrEqual(Target target, S1ChordAngle limit) {
457     static assert(__traits(classInstanceSize, Options) <= 48, "Consider not copying Options here");
458     Options tmp_options = _options;
459     tmp_options.setMaxPoints(1);
460     tmp_options.setConservativeMaxDistance(limit);
461     tmp_options.setMaxError(S1ChordAngle.straight().toS1Angle());
462     return !_base.findClosestPoint(target, tmp_options).isEmpty();
463   }
464 
465   ///////////////////////// Deprecated Methods //////////////////////////
466 
467 private:
468   Options _options;
469   Base _base;
470 
471   // Deprecated methods that return results using the result interface require
472   // keeping a copy of the result vector.
473   Result[] _results;
474 }