[go: up one dir, main page]

File: s2cell_union.h

package info (click to toggle)
s2geometry 0.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 5,564 kB
  • sloc: cpp: 65,177; python: 763; makefile: 5
file content (411 lines) | stat: -rw-r--r-- 17,455 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
// Copyright 2005 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Author: ericv@google.com (Eric Veach)

#ifndef S2_S2CELL_UNION_H_
#define S2_S2CELL_UNION_H_

#include <ostream>
#include <string>
#include <utility>
#include <vector>

#include "absl/base/macros.h"
#include "absl/flags/flag.h"

#include "s2/base/commandlineflags.h"
#include "s2/base/integral_types.h"
#include "s2/base/logging.h"
#include "s2/_fp_contract_off.h"
#include "s2/s2cell_id.h"
#include "s2/s2region.h"

class Decoder;
class Encoder;
class S1Angle;
class S2Cap;
class S2Cell;
class S2LatLngRect;

S2_DECLARE_bool(s2debug);
S2_DECLARE_int32(s2cell_union_decode_max_num_cells);

// An S2CellUnion is a region consisting of cells of various sizes.  Typically
// a cell union is used to approximate some other shape.  There is a tradeoff
// between the accuracy of the approximation and how many cells are used.
// Unlike polygons, cells have a fixed hierarchical structure.  This makes
// them more suitable for optimizations based on preprocessing.
//
// An S2CellUnion is represented as a vector of sorted, non-overlapping
// S2CellIds.  By default the vector is also "normalized", meaning that groups
// of 4 child cells have been replaced by their parent cell whenever possible.
// S2CellUnions are not required to be normalized, but certain operations will
// return different results if they are not (e.g., Contains(S2CellUnion).)
//
// S2CellUnion is movable and copyable.
class S2CellUnion final : public S2Region {
 public:
  // Creates an empty cell union.
  S2CellUnion() {}

  // Constructs a cell union with the given S2CellIds, then calls Normalize()
  // to sort them, remove duplicates, and merge cells when possible.  (See
  // FromNormalized if your vector is already normalized.)
  //
  // The argument is passed by value, so if you are passing a named variable
  // and have no further use for it, consider using std::move().
  //
  // A cell union containing a single S2CellId may be constructed like this:
  //
  //     S2CellUnion example({cell_id});
  explicit S2CellUnion(std::vector<S2CellId> cell_ids);

  // Convenience constructor that accepts a vector of uint64.  Note that
  // unlike the constructor above, this one makes a copy of "cell_ids".
  explicit S2CellUnion(const std::vector<uint64>& cell_ids);

  // Constructs a cell union for the whole sphere.
  static S2CellUnion WholeSphere();

  // Constructs a cell union from S2CellIds that have already been normalized
  // (typically because they were extracted from another S2CellUnion).
  //
  // The argument is passed by value, so if you are passing a named variable
  // and have no further use for it, consider using std::move().
  //
  // REQUIRES: "cell_ids" satisfies the requirements of IsNormalized().
  static S2CellUnion FromNormalized(std::vector<S2CellId> cell_ids);

  // Constructs a cell union from a vector of sorted, non-overlapping
  // S2CellIds.  Unlike the other constructors, FromVerbatim does not require
  // that groups of 4 child cells have been replaced by their parent cell.  In
  // other words, "cell_ids" must satisfy the requirements of IsValid() but
  // not necessarily IsNormalized().
  //
  // Note that if the cell union is not normalized, certain operations may
  // return different results (e.g., Contains(S2CellUnion)).
  //
  // REQUIRES: "cell_ids" satisfies the requirements of IsValid().
  static S2CellUnion FromVerbatim(std::vector<S2CellId> cell_ids);

  // Constructs a cell union that corresponds to a continuous range of cell
  // ids.  The output is a normalized collection of cell ids that covers the
  // leaf cells between "min_id" and "max_id" inclusive.
  //
  // REQUIRES: min_id.is_leaf(), max_id.is_leaf(), min_id <= max_id.
  static S2CellUnion FromMinMax(S2CellId min_id, S2CellId max_id);

