Coverage Report

Created: 2026-09-11 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2region_coverer.h
Line
Count
Source
1
// Copyright 2005 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
#ifndef S2_S2REGION_COVERER_H_
19
#define S2_S2REGION_COVERER_H_
20
21
#include <algorithm>
22
#include <cstddef>
23
#include <new>
24
#include <queue>
25
#include <utility>
26
#include <vector>
27
28
#include "absl/base/casts.h"
29
#include "absl/base/macros.h"
30
#include "s2/_fp_contract_off.h"  // IWYU pragma: keep
31
#include "s2/s2cell.h"
32
#include "s2/s2cell_id.h"
33
#include "s2/s2cell_union.h"
34
#include "s2/s2point.h"
35
#include "s2/s2region.h"
36
37
class S2Region;
38
39
// An S2RegionCoverer is a class that allows arbitrary regions to be
40
// approximated as unions of cells (S2CellUnion).  This is useful for
41
// implementing various sorts of search and precomputation operations.
42
//
43
// Typical usage:
44
//
45
// S2RegionCoverer::Options options;
46
// options.set_max_cells(5);
47
// S2RegionCoverer coverer(options);
48
// S2Cap cap(center, radius);
49
// S2CellUnion covering = coverer.GetCovering(cap);
50
//
51
// This yields a vector of at most 5 cells that is guaranteed to cover the
52
// given cap (a disc-shaped region on the sphere).
53
//
54
// The approximation algorithm is not optimal but does a pretty good job in
55
// practice.  The output does not always use the maximum number of cells
56
// allowed, both because this would not always yield a better approximation,
57
// and because max_cells() is a limit on how much work is done exploring the
58
// possible covering as well as a limit on the final output size.
59
//
60
// Because it is an approximation algorithm, one should not rely on the
61
// stability of the output.  In particular, the output of the covering algorithm
62
// may change across different versions of the library.
63
//
64
// One can also generate interior coverings, which are sets of cells which
65
// are entirely contained within a region.  Interior coverings can be
66
// empty, even for non-empty regions, if there are no cells that satisfy
67
// the provided constraints and are contained by the region.  Note that for
68
// performance reasons, it is wise to specify a max_level when computing
69
// interior coverings - otherwise for regions with small or zero area, the
70
// algorithm may spend a lot of time subdividing cells all the way to leaf
71
// level to try to find contained cells.
72
class S2RegionCoverer {
73
 public:
74
  class Options {
75
   public:
76
    // Sets the desired maximum number of cells in the approximation.  Note
77
    // the following:
78
    //
79
    //  - For any setting of max_cells(), up to 6 cells may be returned if
80
    //    that is the minimum number required (e.g. if the region intersects
81
    //    all six cube faces).  Even for very tiny regions, up to 3 cells may
82
    //    be returned if they happen to be located at the intersection of
83
    //    three cube faces.
84
    //
85
    //  - min_level() takes priority over max_cells(), i.e. cells below the
86
    //    given level will never be used even if this causes a large number of
87
    //    cells to be returned.
88
    //
89
    //  - If max_cells() is less than 4, the area of the covering may be
90
    //    arbitrarily large compared to the area of the original region even
91
    //    if the region is convex (e.g. an S2Cap or S2LatLngRect).
92
    //
93
    // Accuracy is measured by dividing the area of the covering by the area
94
    // of the original region.  The following table shows the median and worst
95
    // case values for this area ratio on a test case consisting of 100,000
96
    // spherical caps of random size (generated using s2region_coverer_test):
97
    //
98
    //   max_cells:        3      4     5     6     8    12    20   100   1000
99
    //   median ratio:  5.33   3.32  2.73  2.34  1.98  1.66  1.42  1.11   1.01
100
    //   worst case:  215518  14.41  9.72  5.26  3.91  2.75  1.92  1.20   1.02
101
    //
102
    // The default value of 8 gives a reasonable tradeoff between the number
103
    // of cells used and the accuracy of the approximation.
104
    //
105
    // DEFAULT: kDefaultMaxCells
106
    static constexpr int kDefaultMaxCells = 8;
107
0
    int max_cells() const { return max_cells_; }
108
    void set_max_cells(int max_cells);
109
110
    // Sets the minimum and maximum cell levels to be used.  The default is to
111
    // use all cell levels.
112
    //
113
    // To find the cell level corresponding to a given physical distance, use
114
    // the S2Cell metrics defined in s2metrics.h.  For example, to find the
115
    // cell level that corresponds to an average edge length of 10km, use:
116
    //
117
    //   int level =
118
    //       S2::kAvgEdge.GetClosestLevel(S2Earth::KmToRadians(length_km));
119
    //
120
    // Note that min_level() takes priority over max_cells(), i.e. cells below
121
    // the given level will never be used even if this causes a large number
122
    // of cells to be returned.  (This doesn't apply to interior coverings,
123
    // since interior coverings make no completeness guarantees -- the result
124
    // is simply a set of cells that covers as much of the interior as
125
    // possible while satisfying the given restrictions.)
126
    //
127
    // REQUIRES: min_level() <= max_level()
128
    // DEFAULT: 0
129
0
    int min_level() const { return min_level_; }
130
    void set_min_level(int min_level);
131
132
    // REQUIRES: min_level() <= max_level()
133
    // DEFAULT: S2CellId::kMaxLevel
134
0
    int max_level() const { return max_level_; }
135
    void set_max_level(int max_level);
136
137
    // Convenience function that sets both the maximum and minimum cell levels.
138
    void set_fixed_level(int level);
139
140
    // If specified, then only cells where (level - min_level) is a multiple
141
    // of "level_mod" will be used (default 1).  This effectively allows the
142
    // branching factor of the S2CellId hierarchy to be increased.  Currently
143
    // the only parameter values allowed are 1, 2, or 3, corresponding to
144
    // branching factors of 4, 16, and 64 respectively.
145
    //
146
    // DEFAULT: 1
147
0
    int level_mod() const { return level_mod_; }
148
    void set_level_mod(int level_mod);
149
150
    // Convenience function that returns the maximum level such that
151
    //
152
    //   (level <= max_level()) && (level - min_level()) % level_mod() == 0.
153
    //
154
    // This is the maximum level that will actually be used in coverings.
155
    int true_max_level() const;
156
157
   protected:
158
    int max_cells_ = kDefaultMaxCells;
159
    int min_level_ = 0;
160
    int max_level_ = S2CellId::kMaxLevel;
161
    int level_mod_ = 1;
162
  };
163
164
  // Constructs an S2RegionCoverer with the given options.
165
  explicit S2RegionCoverer(const Options& options);
166
167
  // S2RegionCoverer is movable but not copyable.
168
  S2RegionCoverer(const S2RegionCoverer&) = delete;
169
  S2RegionCoverer& operator=(const S2RegionCoverer&) = delete;
170
  S2RegionCoverer(S2RegionCoverer&&) noexcept;
171
  S2RegionCoverer& operator=(S2RegionCoverer&&) noexcept;
172
173
  // Default constructor.  Options can be set using mutable_options().
174
  S2RegionCoverer();
175
  ~S2RegionCoverer();
176
177
  // Returns the current options.  Options can be modified between calls.
178
0
  const Options& options() const { return options_; }
179
0
  Options* mutable_options() { return &options_; }
180
181
  // Returns an S2CellUnion that covers (GetCovering) or is contained within
182
  // (GetInteriorCovering) the given region and satisfies the current options.
183
  //
184
  // Note that if options().min_level() > 0 or options().level_mod() > 1, then
185
  // by definition the S2CellUnion may not be normalized, i.e. there may be
186
  // groups of four child cells that can be replaced by their parent cell.
187
  S2CellUnion GetCovering(const S2Region& region);
188
  S2CellUnion GetInteriorCovering(const S2Region& region);
189
190
  // Like the methods above, but works directly with a vector of S2CellIds.
191
  // This version can be more efficient when this method is called many times,
192
  // since it does not require allocating a new vector on each call.
193
  void GetCovering(const S2Region& region, std::vector<S2CellId>* covering);
194
  void GetInteriorCovering(const S2Region& region,
195
                           std::vector<S2CellId>* interior);
196
197
  // Like GetCovering(), except that this method is much faster and the
198
  // coverings are not as tight.  All of the usual parameters are respected
199
  // (max_cells, min_level, max_level, and level_mod), except that the
200
  // implementation makes no attempt to take advantage of large values of
201
  // max_cells().  (A small number of cells will always be returned.)
202
  //
203
  // This function is useful as a starting point for algorithms that
204
  // recursively subdivide cells.
205
  void GetFastCovering(const S2Region& region, std::vector<S2CellId>* covering);
206
207
  // Given a connected region and a starting point on the boundary or inside the
208
  // region, returns a set of cells at the given level that cover the region.
209
  // The output cells are returned in arbitrary order.
210
  //
211
  // Note that this method is *not* faster than the regular GetCovering()
212
  // method for most region types, such as S2Cap or S2Polygon, and in fact it
213
  // can be much slower when the output consists of a large number of cells.
214
  // Currently it can be faster at generating coverings of long narrow regions
215
  // such as polylines, but this may change in the future, in which case this
216
  // method will most likely be removed.
217
  static void GetSimpleCovering(const S2Region& region, const S2Point& start,
218
                                int level, std::vector<S2CellId>* output);
219
220
  // Like GetSimpleCovering(), but accepts a starting S2CellId rather than a
221
  // starting point and cell level.  Returns all edge-connected cells at the
222
  // same level as "start" that intersect "region", in arbitrary order.
223
  static void FloodFill(const S2Region& region, S2CellId start,
224
                        std::vector<S2CellId>* output);
225
226
  // Returns true if the given S2CellId vector represents a valid covering
227
  // that conforms to the current covering parameters.  In particular:
228
  //
229
  //  - All S2CellIds must be valid.
230
  //
231
  //  - S2CellIds must be sorted and non-overlapping.
232
  //
233
  //  - S2CellId levels must satisfy min_level(), max_level(), and level_mod().
234
  //
235
  //  - If covering.size() > max_cells(), there must be no two cells with
236
  //    a common ancestor at min_level() or higher.
237
  //
238
  //  - There must be no sequence of cells that could be replaced by an
239
  //    ancestor (i.e. with level_mod() == 1, the 4 child cells of a parent).
240
  bool IsCanonical(const S2CellUnion& covering) const;
241
  bool IsCanonical(const std::vector<S2CellId>& covering) const;
242
243
  // Modify "covering" if necessary so that it conforms to the current
244
  // covering parameters (max_cells, min_level, max_level, and level_mod).
245
  // There are no restrictions on the input S2CellIds (they may be unsorted,
246
  // overlapping, etc).
247
  S2CellUnion CanonicalizeCovering(const S2CellUnion& covering);
248
  void CanonicalizeCovering(std::vector<S2CellId>* covering);
249
250
 private:
251
  struct Candidate {
252
0
    void* operator new(std::size_t size, std::size_t max_children) {
253
0
      return ::operator new (size + max_children * sizeof(Candidate *));
254
0
    }
255
256
0
    void operator delete(void* p) {
257
0
      ::operator delete (p);
258
0
    }
259
260
    Candidate(const S2Cell& cell, const std::size_t max_children)
261
0
        : cell(cell), is_terminal(max_children == 0) {
262
0
      std::fill_n(&children[0], max_children,
263
0
                  absl::implicit_cast<Candidate*>(nullptr));
264
0
    }
265
266
    // Default destructor is fine; Candidate* is trivially destructible.
267
    // children must be deleted by DeleteCandidate.
268
0
    ~Candidate() = default;
269
270
    S2Cell cell;
271
    bool is_terminal;        // Cell should not be expanded further.
272
    int num_children = 0;    // Number of children that intersect the region.
273
    Candidate* children[0];  // Actual size may be 0, 4, 16, or 64 elements.
274
  };
275
276
  // If the cell intersects the given region, return a new candidate with no
277
  // children, otherwise return nullptr.  Also marks the candidate as "terminal"
278
  // if it should not be expanded further.
279
  Candidate* NewCandidate(const S2Cell& cell);
280
281
  // Returns the log base 2 of the maximum number of children of a candidate.
282
0
  int max_children_shift() const { return 2 * options().level_mod(); }
283
284
  // Frees the memory associated with a candidate.
285
  static void DeleteCandidate(Candidate* candidate, bool delete_children);
286
287
  // Processes a candidate by either adding it to the result_ vector or
288
  // expanding its children and inserting it into the priority queue.
289
  // Passing an argument of nullptr does nothing.
290
  void AddCandidate(Candidate* candidate);
291
292
  // Populates the children of "candidate" by expanding the given number of
293
  // levels from the given cell.  Returns the number of children that were
294
  // marked "terminal".
295
  int ExpandChildren(Candidate* candidate, const S2Cell& cell, int num_levels);
296
297
  // Computes a set of initial candidates that cover the given region.
298
  void GetInitialCandidates();
299
300
  // Generates a covering and stores it in result_.
301
  void GetCoveringInternal(const S2Region& region);
302
303
  // If level > min_level(), then reduces "level" if necessary so that it also
304
  // satisfies level_mod().  Levels smaller than min_level() are not affected
305
  // (since cells at these levels are eventually expanded).
306
  int AdjustLevel(int level) const;
307
308
  // Ensures that all cells with level > min_level() also satisfy level_mod(),
309
  // by replacing them with an ancestor if necessary.  Cell levels smaller
310
  // than min_level() are not modified (see AdjustLevel).  The output is
311
  // then normalized to ensure that no redundant cells are present.
312
  void AdjustCellLevels(std::vector<S2CellId>* cells) const;
313
314
  // Returns true if "covering" contains all children of "id" at level
315
  // (id.level() + options_.level_mod()).
316
  bool ContainsAllChildren(const std::vector<S2CellId>& covering,
317
                           S2CellId id) const;
318
319
  // Replaces all descendants of "id" in "covering" with "id".
320
  // REQUIRES: "covering" contains at least one descendant of "id".
321
  void ReplaceCellsWithAncestor(std::vector<S2CellId>* covering,
322
                                S2CellId id) const;
323
324
  Options options_;
325
326
  // We save a temporary copy of the pointer passed to GetCovering() in order
327
  // to avoid passing this parameter around internally.  It is only used (and
328
  // only valid) for the duration of a single GetCovering() call.
329
  const S2Region* region_ = nullptr;
330
331
  // The set of S2CellIds that have been added to the covering so far.
332
  std::vector<S2CellId> result_;
333
334
  // We keep the candidates in a priority queue.  We specify a vector to hold
335
  // the queue entries since for some reason priority_queue<> uses a deque by
336
  // default.  We define our own own comparison function on QueueEntries in
337
  // order to make the results deterministic.  (Using the default
338
  // less<QueueEntry>, entries of equal priority would be sorted according to
339
  // the memory address of the candidate.)
340
341
  typedef std::pair<int, Candidate*> QueueEntry;
342
  struct CompareQueueEntries {
343
0
    bool operator()(const QueueEntry& x, const QueueEntry& y) const {
344
0
      return x.first < y.first;
345
0
    }
346
  };
347
  typedef std::priority_queue<QueueEntry, std::vector<QueueEntry>,
348
                              CompareQueueEntries> CandidateQueue;
349
  CandidateQueue pq_;
350
351
  // True if we're computing an interior covering.
352
  bool interior_covering_;
353
354
  // Counter of number of candidates created, for performance evaluation.
355
  int candidates_created_counter_;
356
};
357
358
#endif  // S2_S2REGION_COVERER_H_