Coverage Report

Created: 2025-10-12 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2cell.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_S2CELL_H_
19
#define S2_S2CELL_H_
20
21
#include <cmath>
22
#include <cstdint>
23
#include <vector>
24
25
#include "s2/_fp_contract_off.h"  // IWYU pragma: keep
26
#include "s2/r2rect.h"
27
#include "s2/s1chord_angle.h"
28
#include "s2/s2cell_id.h"
29
#include "s2/s2coords.h"
30
#include "s2/s2point.h"
31
#include "s2/s2region.h"
32
#include "s2/util/coding/coder.h"
33
#include "s2/util/math/vector.h"
34
35
class S2Cap;
36
class S2LatLng;
37
class S2LatLngRect;
38
39
// An S2Cell is an S2Region object that represents a cell.  Unlike S2CellIds,
40
// it supports efficient containment and intersection tests.  However, it is
41
// also a more expensive representation (currently 48 bytes rather than 8).
42
43
// This class is intended to be copied by value as desired.  It uses
44
// the default copy constructor and assignment operator, however it is
45
// not a "plain old datatype" (POD) because it has virtual functions.
46
class S2Cell final : public S2Region {
47
 public:
48
  // Canonical identifiers for the boundaries of the cell.  It's promised that
49
  // both GetVertex() and GetBoundUV().GetVertex() return vertices in this
50
  // order.
51
  //
52
  // That is, for a given boundary k, the edge defining the boundary is:
53
  //   {GetVertex(k), GetVertex(k+1)}
54
  //
55
  // The boundaries are defined in UV coordinates.  The orientation may be
56
  // rotated relative to other face cells, but are consistent within a face
57
  // (i.e. a cell's left edge is its left-ward neighbor's right edge).
58
  //
59
  enum Boundary {
60
    // clang-format off
61
    kBottomEdge = 0,
62
    kRightEdge  = 1,
63
    kTopEdge    = 2,
64
    kLeftEdge   = 3
65
    // clang-format on
66
  };
67
68
  // The default constructor is required in order to use freelists.
69
  // Cells should otherwise always be constructed explicitly.
70
0
  S2Cell() = default;
71
72
  // An S2Cell always corresponds to a particular S2CellId.  The other
73
  // constructors are just convenience methods.
74
  explicit S2Cell(S2CellId id);
75
76
  // Convenience constructors.  The S2LatLng must be normalized.
77
0
  explicit S2Cell(const S2Point& p) : S2Cell(S2CellId(p)) {}
78
0
  explicit S2Cell(const S2LatLng& ll) : S2Cell(S2CellId(ll)) {}
79
80
  // Returns the cell corresponding to the given S2 cube face.
81
0
  static S2Cell FromFace(int face) {
82
0
    return S2Cell(S2CellId::FromFace(face));
83
0
  }
84
85
  // Returns a cell given its face (range 0..5), Hilbert curve position within
86
  // that face (an unsigned integer with S2CellId::kPosBits bits), and level
87
  // (range 0..kMaxLevel).  The given position will be modified to correspond
88
  // to the Hilbert curve position at the center of the returned cell.  This
89
  // is a static function rather than a constructor in order to indicate what
90
  // the arguments represent.
91
0
  static S2Cell FromFacePosLevel(int face, uint64_t pos, int level) {
92
0
    return S2Cell(S2CellId::FromFacePosLevel(face, pos, level));
93
0
  }
94
95
0
  S2CellId id() const { return id_; }
96
0
  int face() const { return face_; }
97
0
  int level() const { return level_; }
98
0
  int orientation() const { return orientation_; }
99
0
  bool is_leaf() const { return level_ == S2CellId::kMaxLevel; }
100
101
  // These are equivalent to the S2CellId methods, but have a more efficient
102
  // implementation since the level has been precomputed.
103
  int GetSizeIJ() const;
104
  double GetSizeST() const;
105
106
  // Returns the k-th vertex of the cell (k = 0,1,2,3).  Vertices are returned
107
  // in CCW order (lower left, lower right, upper right, upper left in the UV
108
  // plane).  The points returned by GetVertexRaw are not normalized.
109
  // For convenience, the argument is reduced modulo 4 to the range [0..3].
110
0
  S2Point GetVertex(int k) const { return GetVertexRaw(k).Normalize(); }
111
0
  S2Point GetVertexRaw(int k) const {
112
0
    return S2::FaceUVtoXYZ(face_, uv_.GetVertex(k));
113
0
  }
114
115
  // Returns either U or V for the given edge, whichever is constant along it.
116
  //
117
  // E.g. boundaries 0 and 2 are constant in the V axis so we return those
118
  // coordinates, but boundaries 1 and 3 are constant in the U axis, so we
119
  // return those coordinates.
120
  //
121
  // For convenience, the argument is reduced modulo 4 to the range [0..3].
122
0
  double GetUVCoordOfEdge(int k) const {
123
0
    k %= 4;
124
0
    if (k % 2 == 0) {
125
0
      return GetBoundUV().GetVertex(k).y();
126
0
    }
127
0
    return GetBoundUV().GetVertex(k).x();
128
0
  }
129
130
  // Returns either I or J for the given edge, whichever is constant along it.
131
  //
132
  // E.g. boundaries 0 and 2 are constant in the J axis so we return those
133
  // coordinates, but boundaries 1 and 3 are constant in the I axis, so we
134
  // return those coordinates.
135
  //
136
  // The returned value is not clamped to S2::kLimitIJ-1 as in S2::StToIJ, so
137
  // that cell edges at the maximum extent of a face are properly returned as
138
  // S2::kLimitIJ.
139
  //
140
  // For convenience, the argument is reduced modulo 4 to the range [0..3].
141
0
  int32_t GetIJCoordOfEdge(int k) const {
142
0
    // We can just convert UV->ST->IJ for this because the IJ coordinates only
143
0
    // have 30 bits of resolution in each axis.  The error in the conversion
144
0
    // will be a couple of epsilon which is <<< 2^-30, so if we use a proper
145
0
    // round-to-nearest operation, we'll always round to the correct IJ value.
146
0
    //
147
0
    // But, we need to explicitly use round here since Mathutil::FastIntRound
148
0
    // rounds differently on different platforms.  If we land on 0, we may end
149
0
    // up rounding to -1.
150
0
    //
151
0
    // Intel CPUs that support SSE4.1 have the ROUNDSD instruction, and ARM CPUs
152
0
    // with VFP have the VCVT instruction, both of which can implement correct
153
0
    // rounding efficiently regardless of the current FPU rounding mode.
154
0
    return std::round(S2::kLimitIJ * S2::UVtoST(GetUVCoordOfEdge(k)));
155
0
  }
156
157
  // Returns the inward-facing normal of the great circle passing through the
158
  // edge from vertex k to vertex k+1 (mod 4).  The normals returned by
159
  // GetEdgeRaw are not necessarily unit length, but their length is bounded by
160
  // sqrt(2) since the worst case is two components of magnitude 1.
161
  //
162
  // The vertices returned by GetVertex are not guaranteed to actually be on the
163
  // boundary of the cell exactly.  Instead, they're the nearest representable
164
  // point to the corner.
165
  //
166
  // Cell edge normals returned by GetEdgeRaw, however, are computed exactly and
167
  // can be used with exact predicates to determine spatial relationships to the
168
  // cell exactly.
169
  //
170
  // GetEdge() normalizes it's return value and thus may no longer be exact.
171
  //
172
  // For convenience, the argument is reduced modulo 4 to the range [0..3].
173
0
  S2Point GetEdge(int k) const { return GetEdgeRaw(k).Normalize(); }
174
  S2Point GetEdgeRaw(int k) const;
175
176
  // If this is not a leaf cell, sets children[0..3] to the four children of
177
  // this cell (in traversal order) and return true.  Otherwise returns false.
178
  // This method is equivalent to the following:
179
  //
180
  // for (pos=0, id=child_begin(); id != child_end(); id = id.next(), ++pos)
181
  //   children[pos] = S2Cell(id);
182
  //
183
  // except that it is more than two times faster.
184
  bool Subdivide(S2Cell children[4]) const;
185
186
  // Returns the direction vector corresponding to the center in (s,t)-space of
187
  // the given cell.  This is the point at which the cell is divided into four
188
  // subcells; it is not necessarily the centroid of the cell in (u,v)-space
189
  // or (x,y,z)-space.  The point returned by GetCenterRaw is not necessarily
190
  // unit length.
191
0
  S2Point GetCenter() const { return GetCenterRaw().Normalize(); }
192
  S2Point GetCenterRaw() const;
193
194
  // Returns the average area for cells at the given level.
195
  static double AverageArea(int level);
196
197
  // Returns the average area of cells at this level in steradians.  This is
198
  // accurate to within a factor of 1.7 (for S2_QUADRATIC_PROJECTION) and is
199
  // extremely cheap to compute.
200
0
  double AverageArea() const { return AverageArea(level_); }
201
202
  // Returns the approximate area of this cell in steradians.  This method is
203
  // accurate to within 3% percent for all cell sizes and accurate to within
204
  // 0.1% for cells at level 5 or higher (i.e. squares 350km to a side or
205
  // smaller on the Earth's surface).  It is moderately cheap to compute.
206
  double ApproxArea() const;
207
208
  // Returns the area of this cell as accurately as possible.  This method is
209
  // more expensive but it is accurate to 6 digits of precision even for leaf
210
  // cells (whose area is approximately 1e-18).
211
  double ExactArea() const;
212
213
  // Returns the bounds of this cell in (u,v)-space.
214
0
  R2Rect GetBoundUV() const { return uv_; }
215
216
  // Returns the distance from the cell to the given point.  Returns zero if
217
  // the point is inside the cell.
218
  S1ChordAngle GetDistance(const S2Point& target) const;
219
220
  // Return the distance from the cell boundary to the given point.
221
  S1ChordAngle GetBoundaryDistance(const S2Point& target) const;
222
223
  // Returns the maximum distance from the cell (including its interior) to the
224
  // given point.
225
  S1ChordAngle GetMaxDistance(const S2Point& target) const;
226
227
  // Returns the minimum distance from the cell to the given edge AB.  Returns
228
  // zero if the edge intersects the cell interior.
229
  S1ChordAngle GetDistance(const S2Point& a, const S2Point& b) const;
230
231
  // Returns the maximum distance from the cell (including its interior) to the
232
  // given edge AB.
233
  S1ChordAngle GetMaxDistance(const S2Point& a, const S2Point& b) const;
234
235
  // Returns the distance from the cell to the given cell.  Returns zero if
236
  // one cell contains the other.
237
  S1ChordAngle GetDistance(const S2Cell& target) const;
238
239
  // Returns the maximum distance from the cell (including its interior) to the
240
  // given target cell.
241
  S1ChordAngle GetMaxDistance(const S2Cell& target) const;
242
243
  ////////////////////////////////////////////////////////////////////////
244
  // S2Region interface (see s2region.h for details):
245
246
  S2Cell* Clone() const override;
247
  S2Cap GetCapBound() const override;
248
  S2LatLngRect GetRectBound() const override;
249
  void GetCellUnionBound(std::vector<S2CellId>* cell_ids) const override;
250
  bool Contains(const S2Cell& cell) const override;
251
  bool MayIntersect(const S2Cell& cell) const override;
252
253
  // Returns true if the cell contains the given point "p".  Note that unlike
254
  // S2Loop/S2Polygon, S2Cells are considered to be closed sets.  This means
255
  // that points along an S2Cell edge (or at a vertex) belong to the adjacent
256
  // cell(s) as well.
257
  //
258
  // If instead you want every point to be contained by exactly one S2Cell,
259
  // you will need to convert the S2Cells to S2Loops (which implement point
260
  // containment this way).
261
  //
262
  // The point "p" does not need to be normalized.
263
  bool Contains(const S2Point& p) const override;
264
265
  // Appends a serialized representation of the S2Cell to "encoder".
266
  //
267
  // REQUIRES: "encoder" uses the default constructor, so that its buffer
268
  //           can be enlarged as necessary by calling Ensure(int).
269
  void Encode(Encoder* encoder) const;
270
271
  // Decodes an S2Cell encoded with Encode().  Returns true on success.
272
  bool Decode(Decoder* decoder);
273
274
 private:
275
  // Returns the latitude or longitude of the cell vertex given by (i,j),
276
  // where "i" and "j" are either 0 or 1.
277
  double GetLatitude(int i, int j) const;
278
  double GetLongitude(int i, int j) const;
279
280
  S1ChordAngle VertexChordDist(const S2Point& p, int i, int j) const;
281
  bool UEdgeIsClosest(const S2Point& target, int v_end) const;
282
  bool VEdgeIsClosest(const S2Point& target, int u_end) const;
283
284
  // Returns the distance from the given point to the interior of the cell if
285
  // "to_interior" is true, and to the boundary of the cell otherwise.
286
  S1ChordAngle GetDistanceInternal(const S2Point& target_xyz,
287
                                   bool to_interior) const;
288
289
  // This structure occupies 44 bytes plus one pointer for the vtable.
290
  int8_t face_;
291
  int8_t level_;
292
  int8_t orientation_;
293
  S2CellId id_;
294
  R2Rect uv_;
295
};
296
297
0
inline bool operator==(const S2Cell& x, const S2Cell& y) {
298
0
  return x.id() == y.id();
299
0
}
300
301
0
inline bool operator!=(const S2Cell& x, const S2Cell& y) {
302
0
  return x.id() != y.id();
303
0
}
304
305
0
inline bool operator<(const S2Cell& x, const S2Cell& y) {
306
0
  return x.id() < y.id();
307
0
}
308
309
0
inline bool operator>(const S2Cell& x, const S2Cell& y) {
310
0
  return x.id() > y.id();
311
0
}
312
313
0
inline bool operator<=(const S2Cell& x, const S2Cell& y) {
314
0
  return x.id() <= y.id();
315
0
}
316
317
0
inline bool operator>=(const S2Cell& x, const S2Cell& y) {
318
0
  return x.id() >= y.id();
319
0
}
320
321
0
inline int S2Cell::GetSizeIJ() const {
322
0
  return S2CellId::GetSizeIJ(level());
323
0
}
324
325
0
inline double S2Cell::GetSizeST() const {
326
0
  return S2CellId::GetSizeST(level());
327
0
}
328
329
#endif  // S2_S2CELL_H_