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_S2CELL_UNION_H_
19 #define S2_S2CELL_UNION_H_
20
21 #include <vector>
22
23 #include "s2/base/commandlineflags.h"
24 #include "s2/base/integral_types.h"
25 #include "s2/base/logging.h"
26 #include "s2/_fp_contract_off.h"
27 #include "s2/s2cell_id.h"
28 #include "s2/s2region.h"
29 #include "s2/third_party/absl/base/macros.h"
30
31 class Decoder;
32 class Encoder;
33 class S1Angle;
34 class S2Cap;
35 class S2Cell;
36 class S2LatLngRect;
37
38 DECLARE_bool(s2debug);
39 DECLARE_int32(s2cell_union_decode_max_num_cells);
40
41 // An S2CellUnion is a region consisting of cells of various sizes. Typically
42 // a cell union is used to approximate some other shape. There is a tradeoff
43 // between the accuracy of the approximation and how many cells are used.
44 // Unlike polygons, cells have a fixed hierarchical structure. This makes
45 // them more suitable for optimizations based on preprocessing.
46 //
47 // An S2CellUnion is represented as a vector of sorted, non-overlapping
48 // S2CellIds. By default the vector is also "normalized", meaning that groups
49 // of 4 child cells have been replaced by their parent cell whenever possible.
50 // S2CellUnions are not required to be normalized, but certain operations will
51 // return different results if they are not (e.g., Contains(S2CellUnion).)
52 //
53 // S2CellUnion is movable and copyable.
54 class S2CellUnion final : public S2Region {
55 public:
56 // Creates an empty cell union.
S2CellUnion()57 S2CellUnion() {}
58
59 // Constructs a cell union with the given S2CellIds, then calls Normalize()
60 // to sort them, remove duplicates, and merge cells when possible. (See
61 // FromNormalized if your vector is already normalized.)
62 //
63 // The argument is passed by value, so if you are passing a named variable
64 // and have no further use for it, consider using std::move().
65 //
66 // A cell union containing a single S2CellId may be constructed like this:
67 //
68 // S2CellUnion example({cell_id});
69 explicit S2CellUnion(std::vector<S2CellId> cell_ids);
70
71 // Convenience constructor that accepts a vector of uint64. Note that
72 // unlike the constructor above, this one makes a copy of "cell_ids".
73 explicit S2CellUnion(const std::vector<uint64>& cell_ids);
74
75 // Constructs a cell union for the whole sphere.
76 static S2CellUnion WholeSphere();
77
78 // Constructs a cell union from S2CellIds that have already been normalized
79 // (typically because they were extracted from another S2CellUnion).
80 //
81 // The argument is passed by value, so if you are passing a named variable
82 // and have no further use for it, consider using std::move().
83 //
84 // REQUIRES: "cell_ids" satisfies the requirements of IsNormalized().
85 static S2CellUnion FromNormalized(std::vector<S2CellId> cell_ids);
86
87 // Constructs a cell union from a vector of sorted, non-overlapping
88 // S2CellIds. Unlike the other constructors, FromVerbatim does not require
89 // that groups of 4 child cells have been replaced by their parent cell. In
90 // other words, "cell_ids" must satisfy the requirements of IsValid() but
91 // not necessarily IsNormalized().
92 //
93 // Note that if the cell union is not normalized, certain operations may
94 // return different results (e.g., Contains(S2CellUnion)).
95 //
96 // REQUIRES: "cell_ids" satisfies the requirements of IsValid().
97 static S2CellUnion FromVerbatim(std::vector<S2CellId> cell_ids);
98
99 // Constructs a cell union that corresponds to a continuous range of cell
100 // ids. The output is a normalized collection of cell ids that covers the
101 // leaf cells between "min_id" and "max_id" inclusive.
102 //
103 // REQUIRES: min_id.is_leaf(), max_id.is_leaf(), min_id <= max_id.
104 static S2CellUnion FromMinMax(S2CellId min_id, S2CellId max_id);
105
106 // Like FromMinMax() except that the union covers the range of leaf cells
107 // from "begin" (inclusive) to "end" (exclusive), as with Python ranges or
108 // STL iterator ranges. If (begin == end) the result is empty.
109 //
110 // REQUIRES: begin.is_leaf(), end.is_leaf(), begin <= end.
111 static S2CellUnion FromBeginEnd(S2CellId begin, S2CellId end);
112
113 // Init() methods corresponding to the constructors/factory methods above.
114 // TODO(ericv): Consider deprecating these methods in favor of using the
115 // constructors and move assignment operator.
116 void Init(std::vector<S2CellId> cell_ids);
117 void Init(const std::vector<uint64>& cell_ids);
118 void InitFromMinMax(S2CellId min_id, S2CellId max_id);
119 void InitFromBeginEnd(S2CellId begin, S2CellId end);
120
121 // Clears the contents of the cell union and minimizes memory usage.
122 void Clear();
123
124 // Gives ownership of the vector data to the client without copying, and
125 // clears the content of the cell union. The original data in cell_ids
126 // is lost if there was any.
127 std::vector<S2CellId> Release();
128
129 // Convenience methods for accessing the individual cell ids.
num_cells()130 int num_cells() const { return static_cast<int>(cell_ids_.size()); }
cell_id(int i)131 S2CellId cell_id(int i) const { return cell_ids_[i]; }
132
133 // Vector-like methods for accessing the individual cell ids.
size()134 size_t size() const { return cell_ids_.size(); }
empty()135 bool empty() const { return cell_ids_.empty(); }
136 S2CellId operator[](int i) const { return cell_ids_[i]; }
137
138 // Standard begin/end methods, to allow range-based for loops:
139 //
140 // for (S2CellId id : cell_union) { ... }
141 std::vector<S2CellId>::const_iterator begin() const;
142 std::vector<S2CellId>::const_iterator end() const;
143
144 // Direct access to the underlying vector for STL algorithms.
cell_ids()145 const std::vector<S2CellId>& cell_ids() const { return cell_ids_; }
146
147 // Returns true if the cell union is valid, meaning that the S2CellIds are
148 // valid, non-overlapping, and sorted in increasing order.
149 bool IsValid() const;
150
151 // Returns true if the cell union is normalized, meaning that it is
152 // satisfies IsValid() and that no four cells have a common parent.
153 // Certain operations such as Contains(S2CellUnion) will return a different
154 // result if the cell union is not normalized.
155 bool IsNormalized() const;
156
157 // Normalizes the cell union by discarding cells that are contained by other
158 // cells, replacing groups of 4 child cells by their parent cell whenever
159 // possible, and sorting all the cell ids in increasing order.
160 //
161 // Returns true if the number of cells was reduced.
162 // TODO(ericv): Change this method to return void.
163 bool Normalize();
164
165 // Replaces "output" with an expanded version of the cell union where any
166 // cells whose level is less than "min_level" or where (level - min_level)
167 // is not a multiple of "level_mod" are replaced by their children, until
168 // either both of these conditions are satisfied or the maximum level is
169 // reached.
170 //
171 // This method allows a covering generated by S2RegionCoverer using
172 // min_level() or level_mod() constraints to be stored as a normalized cell
173 // union (which allows various geometric computations to be done) and then
174 // converted back to the original list of cell ids that satisfies the
175 // desired constraints.
176 void Denormalize(int min_level, int level_mod,
177 std::vector<S2CellId>* output) const;
178
179 // If there are more than "excess" elements of the cell_ids() vector that
180 // are allocated but unused, reallocates the array to eliminate the excess
181 // space. This reduces memory usage when many cell unions need to be held
182 // in memory at once.
183 void Pack(int excess = 0);
184
185 // Returns true if the cell union contains the given cell id. Containment
186 // is defined with respect to regions, e.g. a cell contains its 4 children.
187 // This is a fast operation (logarithmic in the size of the cell union).
188 //
189 // CAVEAT: If you have constructed a non-normalized S2CellUnion using
190 // FromVerbatim, note that groups of 4 child cells are *not* considered to
191 // contain their parent cell. To get this behavior you must use one of the
192 // other constructors or call Normalize() explicitly.
193 bool Contains(S2CellId id) const;
194
195 // Returns true if the cell union intersects the given cell id.
196 // This is a fast operation (logarithmic in the size of the cell union).
197 bool Intersects(S2CellId id) const;
198
199 // Returns true if this cell union contains the given other cell union.
200 //
201 // CAVEAT: If you have constructed a non-normalized S2CellUnion using
202 // FromVerbatim, note that groups of 4 child cells are *not* considered to
203 // contain their parent cell. To get this behavior you must use one of the
204 // other constructors or call Normalize() explicitly.
205 bool Contains(const S2CellUnion& y) const;
206
207 // Returns true if this cell union intersects the given other cell union.
208 bool Intersects(const S2CellUnion& y) const;
209
210 // Returns the union of the two given cell unions.
211 S2CellUnion Union(const S2CellUnion& y) const;
212
213 // Returns the intersection of the two given cell unions.
214 S2CellUnion Intersection(const S2CellUnion& y) const;
215
216 // Specialized version of GetIntersection() that returns the intersection of
217 // a cell union with an S2CellId. This can be useful for splitting a cell
218 // union into pieces.
219 S2CellUnion Intersection(S2CellId id) const;
220
221 // Returns the difference of the two given cell unions.
222 S2CellUnion Difference(const S2CellUnion& y) const;
223
224 // Expands the cell union by adding a buffer of cells at "expand_level"
225 // around the union boundary.
226 //
227 // For each cell "c" in the union, we add all neighboring cells at level
228 // "expand_level" that are adjacent to "c". Note that there can be many
229 // such cells if "c" is large compared to "expand_level". If "c" is smaller
230 // than "expand_level", we first add the parent of "c" at "expand_level" and
231 // then add all the neighbors of that cell.
232 //
233 // Note that the size of the output is exponential in "expand_level". For
234 // example, if expand_level == 20 and the input has a cell at level 10,
235 // there will be on the order of 4000 adjacent cells in the output. For
236 // most applications the Expand(min_radius, max_level_diff) method below is
237 // easier to use.
238 void Expand(int expand_level);
239
240 // Expands the cell union such that it contains all points whose distance to
241 // the cell union is at most "min_radius", but do not use cells that are
242 // more than "max_level_diff" levels higher than the largest cell in the
243 // input. The second parameter controls the tradeoff between accuracy and
244 // output size when a large region is being expanded by a small amount
245 // (e.g. expanding Canada by 1km). For example, if max_level_diff == 4 the
246 // region will always be expanded by approximately 1/16 the width of its
247 // largest cell. Note that in the worst case, the number of cells in the
248 // output can be up to 4 * (1 + 2 ** max_level_diff) times larger than the
249 // number of cells in the input.
250 void Expand(S1Angle min_radius, int max_level_diff);
251
252 // The number of leaf cells covered by the union.
253 // This will be no more than 6*2^60 for the whole sphere.
254 uint64 LeafCellsCovered() const;
255
256 // Approximates this cell union's area in steradians by summing the average
257 // area of each contained cell's average area, using the AverageArea method
258 // from the S2Cell class. This is equivalent to the number of leaves covered,
259 // multiplied by the average area of a leaf. Note that AverageArea does not
260 // take into account distortion of cell, and thus may be off by up to a
261 // factor of up to 1.7.
262 //
263 // NOTE: Since this is proportional to LeafCellsCovered(), it is
264 // always better to use that function if all you care about is
265 // the relative average area between objects.
266 double AverageBasedArea() const;
267
268 // Calculates this cell union's area in steradians by summing the approximate
269 // area for each contained cell, using the ApproxArea method from the S2Cell
270 // class.
271 double ApproxArea() const;
272
273 // Calculates this cell union's area in steradians by summing the exact area
274 // for each contained cell, using the Exact method from the S2Cell class.
275 double ExactArea() const;
276
277 // Return true if two cell unions are identical.
278 friend bool operator==(const S2CellUnion& x, const S2CellUnion& y);
279
280 // Return true if two cell unions are different.
281 friend bool operator!=(const S2CellUnion& x, const S2CellUnion& y);
282
283 ////////////////////////////////////////////////////////////////////////
284 // S2Region interface (see s2region.h for details):
285
286 S2CellUnion* Clone() const override;
287 S2Cap GetCapBound() const override;
288 S2LatLngRect GetRectBound() const override;
289
290 // This is a fast operation (logarithmic in the size of the cell union).
291 bool Contains(const S2Cell& cell) const override;
292
293 // This is a fast operation (logarithmic in the size of the cell union).
294 bool MayIntersect(const S2Cell& cell) const override;
295
296 // The point 'p' does not need to be normalized.
297 // This is a fast operation (logarithmic in the size of the cell union).
298 bool Contains(const S2Point& p) const override;
299
300 // Appends a serialized representation of the S2CellUnion to "encoder".
301 //
302 // REQUIRES: "encoder" uses the default constructor, so that its buffer
303 // can be enlarged as necessary by calling Ensure(int).
304 void Encode(Encoder* const encoder) const;
305
306 // Decodes an S2CellUnion encoded with Encode(). Returns true on success.
307 bool Decode(Decoder* const decoder);
308
309 ////////////////////////////////////////////////////////////////////////
310 // Static methods intended for high-performance clients that prefer to
311 // manage their own storage.
312
313 // Like Normalize(), but works with a vector of S2CellIds.
314 // Equivalent to:
315 // *cell_ids = S2CellUnion(std::move(*cell_ids)).Release();
316 static bool Normalize(std::vector<S2CellId>* cell_ids);
317
318 // Like Denormalize(), but works with a vector of S2CellIds.
319 // REQUIRES: out != &in
320 static void Denormalize(const std::vector<S2CellId>& in,
321 int min_level, int level_mod,
322 std::vector<S2CellId>* out);
323
324 // Like GetIntersection(), but works directly with vectors of S2CellIds,
325 // Equivalent to:
326 //
327 // *out = S2CellUnion(x).Intersection(S2CellUnion(y)).Release()
328 //
329 // except that this method has slightly more relaxed normalization
330 // requirements: the input vectors may contain groups of 4 child cells that
331 // all have the same parent. (In a normalized S2CellUnion, such groups are
332 // always replaced by the parent cell.)
333 static void GetIntersection(const std::vector<S2CellId>& x,
334 const std::vector<S2CellId>& y,
335 std::vector<S2CellId>* out);
336
337 private:
338 friend class S2CellUnionTestPeer; // For creating invalid S2CellUnions.
339
340 // Internal constructor that does not check "cell_ids" for validity.
341 enum VerbatimFlag { VERBATIM };
S2CellUnion(std::vector<S2CellId> cell_ids,VerbatimFlag verbatim)342 S2CellUnion(std::vector<S2CellId> cell_ids, VerbatimFlag verbatim)
343 : cell_ids_(std::move(cell_ids)) {}
344
345 // Converts a vector of uint64 to a vector of S2CellIds.
346 static std::vector<S2CellId> ToS2CellIds(const std::vector<uint64>& ids);
347
348 std::vector<S2CellId> cell_ids_;
349 };
350
351
352 ////////////////// Implementation details follow ////////////////////
353
354
S2CellUnion(std::vector<S2CellId> cell_ids)355 inline S2CellUnion::S2CellUnion(std::vector<S2CellId> cell_ids)
356 : cell_ids_(std::move(cell_ids)) {
357 Normalize();
358 }
359
FromNormalized(std::vector<S2CellId> cell_ids)360 inline S2CellUnion S2CellUnion::FromNormalized(std::vector<S2CellId> cell_ids) {
361 S2CellUnion result(std::move(cell_ids), VERBATIM);
362 S2_DCHECK(result.IsNormalized());
363 return result;
364 }
365
FromVerbatim(std::vector<S2CellId> cell_ids)366 inline S2CellUnion S2CellUnion::FromVerbatim(std::vector<S2CellId> cell_ids) {
367 S2CellUnion result(std::move(cell_ids), VERBATIM);
368 S2_DCHECK(!FLAGS_s2debug || result.IsValid());
369 return result;
370 }
371
Init(std::vector<S2CellId> cell_ids)372 inline void S2CellUnion::Init(std::vector<S2CellId> cell_ids) {
373 cell_ids_ = std::move(cell_ids);
374 Normalize();
375 }
376
Clear()377 inline void S2CellUnion::Clear() {
378 // swap() guarantees to reduce the RHS vector's size and capacity
379 // to zero (as opposed to clear(), shrink_to_fit() sequence).
380 std::vector<S2CellId>().swap(cell_ids_);
381 }
382
Release()383 inline std::vector<S2CellId> S2CellUnion::Release() {
384 // vector's rvalue reference constructor does not necessarily leave
385 // moved-from value in empty state, so swap instead.
386 std::vector<S2CellId> cell_ids;
387 cell_ids_.swap(cell_ids);
388 return cell_ids;
389 }
390
begin()391 inline std::vector<S2CellId>::const_iterator S2CellUnion::begin() const {
392 return cell_ids_.begin();
393 }
394
end()395 inline std::vector<S2CellId>::const_iterator S2CellUnion::end() const {
396 return cell_ids_.end();
397 }
398
399 #endif // S2_S2CELL_UNION_H_
400