  // Like FromMinMax() except that the union covers the range of leaf cells
  // from "begin" (inclusive) to "end" (exclusive), as with Python ranges or
  // STL iterator ranges.  If (begin == end) the result is empty.
  //
  // REQUIRES: begin.is_leaf(), end.is_leaf(), begin <= end.
  static S2CellUnion FromBeginEnd(S2CellId begin, S2CellId end);

  // Init() methods corresponding to the constructors/factory methods above.
  // TODO(ericv): Consider deprecating these methods in favor of using the
  // constructors and move assignment operator.
  void Init(std::vector<S2CellId> cell_ids);
  void Init(const std::vector<uint64>& cell_ids);
  void InitFromMinMax(S2CellId min_id, S2CellId max_id);
  void InitFromBeginEnd(S2CellId begin, S2CellId end);

  // Clears the contents of the cell union and minimizes memory usage.
  void Clear();

  // Gives ownership of the vector data to the client without copying, and
  // clears the content of the cell union.  The original data in cell_ids
  // is lost if there was any.
  std::vector<S2CellId> Release();

  // Convenience methods for accessing the individual cell ids.
  int num_cells() const { return static_cast<int>(cell_ids_.size()); }
  S2CellId cell_id(int i) const { return cell_ids_[i]; }

  // Vector-like methods for accessing the individual cell ids.
  size_t size() const { return cell_ids_.size(); }
  bool empty() const { return cell_ids_.empty(); }
  S2CellId operator[](int i) const { return cell_ids_[i]; }

  // Standard begin/end methods, to allow range-based for loops:
  //
  //  for (S2CellId id : cell_union) { ... }
  std::vector<S2CellId>::const_iterator begin() const;
  std::vector<S2CellId>::const_iterator end() const;

  // Direct access to the underlying vector for STL algorithms.
  const std::vector<S2CellId>& cell_ids() const { return cell_ids_; }

  // Returns true if the cell union is valid, meaning that the S2CellIds are
  // valid, non-overlapping, and sorted in increasing order.
  bool IsValid() const;

  // Returns true if the cell union is normalized, meaning that it is
  // satisfies IsValid() and that no four cells have a common parent.
  // Certain operations such as Contains(S2CellUnion) will return a different
  // result if the cell union is not normalized.
  bool IsNormalized() const;

  // Normalizes the cell union by discarding cells that are contained by other
  // cells, replacing groups of 4 child cells by their parent cell whenever
  // possible, and sorting all the cell ids in increasing order.
  void Normalize();

  // Replaces "output" with an expanded version of the cell union where any
  // cells whose level is less than "min_level" or where (level - min_level)
  // is not a multiple of "level_mod" are replaced by their children, until
  // either both of these conditions are satisfied or the maximum level is
  // reached.
  //
  // This method allows a covering generated by S2RegionCoverer using
  // min_level() or level_mod() constraints to be stored as a normalized cell
  // union (which allows various geometric computations to be done) and then
  // converted back to the original list of cell ids that satisfies the
  // desired constraints.
  void Denormalize(int min_level, int level_mod,
                   std::vector<S2CellId>* output) const;

  // If there are more than "excess" elements of the cell_ids() vector that
  // are allocated but unused, reallocates the array to eliminate the excess
  // space.  This reduces memory usage when many cell unions need to be held
  // in memory at once.
  void Pack(int excess = 0);

  // Returns true if the cell union contains the given cell id.  Containment
  // is defined with respect to regions, e.g. a cell contains its 4 children.
  // This is a fast operation (logarithmic in the size of the cell union).
  //
  // CAVEAT: If you have constructed a non-normalized S2CellUnion using
  // FromVerbatim, note that groups of 4 child cells are *not* considered to
  // contain their parent cell.  To get this behavior you must use one of the
  // other constructors or call Normalize() explicitly.
  bool Contains(S2CellId id) const;

  // Returns true if the cell union intersects the given cell id.
  // This is a fast operation (logarithmic in the size of the cell union).
  bool Intersects(S2CellId id) const;

  // Returns true if this cell union contains the given other cell union.
  //
  // CAVEAT: If you have constructed a non-normalized S2CellUnion using
  // FromVerbatim, note that groups of 4 child cells are *not* considered to
  // contain their parent cell.  To get this behavior you must use one of the
  // other constructors or call Normalize() explicitly.
  bool Contains(const S2CellUnion& y) const;

