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