/src/s2geometry/src/s2/s2closest_cell_query_base.h
Line | Count | Source |
1 | | // Copyright 2018 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 | | // Author: ericv@google.com (Eric Veach) |
17 | | // |
18 | | // See S2ClosestCellQueryBase (defined below) for an overview. |
19 | | |
20 | | #ifndef S2_S2CLOSEST_CELL_QUERY_BASE_H_ |
21 | | #define S2_S2CLOSEST_CELL_QUERY_BASE_H_ |
22 | | |
23 | | #include <algorithm> |
24 | | #include <cstddef> |
25 | | #include <iterator> |
26 | | #include <limits> |
27 | | #include <queue> |
28 | | #include <type_traits> |
29 | | #include <vector> |
30 | | |
31 | | #include "absl/container/btree_set.h" |
32 | | #include "absl/container/flat_hash_set.h" |
33 | | #include "absl/container/inlined_vector.h" |
34 | | #include "absl/hash/hash.h" |
35 | | #include "absl/log/absl_check.h" |
36 | | #include "absl/log/absl_log.h" |
37 | | #include "s2/_fp_contract_off.h" // IWYU pragma: keep |
38 | | #include "s2/s1chord_angle.h" |
39 | | #include "s2/s2cap.h" |
40 | | #include "s2/s2cell.h" |
41 | | #include "s2/s2cell_id.h" |
42 | | #include "s2/s2cell_index.h" |
43 | | #include "s2/s2cell_union.h" |
44 | | #include "s2/s2distance_target.h" |
45 | | #include "s2/s2region.h" |
46 | | #include "s2/s2region_coverer.h" |
47 | | |
48 | | // S2ClosestCellQueryBase is a templatized class for finding the closest |
49 | | // (cell_id, label) pairs in an S2CellIndex to a given target. It is not |
50 | | // intended to be used directly, but rather to serve as the implementation of |
51 | | // various specialized classes with more convenient APIs (such as |
52 | | // S2ClosestCellQuery). It is flexible enough so that it can be adapted to |
53 | | // compute maximum distances and even potentially Hausdorff distances. |
54 | | // |
55 | | // By using the appropriate options, this class can answer questions such as: |
56 | | // |
57 | | // - Find the minimum distance between a cell collection A and a target B. |
58 | | // - Find all cells in collection A that are within a distance D of target B. |
59 | | // - Find the k cells of collection A that are closest to a given point P. |
60 | | // |
61 | | // The target is any class that implements the S2DistanceTarget interface. |
62 | | // There are predefined targets for points, edges, S2Cells, S2CellUnions, and |
63 | | // S2ShapeIndexes (arbitrary collctions of points, polylines, and polygons). |
64 | | // |
65 | | // The Distance template argument is used to represent distances. Usually it |
66 | | // is a thin wrapper around S1ChordAngle, but another distance type may be |
67 | | // used as long as it implements the Distance concept described in |
68 | | // s2distance_target.h. For example this can be used to measure maximum |
69 | | // distances, to get more accuracy, or to measure non-spheroidal distances. |
70 | | template <class Distance> |
71 | | class S2ClosestCellQueryBase { |
72 | | public: |
73 | | using Delta = typename Distance::Delta; |
74 | | using Label = S2CellIndex::Label; |
75 | | using LabelledCell = S2CellIndex::LabelledCell; |
76 | | |
77 | | // Options that control the set of cells returned. Note that by default |
78 | | // *all* cells are returned, so you will always want to set either the |
79 | | // max_results() option or the max_distance() option (or both). |
80 | | // |
81 | | // This class is also available as S2ClosestCellQueryBase<Data>::Options. |
82 | | // |
83 | | // The Distance template argument is described below. |
84 | | class Options { |
85 | | public: |
86 | | Options(); |
87 | | |
88 | | // Specifies that at most "max_results" cells should be returned. |
89 | | // |
90 | | // REQUIRES: max_results >= 1 |
91 | | // DEFAULT: kMaxMaxResults |
92 | | int max_results() const; |
93 | | void set_max_results(int max_results); |
94 | | static constexpr int kMaxMaxResults = std::numeric_limits<int>::max(); |
95 | | |
96 | | // Specifies that only cells whose distance to the target is less than |
97 | | // "max_distance" should be returned. |
98 | | // |
99 | | // Note that cells whose distance is exactly equal to "max_distance" are |
100 | | // not returned. In most cases this doesn't matter (since distances are |
101 | | // not computed exactly in the first place), but if such cells are needed |
102 | | // then you can retrieve them by specifying "max_distance" as the next |
103 | | // largest representable Distance. For example, if Distance is an |
104 | | // S1ChordAngle then you can specify max_distance.Successor(). |
105 | | // |
106 | | // DEFAULT: Distance::Infinity() |
107 | | Distance max_distance() const; |
108 | | void set_max_distance(Distance max_distance); |
109 | | |
110 | | // Specifies that cells up to max_error() further away than the true |
111 | | // closest cells may be substituted in the result set, as long as such |
112 | | // cells satisfy all the remaining search criteria (such as max_distance). |
113 | | // This option only has an effect if max_results() is also specified; |
114 | | // otherwise all cells closer than max_distance() will always be returned. |
115 | | // |
116 | | // Note that this does not affect how the distance between cells is |
117 | | // computed; it simply gives the algorithm permission to stop the search |
118 | | // early as soon as the best possible improvement drops below max_error(). |
119 | | // |
120 | | // This can be used to implement distance predicates efficiently. For |
121 | | // example, to determine whether the minimum distance is less than D, the |
122 | | // IsDistanceLess() method sets max_results() == 1 and max_distance() == |
123 | | // max_error() == D. This causes the algorithm to terminate as soon as it |
124 | | // finds any cell whose distance is less than D, rather than continuing to |
125 | | // search for a cell that is even closer. |
126 | | // |
127 | | // DEFAULT: Distance::Delta::Zero() |
128 | | Delta max_error() const; |
129 | | void set_max_error(Delta max_error); |
130 | | |
131 | | // Specifies that cells must intersect the given S2Region. "region" is |
132 | | // owned by the caller and must persist during the lifetime of this |
133 | | // object. The value may be changed between calls to FindClosestPoints(), |
134 | | // or reset by calling set_region(nullptr). |
135 | | // |
136 | | // Note that if you want to set the region to a disc around a target |
137 | | // point, it is faster to use a PointTarget with set_max_distance() |
138 | | // instead. You can also call both methods, e.g. to set a maximum |
139 | | // distance and also require that cells lie within a given rectangle. |
140 | | const S2Region* region() const; |
141 | | void set_region(const S2Region* region); |
142 | | |
143 | | // Specifies that distances should be computed by examining every cell |
144 | | // rather than using the S2ShapeIndex. This is useful for testing, |
145 | | // benchmarking, and debugging. |
146 | | // |
147 | | // DEFAULT: false |
148 | | bool use_brute_force() const; |
149 | | void set_use_brute_force(bool use_brute_force); |
150 | | |
151 | | private: |
152 | | Distance max_distance_ = Distance::Infinity(); |
153 | | Delta max_error_ = Delta::Zero(); |
154 | | const S2Region* region_ = nullptr; |
155 | | int max_results_ = kMaxMaxResults; |
156 | | bool use_brute_force_ = false; |
157 | | }; |
158 | | |
159 | | // The Target class represents the geometry to which the distance is |
160 | | // measured. For example, there can be subtypes for measuring the distance |
161 | | // to a point, an edge, or to an S2ShapeIndex (an arbitrary collection of |
162 | | // geometry). |
163 | | // |
164 | | // Implementations do *not* need to be thread-safe. They may cache data or |
165 | | // allocate temporary data structures in order to improve performance. |
166 | | using Target = S2DistanceTarget<Distance>; |
167 | | |
168 | | // Each "Result" object represents a closest (cell_id, label) pair. |
169 | | class Result { |
170 | | public: |
171 | | // The default constructor yields an empty result, with a distance() of |
172 | | // Infinity() and invalid cell_id() and label() values. |
173 | 0 | Result() : distance_(Distance::Infinity()), cell_id_(S2CellId::None()), |
174 | 0 | label_(-1) {} |
175 | | |
176 | | // Constructs a Result object for the given (cell_id, label) pair. |
177 | | Result(Distance distance, S2CellId cell_id, Label label) |
178 | 0 | : distance_(distance), cell_id_(cell_id), label_(label) {} |
179 | | |
180 | | // The distance from the target to this cell. |
181 | 0 | Distance distance() const { return distance_; } |
182 | | |
183 | | // The cell itself. |
184 | | S2CellId cell_id() const { return cell_id_; } |
185 | | |
186 | | // The label associated with this S2CellId. |
187 | | Label label() const { return label_; } |
188 | | |
189 | | // Returns true if this Result object does not refer to any cell. |
190 | | // (The only case where an empty Result is returned is when the |
191 | | // FindClosestCell() method does not find any cells that meet the |
192 | | // specified criteria.) |
193 | 0 | bool is_empty() const { return cell_id_ == S2CellId::None(); } |
194 | | |
195 | | // Returns true if two Result objects are identical. |
196 | 0 | friend bool operator==(const Result& x, const Result& y) { |
197 | 0 | return (x.distance_ == y.distance_ && |
198 | 0 | x.cell_id_ == y.cell_id_ && |
199 | 0 | x.label_ == y.label_); |
200 | 0 | } |
201 | | |
202 | | // Compares two Result objects first by distance, then by cell_id and |
203 | | // finally by label. |
204 | 0 | friend bool operator<(const Result& x, const Result& y) { |
205 | 0 | if (x.distance_ < y.distance_) return true; |
206 | 0 | if (y.distance_ < x.distance_) return false; |
207 | 0 | if (x.cell_id_ < y.cell_id_) return true; |
208 | 0 | if (y.cell_id_ < x.cell_id_) return false; |
209 | 0 | return x.label_ < y.label_; |
210 | 0 | } |
211 | | |
212 | | // Indicates that linear rather than binary search should be used when this |
213 | | // type is used as the key in gtl::btree data structures. |
214 | | using absl_btree_prefer_linear_node_search = std::true_type; |
215 | | |
216 | | private: |
217 | | Distance distance_; |
218 | | S2CellId cell_id_; |
219 | | Label label_; |
220 | | }; |
221 | | |
222 | | // The minimum number of ranges that a cell must contain to enqueue it |
223 | | // rather than processing its contents immediately. |
224 | | static constexpr int kMinRangesToEnqueue = 6; |
225 | | |
226 | | // Default constructor; requires Init() to be called. |
227 | | S2ClosestCellQueryBase(); |
228 | | ~S2ClosestCellQueryBase(); |
229 | | |
230 | | // Convenience constructor that calls Init(). |
231 | | explicit S2ClosestCellQueryBase(const S2CellIndex* index); |
232 | | |
233 | | // S2ClosestCellQueryBase is not copyable. |
234 | | S2ClosestCellQueryBase(const S2ClosestCellQueryBase&) = delete; |
235 | | void operator=(const S2ClosestCellQueryBase&) = delete; |
236 | | |
237 | | // Initializes the query. |
238 | | // REQUIRES: ReInit() must be called if "index" is modified. |
239 | | void Init(const S2CellIndex* index); |
240 | | |
241 | | // Reinitializes the query. This method must be called whenever the |
242 | | // underlying index is modified. |
243 | | void ReInit(); |
244 | | |
245 | | // Return a reference to the underlying S2CellIndex. |
246 | | const S2CellIndex& index() const; |
247 | | |
248 | | // Returns the closest (cell_id, label) pairs to the given target that |
249 | | // satisfy the given options. This method may be called multiple times. |
250 | | std::vector<Result> FindClosestCells(Target* target, const Options& options); |
251 | | |
252 | | // This version can be more efficient when this method is called many times, |
253 | | // since it does not require allocating a new vector on each call. |
254 | | void FindClosestCells(Target* target, const Options& options, |
255 | | std::vector<Result>* results); |
256 | | |
257 | | // Convenience method that returns exactly one (cell_id, label) pair. If no |
258 | | // cells satisfy the given search criteria, then a Result with |
259 | | // distance() == Infinity() and is_empty() == true is returned. |
260 | | // |
261 | | // REQUIRES: options.max_results() == 1 |
262 | | Result FindClosestCell(Target* target, const Options& options); |
263 | | |
264 | | private: |
265 | | using CellIterator = S2CellIndex::CellIterator; |
266 | | using ContentsIterator = S2CellIndex::ContentsIterator; |
267 | | using NonEmptyRangeIterator = S2CellIndex::NonEmptyRangeIterator; |
268 | | using RangeIterator = S2CellIndex::RangeIterator; |
269 | | |
270 | 0 | const Options& options() const { return *options_; } |
271 | | void FindClosestCellsInternal(Target* target, const Options& options); |
272 | | void FindClosestCellsBruteForce(); |
273 | | void FindClosestCellsOptimized(); |
274 | | void InitQueue(); |
275 | | void InitCovering(); |
276 | | void AddInitialRange(S2CellId first_id, S2CellId last_id); |
277 | | void MaybeAddResult(S2CellId cell_id, Label label); |
278 | | bool ProcessOrEnqueue(S2CellId id, NonEmptyRangeIterator* iter, bool seek); |
279 | | void AddRange(const RangeIterator& range); |
280 | | |
281 | | const S2CellIndex* index_; |
282 | | const Options* options_; |
283 | | Target* target_; |
284 | | |
285 | | // True if max_error() must be subtracted from priority queue cell distances |
286 | | // in order to ensure that such distances are measured conservatively. This |
287 | | // is true only if the target takes advantage of max_error() in order to |
288 | | // return faster results, and 0 < max_error() < distance_limit_. |
289 | | bool use_conservative_cell_distance_; |
290 | | |
291 | | // For the optimized algorithm we precompute the top-level S2CellIds that |
292 | | // will be added to the priority queue. There can be at most 6 of these |
293 | | // cells. Essentially this is just a covering of the indexed cells. |
294 | | std::vector<S2CellId> index_covering_; |
295 | | |
296 | | // The distance beyond which we can safely ignore further candidate cells. |
297 | | // (Candidates that are exactly at the limit are ignored; this is more |
298 | | // efficient for UpdateMinDistance() and should not affect clients since |
299 | | // distance measurements have a small amount of error anyway.) |
300 | | // |
301 | | // Initially this is the same as the maximum distance specified by the user, |
302 | | // but it can also be updated by the algorithm (see MaybeAddResult). |
303 | | Distance distance_limit_; |
304 | | |
305 | | // The current result set is stored in one of three ways: |
306 | | // |
307 | | // - If max_results() == 1, the best result is kept in result_singleton_. |
308 | | // |
309 | | // - If max_results() == kMaxMaxResults, results are appended to |
310 | | // result_vector_ and sorted/uniqued at the end. |
311 | | // |
312 | | // - Otherwise results are kept in a btree_set so that we can progressively |
313 | | // reduce the distance limit once max_results() results have been found. |
314 | | // (A priority queue is not sufficient because we need to be able to |
315 | | // check whether a candidate cell is already in the result set.) |
316 | | // |
317 | | // TODO(ericv): Check whether it would be faster to use avoid_duplicates_ |
318 | | // when result_set_ is used so that we could use a priority queue instead. |
319 | | Result result_singleton_; |
320 | | std::vector<Result> result_vector_; |
321 | | absl::btree_set<Result> result_set_; |
322 | | |
323 | | // When the results are stored in a btree_set (see above), usually |
324 | | // duplicates can be removed simply by inserting candidate cells in the |
325 | | // current result set. However this is not true if Options::max_error() > 0 |
326 | | // and the Target subtype takes advantage of this by returning suboptimal |
327 | | // distances. This is because when UpdateMinDistance() is called with |
328 | | // different "min_dist" parameters (i.e., the distance to beat), the |
329 | | // implementation may return a different distance for the same cell. Since |
330 | | // the btree_set is keyed by (distance, cell_id, label) this can create |
331 | | // duplicate results. |
332 | | // |
333 | | // The flag below is true when duplicates must be avoided explicitly. This |
334 | | // is achieved by maintaining a separate set keyed by (cell_id, label) only, |
335 | | // and checking whether each edge is in that set before computing the |
336 | | // distance to it. |
337 | | // |
338 | | // TODO(ericv): Check whether it is faster to avoid duplicates by default |
339 | | // (even when Options::max_results() == 1), rather than just when we need to. |
340 | | bool avoid_duplicates_; |
341 | | absl::flat_hash_set<LabelledCell> tested_cells_; |
342 | | |
343 | | // The algorithm maintains a priority queue of unprocessed S2CellIds, sorted |
344 | | // in increasing order of distance from the target. |
345 | | struct QueueEntry { |
346 | | // A lower bound on the distance from the target to "id". This is the key |
347 | | // of the priority queue. |
348 | | Distance distance; |
349 | | |
350 | | // The cell being queued. |
351 | | S2CellId id; |
352 | | |
353 | | QueueEntry(Distance _distance, S2CellId _id) |
354 | 0 | : distance(_distance), id(_id) {} |
355 | | |
356 | 0 | bool operator<(const QueueEntry& other) const { |
357 | | // The priority queue returns the largest elements first, so we want the |
358 | | // "largest" entry to have the smallest distance. |
359 | 0 | return other.distance < distance; |
360 | 0 | } |
361 | | }; |
362 | | using CellQueue = |
363 | | std::priority_queue<QueueEntry, absl::InlinedVector<QueueEntry, 16>>; |
364 | | CellQueue queue_; |
365 | | |
366 | | // Used to iterate over the contents of an S2CellIndex range. It is defined |
367 | | // here to take advantage of the fact that when multiple ranges are visited |
368 | | // in increasing order, duplicates can automatically be eliminated. |
369 | | S2CellIndex::ContentsIterator contents_it_; |
370 | | |
371 | | // Temporaries, defined here to avoid multiple allocations / initializations. |
372 | | |
373 | | std::vector<S2CellId> max_distance_covering_; |
374 | | std::vector<S2CellId> intersection_with_max_distance_; |
375 | | }; |
376 | | |
377 | | |
378 | | ////////////////// Implementation details follow //////////////////// |
379 | | |
380 | | template <class Distance> |
381 | 0 | inline S2ClosestCellQueryBase<Distance>::Options::Options() = default; |
382 | | |
383 | | template <class Distance> |
384 | 0 | inline int S2ClosestCellQueryBase<Distance>::Options::max_results() const { |
385 | 0 | return max_results_; |
386 | 0 | } |
387 | | |
388 | | template <class Distance> |
389 | | inline void S2ClosestCellQueryBase<Distance>::Options::set_max_results( |
390 | 0 | int max_results) { |
391 | 0 | ABSL_DCHECK_GE(max_results, 1); |
392 | 0 | max_results_ = max_results; |
393 | 0 | } |
394 | | |
395 | | template <class Distance> |
396 | | inline Distance S2ClosestCellQueryBase<Distance>::Options::max_distance() |
397 | 0 | const { |
398 | 0 | return max_distance_; |
399 | 0 | } |
400 | | |
401 | | template <class Distance> |
402 | | inline void S2ClosestCellQueryBase<Distance>::Options::set_max_distance( |
403 | 0 | Distance max_distance) { |
404 | 0 | max_distance_ = max_distance; |
405 | 0 | } |
406 | | |
407 | | template <class Distance> |
408 | | inline typename Distance::Delta |
409 | 0 | S2ClosestCellQueryBase<Distance>::Options::max_error() const { |
410 | 0 | return max_error_; |
411 | 0 | } |
412 | | |
413 | | template <class Distance> |
414 | | inline void S2ClosestCellQueryBase<Distance>::Options::set_max_error( |
415 | 0 | Delta max_error) { |
416 | 0 | max_error_ = max_error; |
417 | 0 | } |
418 | | |
419 | | template <class Distance> |
420 | | inline const S2Region* S2ClosestCellQueryBase<Distance>::Options::region() |
421 | 0 | const { |
422 | 0 | return region_; |
423 | 0 | } |
424 | | |
425 | | template <class Distance> |
426 | | inline void S2ClosestCellQueryBase<Distance>::Options::set_region( |
427 | | const S2Region* region) { |
428 | | region_ = region; |
429 | | } |
430 | | |
431 | | template <class Distance> |
432 | 0 | inline bool S2ClosestCellQueryBase<Distance>::Options::use_brute_force() const { |
433 | 0 | return use_brute_force_; |
434 | 0 | } |
435 | | |
436 | | template <class Distance> |
437 | | inline void S2ClosestCellQueryBase<Distance>::Options::set_use_brute_force( |
438 | 0 | bool use_brute_force) { |
439 | 0 | use_brute_force_ = use_brute_force; |
440 | 0 | } |
441 | | |
442 | | template <class Distance> |
443 | | S2ClosestCellQueryBase<Distance>::S2ClosestCellQueryBase() |
444 | 0 | : tested_cells_(/*bucket_count=*/1) {} |
445 | | |
446 | | template <class Distance> |
447 | 0 | S2ClosestCellQueryBase<Distance>::~S2ClosestCellQueryBase() { |
448 | | // Prevent inline destructor bloat by providing a definition. |
449 | 0 | } |
450 | | |
451 | | template <class Distance> |
452 | | inline S2ClosestCellQueryBase<Distance>::S2ClosestCellQueryBase( |
453 | | const S2CellIndex* index) : S2ClosestCellQueryBase() { |
454 | | Init(index); |
455 | | } |
456 | | |
457 | | template <class Distance> |
458 | | void S2ClosestCellQueryBase<Distance>::Init( |
459 | 0 | const S2CellIndex* index) { |
460 | 0 | index_ = index; |
461 | 0 | contents_it_.Init(index); |
462 | 0 | ReInit(); |
463 | 0 | } |
464 | | |
465 | | template <class Distance> |
466 | 0 | void S2ClosestCellQueryBase<Distance>::ReInit() { |
467 | 0 | index_covering_.clear(); |
468 | 0 | } |
469 | | |
470 | | template <class Distance> |
471 | | inline const S2CellIndex& |
472 | 0 | S2ClosestCellQueryBase<Distance>::index() const { |
473 | 0 | return *index_; |
474 | 0 | } |
475 | | |
476 | | template <class Distance> |
477 | | inline std::vector<typename S2ClosestCellQueryBase<Distance>::Result> |
478 | | S2ClosestCellQueryBase<Distance>::FindClosestCells( |
479 | 0 | Target* target, const Options& options) { |
480 | 0 | std::vector<Result> results; |
481 | 0 | FindClosestCells(target, options, &results); |
482 | 0 | return results; |
483 | 0 | } |
484 | | |
485 | | template <class Distance> |
486 | | typename S2ClosestCellQueryBase<Distance>::Result |
487 | | S2ClosestCellQueryBase<Distance>::FindClosestCell( |
488 | 0 | Target* target, const Options& options) { |
489 | 0 | ABSL_DCHECK_EQ(options.max_results(), 1); |
490 | 0 | FindClosestCellsInternal(target, options); |
491 | 0 | return result_singleton_; |
492 | 0 | } |
493 | | |
494 | | template <class Distance> |
495 | | void S2ClosestCellQueryBase<Distance>::FindClosestCells( |
496 | 0 | Target* target, const Options& options, std::vector<Result>* results) { |
497 | 0 | FindClosestCellsInternal(target, options); |
498 | 0 | results->clear(); |
499 | 0 | if (options.max_results() == 1) { |
500 | 0 | if (!result_singleton_.is_empty()) { |
501 | 0 | results->push_back(result_singleton_); |
502 | 0 | } |
503 | 0 | } else if (options.max_results() == Options::kMaxMaxResults) { |
504 | 0 | std::sort(result_vector_.begin(), result_vector_.end()); |
505 | 0 | std::unique_copy(result_vector_.begin(), result_vector_.end(), |
506 | 0 | std::back_inserter(*results)); |
507 | 0 | result_vector_.clear(); |
508 | 0 | } else { |
509 | 0 | results->assign(result_set_.begin(), result_set_.end()); |
510 | 0 | result_set_.clear(); |
511 | 0 | } |
512 | 0 | } |
513 | | |
514 | | template <class Distance> |
515 | | void S2ClosestCellQueryBase<Distance>::FindClosestCellsInternal( |
516 | 0 | Target* target, const Options& options) { |
517 | 0 | target_ = target; |
518 | 0 | options_ = &options; |
519 | |
|
520 | 0 | tested_cells_.clear(); |
521 | 0 | contents_it_.Clear(); |
522 | 0 | distance_limit_ = options.max_distance(); |
523 | 0 | result_singleton_ = Result(); |
524 | 0 | ABSL_DCHECK(result_vector_.empty()); |
525 | 0 | ABSL_DCHECK(result_set_.empty()); |
526 | 0 | ABSL_DCHECK_GE(target->max_brute_force_index_size(), 0); |
527 | 0 | if (distance_limit_ == Distance::Zero()) return; |
528 | | |
529 | 0 | if (options.max_results() == Options::kMaxMaxResults && |
530 | 0 | options.max_distance() == Distance::Infinity() && |
531 | 0 | options.region() == nullptr) { |
532 | 0 | ABSL_LOG(WARNING) << "Returning all cells " |
533 | 0 | "(max_results/max_distance/region not set)"; |
534 | 0 | } |
535 | | |
536 | | // If max_error() > 0 and the target takes advantage of this, then we may |
537 | | // need to adjust the distance estimates to the priority queue cells to |
538 | | // ensure that they are always a lower bound on the true distance. For |
539 | | // example, suppose max_distance == 100, max_error == 30, and we compute the |
540 | | // distance to the target from some cell C0 as d(C0) == 80. Then because |
541 | | // the target takes advantage of max_error(), the true distance could be as |
542 | | // low as 50. In order not to miss edges contained by such cells, we need |
543 | | // to subtract max_error() from the distance estimates. This behavior is |
544 | | // controlled by the use_conservative_cell_distance_ flag. |
545 | | // |
546 | | // However there is one important case where this adjustment is not |
547 | | // necessary, namely when max_distance() < max_error(). This is because |
548 | | // max_error() only affects the algorithm once at least max_edges() edges |
549 | | // have been found that satisfy the given distance limit. At that point, |
550 | | // max_error() is subtracted from distance_limit_ in order to ensure that |
551 | | // any further matches are closer by at least that amount. But when |
552 | | // max_distance() < max_error(), this reduces the distance limit to 0, |
553 | | // i.e. all remaining candidate cells and edges can safely be discarded. |
554 | | // (Note that this is how IsDistanceLess() and friends are implemented.) |
555 | | // |
556 | | // Note that Distance::Delta only supports operator==. |
557 | 0 | bool target_uses_max_error = (!(options.max_error() == Delta::Zero()) && |
558 | 0 | target_->set_max_error(options.max_error())); |
559 | | |
560 | | // Note that we can't compare max_error() and distance_limit_ directly |
561 | | // because one is a Delta and one is a Distance. Instead we subtract them. |
562 | 0 | use_conservative_cell_distance_ = target_uses_max_error && |
563 | 0 | (distance_limit_ == Distance::Infinity() || |
564 | 0 | Distance::Zero() < distance_limit_ - options.max_error()); |
565 | | |
566 | | // Use the brute force algorithm if the index is small enough. |
567 | 0 | if (options.use_brute_force() || |
568 | 0 | index_->num_cells() <= target_->max_brute_force_index_size()) { |
569 | 0 | avoid_duplicates_ = false; |
570 | 0 | FindClosestCellsBruteForce(); |
571 | 0 | } else { |
572 | | // If the target takes advantage of max_error() then we need to avoid |
573 | | // duplicate edges explicitly. (Otherwise it happens automatically.) |
574 | 0 | avoid_duplicates_ = (target_uses_max_error && options.max_results() > 1); |
575 | 0 | FindClosestCellsOptimized(); |
576 | 0 | } |
577 | 0 | } |
578 | | |
579 | | template <class Distance> |
580 | 0 | void S2ClosestCellQueryBase<Distance>::FindClosestCellsBruteForce() { |
581 | 0 | for (CellIterator it(index_); !it.done(); it.Next()) { |
582 | 0 | MaybeAddResult(it.cell_id(), it.label()); |
583 | 0 | } |
584 | 0 | } |
585 | | |
586 | | template <class Distance> |
587 | 0 | void S2ClosestCellQueryBase<Distance>::FindClosestCellsOptimized() { |
588 | 0 | InitQueue(); |
589 | 0 | while (!queue_.empty()) { |
590 | | // We need to copy the top entry before removing it, and we need to remove |
591 | | // it before adding any new entries to the queue. |
592 | 0 | QueueEntry entry = queue_.top(); |
593 | 0 | queue_.pop(); |
594 | 0 | if (!(entry.distance < distance_limit_)) { |
595 | 0 | queue_ = CellQueue(); // Clear any remaining entries. |
596 | 0 | break; |
597 | 0 | } |
598 | 0 | S2CellId child = entry.id.child_begin(); |
599 | | // We already know that it has too many cells, so process its children. |
600 | | // Each child may either be processed directly or enqueued again. The |
601 | | // loop is optimized so that we don't seek unnecessarily. |
602 | 0 | bool seek = true; |
603 | 0 | NonEmptyRangeIterator range(index_); |
604 | 0 | for (int i = 0; i < 4; ++i, child = child.next()) { |
605 | 0 | seek = ProcessOrEnqueue(child, &range, seek); |
606 | 0 | } |
607 | 0 | } |
608 | 0 | } |
609 | | |
610 | | template <class Distance> |
611 | 0 | void S2ClosestCellQueryBase<Distance>::InitQueue() { |
612 | 0 | ABSL_DCHECK(queue_.empty()); |
613 | | |
614 | | // Optimization: rather than starting with the entire index, see if we can |
615 | | // limit the search region to a small disc. Then we can find a covering for |
616 | | // that disc and intersect it with the covering for the index. This can |
617 | | // save a lot of work when the search region is small. |
618 | 0 | S2Cap cap = target_->GetCapBound(); |
619 | 0 | if (cap.is_empty()) return; // Empty target. |
620 | 0 | if (options().max_results() == 1) { |
621 | | // If the user is searching for just the closest cell, we can compute an |
622 | | // upper bound on search radius by seeking to the center of the target's |
623 | | // bounding cap and looking at the contents of that leaf cell range. If |
624 | | // the range intersects any cells, then the distance is zero. Otherwise |
625 | | // we can still look at the two neighboring ranges, and use the minimum |
626 | | // distance to any cell in those ranges as an upper bound on the search |
627 | | // radius. These cells may wind up being processed twice, but in general |
628 | | // this is still faster. |
629 | | // |
630 | | // First check the range containing or immediately following "center". |
631 | 0 | NonEmptyRangeIterator range(index_); |
632 | 0 | S2CellId target(cap.center()); |
633 | 0 | range.Seek(target); |
634 | 0 | AddRange(range); |
635 | 0 | if (distance_limit_ == Distance::Zero()) return; |
636 | | |
637 | | // If the range immediately follows "center" (rather than containing it), |
638 | | // then check the previous non-empty range as well. |
639 | 0 | if (range.start_id() > target && range.Prev()) { |
640 | 0 | AddRange(range); |
641 | 0 | if (distance_limit_ == Distance::Zero()) return; |
642 | 0 | } |
643 | 0 | } |
644 | | |
645 | | // We start with a covering of the set of indexed cells, then intersect it |
646 | | // with the maximum search radius disc (if any). |
647 | | // |
648 | | // Note that unlike S2ClosestPointQuery, we can't also intersect with the |
649 | | // given region (if any). This is because the index cells in the result are |
650 | | // only required to intersect the region. This means that an index cell that |
651 | | // intersects the region's covering may be much closer to the target than the |
652 | | // covering itself, which means that we cannot use the region's covering to |
653 | | // restrict the search. |
654 | | // |
655 | | // TODO(ericv): If this feature becomes important, this could be fixed by |
656 | | // (1) computing a covering of the region, (2) looking up any index cells |
657 | | // that contain each covering cell by seeking to covering_cell.range_min(), |
658 | | // (3) replacing each covering cell by the largest such cell (if any), and |
659 | | // (4) normalizing the result. |
660 | 0 | if (index_covering_.empty()) InitCovering(); |
661 | 0 | const std::vector<S2CellId>* initial_cells = &index_covering_; |
662 | 0 | if (distance_limit_ < Distance::Infinity()) { |
663 | 0 | S2RegionCoverer coverer; |
664 | 0 | coverer.mutable_options()->set_max_cells(4); |
665 | 0 | S1ChordAngle radius = cap.radius() + distance_limit_.GetChordAngleBound(); |
666 | 0 | S2Cap search_cap(cap.center(), radius); |
667 | 0 | coverer.GetFastCovering(search_cap, &max_distance_covering_); |
668 | 0 | S2CellUnion::GetIntersection(*initial_cells, max_distance_covering_, |
669 | 0 | &intersection_with_max_distance_); |
670 | 0 | initial_cells = &intersection_with_max_distance_; |
671 | 0 | } |
672 | 0 | NonEmptyRangeIterator range(index_); |
673 | 0 | for (size_t i = 0; i < initial_cells->size(); ++i) { |
674 | 0 | S2CellId id = (*initial_cells)[i]; |
675 | 0 | bool seek = (i == 0) || id.range_min() >= range.limit_id(); |
676 | 0 | ProcessOrEnqueue(id, &range, seek); |
677 | 0 | if (range.done()) break; |
678 | 0 | } |
679 | 0 | } |
680 | | |
681 | | template <class Distance> |
682 | 0 | void S2ClosestCellQueryBase<Distance>::InitCovering() { |
683 | | // Compute the "index covering", which is a small number of S2CellIds that |
684 | | // cover the indexed cells. There are two cases: |
685 | | // |
686 | | // - If the index spans more than one face, then there is one covering cell |
687 | | // per spanned face, just big enough to cover the indexed cells on that face. |
688 | | // |
689 | | // - If the index spans only one face, then we find the smallest cell "C" |
690 | | // that covers the indexed cells on that face (just like the case above). |
691 | | // Then for each of the 4 children of "C", if the child contains any index |
692 | | // cells then we create a covering cell that is big enough to just fit |
693 | | // those indexed cells (i.e., shrinking the child as much as possible to fit |
694 | | // its contents). This essentially replicates what would happen if we |
695 | | // started with "C" as the covering cell, since "C" would immediately be |
696 | | // split, except that we take the time to prune the children further since |
697 | | // this will save work on every subsequent query. |
698 | 0 | index_covering_.reserve(6); |
699 | 0 | NonEmptyRangeIterator it(index_), last(index_); |
700 | 0 | it.Begin(); |
701 | 0 | last.Finish(); |
702 | 0 | if (!last.Prev()) return; // Empty index. |
703 | 0 | S2CellId index_last_id = last.limit_id().prev(); |
704 | 0 | if (it.start_id() != last.start_id()) { |
705 | | // The index contains at least two distinct S2CellIds (because otherwise |
706 | | // there would only be one non-empty range). Choose a level such that the |
707 | | // entire index can be spanned with at most 6 cells (if the index spans |
708 | | // multiple faces) or 4 cells (it the index spans a single face). |
709 | 0 | int level = it.start_id().GetCommonAncestorLevel(index_last_id) + 1; |
710 | | |
711 | | // Visit each potential covering cell except the last (handled below). |
712 | 0 | S2CellId start_id = it.start_id().parent(level); |
713 | 0 | S2CellId last_id = index_last_id.parent(level); |
714 | 0 | for (S2CellId id = start_id; id != last_id; id = id.next()) { |
715 | | // Skip any covering cells that don't contain an indexed range. |
716 | 0 | if (id.range_max() < it.start_id()) continue; |
717 | | |
718 | | // Find the indexed range contained by this covering cell and then |
719 | | // shrink the cell if necessary so that it just covers this range. |
720 | 0 | S2CellId cell_first_id = it.start_id(); |
721 | 0 | it.Seek(id.range_max().next()); |
722 | | // Find the last leaf cell covered by the previous non-empty range. |
723 | 0 | last = it; |
724 | 0 | last.Prev(); |
725 | 0 | AddInitialRange(cell_first_id, last.limit_id().prev()); |
726 | 0 | } |
727 | 0 | } |
728 | 0 | AddInitialRange(it.start_id(), index_last_id); |
729 | 0 | } |
730 | | |
731 | | // Adds a cell to index_covering_ that covers the given inclusive range. |
732 | | // |
733 | | // REQUIRES: "first" and "last" have a common ancestor. |
734 | | template <class Distance> |
735 | | void S2ClosestCellQueryBase<Distance>::AddInitialRange( |
736 | 0 | S2CellId first_id, S2CellId last_id) { |
737 | | // Add the lowest common ancestor of the given range. |
738 | 0 | int level = first_id.GetCommonAncestorLevel(last_id); |
739 | 0 | ABSL_DCHECK_GE(level, 0); |
740 | 0 | index_covering_.push_back(first_id.parent(level)); |
741 | 0 | } |
742 | | |
743 | | // TODO(ericv): Consider having this method return false when distance_limit_ |
744 | | // is reduced to zero, and terminating any calling loops early. |
745 | | template <class Distance> |
746 | | void S2ClosestCellQueryBase<Distance>::MaybeAddResult(S2CellId cell_id, |
747 | 0 | Label label) { |
748 | 0 | if (avoid_duplicates_ && |
749 | 0 | !tested_cells_.insert(LabelledCell{cell_id, label}).second) { |
750 | 0 | return; |
751 | 0 | } |
752 | | |
753 | | // TODO(ericv): It may be relatively common to add the same S2CellId |
754 | | // multiple times with different labels. This could be optimized by |
755 | | // remembering the last "cell_id" argument and its distance. However this |
756 | | // may not be beneficial when Options::max_results() == 1, for example. |
757 | 0 | S2Cell cell(cell_id); |
758 | 0 | Distance distance = distance_limit_; |
759 | 0 | if (!target_->UpdateMinDistance(cell, &distance)) return; |
760 | | |
761 | 0 | const S2Region* region = options().region(); |
762 | 0 | if (region && !region->MayIntersect(cell)) return; |
763 | | |
764 | 0 | Result result(distance, cell_id, label); |
765 | 0 | if (options().max_results() == 1) { |
766 | | // Optimization for the common case where only the closest cell is wanted. |
767 | 0 | result_singleton_ = result; |
768 | 0 | distance_limit_ = result.distance() - options().max_error(); |
769 | 0 | } else if (options().max_results() == Options::kMaxMaxResults) { |
770 | 0 | result_vector_.push_back(result); // Sort/unique at end. |
771 | 0 | } else { |
772 | | // Add this cell to result_set_. Note that even if we already have enough |
773 | | // edges, we can't erase an element before insertion because the "new" |
774 | | // edge might in fact be a duplicate. |
775 | 0 | result_set_.insert(result); |
776 | 0 | int size = result_set_.size(); |
777 | 0 | if (size >= options().max_results()) { |
778 | 0 | if (size > options().max_results()) { |
779 | 0 | result_set_.erase(--result_set_.end()); |
780 | 0 | } |
781 | 0 | distance_limit_ = (--result_set_.end())->distance() - |
782 | 0 | options().max_error(); |
783 | 0 | } |
784 | 0 | } |
785 | 0 | } |
786 | | |
787 | | // Either process the contents of the given cell immediately, or add it to the |
788 | | // queue to be subdivided. If "seek" is false, then "iter" must be positioned |
789 | | // at the first non-empty range (if any) with start_id() >= id.range_min(). |
790 | | // |
791 | | // Returns "true" if the cell was added to the queue, and "false" if it was |
792 | | // processed immediately, in which case "iter" is positioned at the first |
793 | | // non-empty range (if any) with start_id() > id.range_max(). |
794 | | template <class Distance> |
795 | | bool S2ClosestCellQueryBase<Distance>::ProcessOrEnqueue( |
796 | 0 | S2CellId id, NonEmptyRangeIterator* iter, bool seek) { |
797 | 0 | if (seek) iter->Seek(id.range_min()); |
798 | 0 | S2CellId last = id.range_max(); |
799 | 0 | if (iter->start_id() > last) { |
800 | 0 | return false; // No need to seek to next child. |
801 | 0 | } |
802 | | // If this cell intersects at least "kMinRangesToEnqueue" leaf cell ranges |
803 | | // (including ranges whose contents are empty), then enqueue it. We test |
804 | | // this by advancing (n - 1) ranges and checking whether that range also |
805 | | // intersects this cell. |
806 | 0 | RangeIterator max_it = *iter; |
807 | 0 | if (max_it.Advance(kMinRangesToEnqueue - 1) && max_it.start_id() <= last) { |
808 | | // This cell intersects at least kMinRangesToEnqueue ranges, so enqueue it. |
809 | 0 | S2Cell cell(id); |
810 | 0 | Distance distance = distance_limit_; |
811 | | // We check "region_" second because it may be relatively expensive. |
812 | 0 | if (target_->UpdateMinDistance(cell, &distance) && |
813 | 0 | (!options().region() || options().region()->MayIntersect(cell))) { |
814 | 0 | if (use_conservative_cell_distance_) { |
815 | | // Ensure that "distance" is a lower bound on distance to the cell. |
816 | 0 | distance = distance - options().max_error(); |
817 | 0 | } |
818 | 0 | queue_.push(QueueEntry(distance, id)); |
819 | 0 | } |
820 | 0 | return true; // Seek to next child. |
821 | 0 | } |
822 | | // There were few enough ranges that we might as well process them now. |
823 | 0 | for (; iter->start_id() <= last; iter->Next()) { |
824 | 0 | AddRange(*iter); |
825 | 0 | } |
826 | 0 | return false; // No need to seek to next child. |
827 | 0 | } |
828 | | |
829 | | template <class Distance> |
830 | 0 | void S2ClosestCellQueryBase<Distance>::AddRange(const RangeIterator& range) { |
831 | 0 | for (contents_it_.StartUnion(range); |
832 | 0 | !contents_it_.done(); contents_it_.Next()) { |
833 | 0 | MaybeAddResult(contents_it_.cell_id(), contents_it_.label()); |
834 | 0 | } |
835 | 0 | } |
836 | | |
837 | | #endif // S2_S2CLOSEST_CELL_QUERY_BASE_H_ |