  // Returns true if this cell union intersects the given other cell union.
  bool Intersects(const S2CellUnion& y) const;

  // Returns the union of the two given cell unions.
  S2CellUnion Union(const S2CellUnion& y) const;

  // Returns the intersection of the two given cell unions.
  S2CellUnion Intersection(const S2CellUnion& y) const;

  // Specialized version of GetIntersection() that returns the intersection of
  // a cell union with an S2CellId.  This can be useful for splitting a cell
  // union into pieces.
  S2CellUnion Intersection(S2CellId id) const;

  // Returns the difference of the two given cell unions.
  S2CellUnion Difference(const S2CellUnion& y) const;

  // Expands the cell union by adding a buffer of cells at "expand_level"
  // around the union boundary.
  //
  // For each cell "c" in the union, we add all neighboring cells at level
  // "expand_level" that are adjacent to "c".  Note that there can be many
  // such cells if "c" is large compared to "expand_level".  If "c" is smaller
  // than "expand_level", we first add the parent of "c" at "expand_level" and
  // then add all the neighbors of that cell.
  //
  // Note that the size of the output is exponential in "expand_level".  For
  // example, if expand_level == 20 and the input has a cell at level 10,
  // there will be on the order of 4000 adjacent cells in the output.  For
  // most applications the Expand(min_radius, max_level_diff) method below is
  // easier to use.
  void Expand(int expand_level);

  // Expands the cell union such that it contains all points whose distance to
  // the cell union is at most "min_radius", but do not use cells that are
  // more than "max_level_diff" levels higher than the largest cell in the
  // input.  The second parameter controls the tradeoff between accuracy and
  // output size when a large region is being expanded by a small amount
  // (e.g. expanding Canada by 1km).  For example, if max_level_diff == 4 the
  // region will always be expanded by approximately 1/16 the width of its
  // largest cell.  Note that in the worst case, the number of cells in the
  // output can be up to 4 * (1 + 2 ** max_level_diff) times larger than the
  // number of cells in the input.
  void Expand(S1Angle min_radius, int max_level_diff);

  // The number of leaf cells covered by the union.
  // This will be no more than 6*2^60 for the whole sphere.
  uint64 LeafCellsCovered() const;

  // Approximates this cell union's area in steradians by summing the average
  // area of each contained cell's average area, using the AverageArea method
  // from the S2Cell class.  This is equivalent to the number of leaves covered,
  // multiplied by the average area of a leaf.  Note that AverageArea does not
  // take into account distortion of cell, and thus may be off by up to a
  // factor of up to 1.7.
  //
  // NOTE: Since this is proportional to LeafCellsCovered(), it is
  // always better to use that function if all you care about is
  // the relative average area between objects.
  double AverageBasedArea() const;

  // Calculates this cell union's area in steradians by summing the approximate
  // area for each contained cell, using the ApproxArea method from the S2Cell
  // class.
  double ApproxArea() const;

  // Calculates this cell union's area in steradians by summing the exact area
  // for each contained cell, using the Exact method from the S2Cell class.
  double ExactArea() const;

  // Return true if two cell unions are identical.
  friend bool operator==(const S2CellUnion& x, const S2CellUnion& y);

  // Return true if two cell unions are different.
  friend bool operator!=(const S2CellUnion& x, const S2CellUnion& y);

  ////////////////////////////////////////////////////////////////////////
  // S2Region interface (see s2region.h for details):

  S2CellUnion* Clone() const override;
  S2Cap GetCapBound() const override;
  S2LatLngRect GetRectBound() const override;

  // This is a fast operation (logarithmic in the size of the cell union).
  bool Contains(const S2Cell& cell) const override;

  // This is a fast operation (logarithmic in the size of the cell union).
  bool MayIntersect(const S2Cell& cell) const override;

  // The point 'p' does not need to be normalized.
  // This is a fast operation (logarithmic in the size of the cell union).
  bool Contains(const S2Point& p) const override;

