Coverage Report

Created: 2026-10-10 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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_