  // Appends a serialized representation of the S2CellUnion to "encoder".
  //
  // REQUIRES: "encoder" uses the default constructor, so that its buffer
  //           can be enlarged as necessary by calling Ensure(int).
  void Encode(Encoder* const encoder) const;

  // Decodes an S2CellUnion encoded with Encode().  Returns true on success.
  bool Decode(Decoder* const decoder);

  ////////////////////////////////////////////////////////////////////////
  // Static methods intended for high-performance clients that prefer to
  // manage their own storage.

  // Like Normalize(), but works with a vector of S2CellIds.
  // Equivalent to:
  //   *cell_ids = S2CellUnion(std::move(*cell_ids)).Release();
  static void Normalize(std::vector<S2CellId>* cell_ids);

  // Like Denormalize(), but works with a vector of S2CellIds.
  // REQUIRES: out != &in
  static void Denormalize(const std::vector<S2CellId>& in,
                          int min_level, int level_mod,
                          std::vector<S2CellId>* out);

  // Like GetIntersection(), but works directly with vectors of S2CellIds,
  // Equivalent to:
  //
  //    *out = S2CellUnion(x).Intersection(S2CellUnion(y)).Release()
  //
  // except that this method has slightly more relaxed normalization
  // requirements: the input vectors may contain groups of 4 child cells that
  // all have the same parent.  (In a normalized S2CellUnion, such groups are
  // always replaced by the parent cell.)
  static void GetIntersection(const std::vector<S2CellId>& x,
                              const std::vector<S2CellId>& y,
                              std::vector<S2CellId>* out);

  // Returns a human-readable string describing the S2CellUnion, consisting of
  // the number of cells and the list of S2CellIds in S2CellId::ToToken()
  // format (limited to at most 500 cells).
  std::string ToString() const;

 private:
  friend class S2CellUnionTestPeer;  // For creating invalid S2CellUnions.

  // Internal constructor that does not check "cell_ids" for validity.
  enum VerbatimFlag { VERBATIM };
  S2CellUnion(std::vector<S2CellId> cell_ids, VerbatimFlag verbatim)
      : cell_ids_(std::move(cell_ids)) {}

  // Converts a vector of uint64 to a vector of S2CellIds.
  static std::vector<S2CellId> ToS2CellIds(const std::vector<uint64>& ids);

  std::vector<S2CellId> cell_ids_;
};


//////////////////   Implementation details follow   ////////////////////


inline S2CellUnion::S2CellUnion(std::vector<S2CellId> cell_ids)
    : cell_ids_(std::move(cell_ids)) {
  Normalize();
}

inline S2CellUnion S2CellUnion::FromNormalized(std::vector<S2CellId> cell_ids) {
  S2CellUnion result(std::move(cell_ids), VERBATIM);
  S2_DCHECK(result.IsNormalized());
  return result;
}

inline S2CellUnion S2CellUnion::FromVerbatim(std::vector<S2CellId> cell_ids) {
  S2CellUnion result(std::move(cell_ids), VERBATIM);
  S2_DCHECK(!absl::GetFlag(FLAGS_s2debug) || result.IsValid());
  return result;
}

inline void S2CellUnion::Init(std::vector<S2CellId> cell_ids) {
  cell_ids_ = std::move(cell_ids);
  Normalize();
}

inline void S2CellUnion::Clear() {
  // swap() guarantees to reduce the RHS vector's size and capacity
  // to zero (as opposed to clear(), shrink_to_fit() sequence).
  std::vector<S2CellId>().swap(cell_ids_);
}

inline std::vector<S2CellId> S2CellUnion::Release() {
  // vector's rvalue reference constructor does not necessarily leave
  // moved-from value in empty state, so swap instead.
  std::vector<S2CellId> cell_ids;
  cell_ids_.swap(cell_ids);
  return cell_ids;
}

inline std::vector<S2CellId>::const_iterator S2CellUnion::begin() const {
  return cell_ids_.begin();
}

inline std::vector<S2CellId>::const_iterator S2CellUnion::end() const {
  return cell_ids_.end();
}

// Output stream operator. Automatically guards against large inputs.
inline std::ostream& operator<<(std::ostream& os, const S2CellUnion& u) {
  return os << u.ToString();
}

#endif  // S2_S2CELL_UNION_H_