Coverage Report

Created: 2026-06-09 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/s2geometry/src/s2/s2latlng.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_S2LATLNG_H_
19
#define S2_S2LATLNG_H_
20
21
#include <cmath>
22
#include <cstdint>
23
#include <iosfwd>
24
#include <ostream>
25
#include <string>
26
#include <utility>
27
28
#include "absl/hash/hash.h"
29
30
#include "s2/_fp_contract_off.h"  // IWYU pragma: keep
31
#include "s2/r2.h"
32
#include "s2/s1angle.h"
33
#include "s2/s2coder.h"
34
#include "s2/s2error.h"
35
#include "s2/s2point.h"
36
#include "s2/util/coding/coder.h"
37
#include "s2/util/math/vector.h"
38
39
// This class represents a point on the unit sphere as a pair
40
// of latitude-longitude coordinates.  Like the rest of the "geometry"
41
// package, the intent is to represent spherical geometry as a mathematical
42
// abstraction, so functions that are specifically related to the Earth's
43
// geometry (e.g. easting/northing conversions) should be put elsewhere.
44
//
45
// This class is intended to be copied by value as desired.  It uses
46
// the default copy constructor and assignment operator.
47
class S2LatLng {
48
 public:
49
  typedef s2coding::S2BasicCoder<S2LatLng> Coder;
50
51
  // Constructor.  The latitude and longitude are allowed to be outside
52
  // the is_valid() range.  However, note that most methods that accept
53
  // S2LatLngs expect them to be normalized (see Normalized() below).
54
  constexpr S2LatLng(S1Angle lat, S1Angle lng);
55
56
  // The default constructor sets the latitude and longitude to zero.  This is
57
  // mainly useful when declaring arrays, STL containers, etc.
58
  constexpr S2LatLng();
59
60
  // Convert a direction vector (not necessarily unit length) to an S2LatLng.
61
  explicit S2LatLng(const S2Point& p);
62
63
  // Decodes an S2LatLng, returning true on success.  Populates error
64
  // on failure.
65
  bool Init(Decoder* decoder, S2Error& error);
66
67
  // Returns an S2LatLng for which is_valid() will return false.
68
  static constexpr S2LatLng Invalid();
69
70
  // Convenience functions -- shorter than calling S1Angle::Radians(), etc.
71
  static constexpr S2LatLng FromRadians(double lat_radians, double lng_radians);
72
  static constexpr S2LatLng FromDegrees(double lat_degrees, double lng_degrees);
73
  static constexpr S2LatLng FromE5(int32_t lat_e5, int32_t lng_e5);
74
  static constexpr S2LatLng FromE6(int32_t lat_e6, int32_t lng_e6);
75
  static constexpr S2LatLng FromE7(int32_t lat_e7, int32_t lng_e7);
76
77
  // Appends an encoded representation of the S2LatLng to "encoder".
78
  //
79
  // REQUIRES: "encoder" uses the default constructor, so that its buffer can be
80
  //           enlarged as necessary by calling Ensure(int).
81
  void Encode(Encoder* encoder) const;
82
83
  // Convenience functions -- to use when args have been fixed32s in protos.
84
  //
85
  // The arguments are static_cast into int32_t, so very large unsigned values
86
  // are treated as negative numbers.
87
  static constexpr S2LatLng FromUnsignedE6(uint32_t lat_e6, uint32_t lng_e6);
88
  static constexpr S2LatLng FromUnsignedE7(uint32_t lat_e7, uint32_t lng_e7);
89
90
  // Methods to compute the latitude and longitude of a point separately.
91
  static S1Angle Latitude(const S2Point& p);
92
  static S1Angle Longitude(const S2Point& p);
93
94
  // Accessor methods.
95
6.76M
  constexpr S1Angle lat() const { return S1Angle::Radians(coords_.x()); }
96
6.76M
  constexpr S1Angle lng() const { return S1Angle::Radians(coords_.y()); }
97
0
  const R2Point& coords() const { return coords_; }
98
99
  // Return true if the latitude is between -90 and 90 degrees inclusive
100
  // and the longitude is between -180 and 180 degrees inclusive.
101
  bool is_valid() const;
102
103
  // Clamps the latitude to the range [-90, 90] degrees, and adds or subtracts
104
  // a multiple of 360 degrees to the longitude if necessary to reduce it to
105
  // the range [-180, 180]. Returns Invalid() if not finite.
106
  S2LatLng Normalized() const;
107
108
  // Converts an S2LatLng to the equivalent unit-length vector.  Unnormalized
109
  // values (see Normalize()) are wrapped around the sphere as would be expected
110
  // based on their definition as spherical angles.  So for example the
111
  // following pairs yield equivalent points (modulo numerical error):
112
  //     (90.5, 10) =~ (89.5, -170)
113
  //     (a, b) =~ (a + 360 * n, b)
114
  // The maximum error in the result is 1.5 * DBL_EPSILON.  (This does not
115
  // include the error of converting degrees, E5, E6, or E7 to radians.)
116
  //
117
  // Can be used just like an S2Point constructor.  For example:
118
  //   S2Cap cap;
119
  //   cap.AddPoint(S2Point(latlng));
120
  //
121
  // REQUIRES: Latitude and longitude are finite.  (This is weaker than
122
  // `is_valid()`.
123
  explicit operator S2Point() const;
124
125
  // Converts to an S2Point (equivalent to the operator above).
126
  S2Point ToPoint() const;
127
128
  // Returns the distance (measured along the surface of the sphere) to the
129
  // given S2LatLng, implemented using the Haversine formula.  This is
130
  // equivalent to
131
  //
132
  //   S1Angle(ToPoint(), o.ToPoint())
133
  //
134
  // except that this function is slightly faster, and is also somewhat less
135
  // accurate for distances approaching 180 degrees (see s1angle.h for
136
  // details).  Both S2LatLngs must be normalized.
137
  S1Angle GetDistance(const S2LatLng& o) const;
138
139
  // Simple arithmetic operations for manipulating latitude-longitude pairs.
140
  // The results are not normalized (see Normalized()).
141
  friend S2LatLng operator+(const S2LatLng& a, const S2LatLng& b);
142
  friend S2LatLng operator-(const S2LatLng& a, const S2LatLng& b);
143
  friend S2LatLng operator*(double m, const S2LatLng& a);
144
  friend S2LatLng operator*(const S2LatLng& a, double m);
145
146
0
  bool operator==(const S2LatLng& o) const { return coords_ == o.coords_; }
147
0
  bool operator!=(const S2LatLng& o) const { return coords_ != o.coords_; }
148
0
  bool operator<(const S2LatLng& o) const { return coords_ < o.coords_; }
149
0
  bool operator>(const S2LatLng& o) const { return coords_ > o.coords_; }
150
0
  bool operator<=(const S2LatLng& o) const { return coords_ <= o.coords_; }
151
0
  bool operator>=(const S2LatLng& o) const { return coords_ >= o.coords_; }
152
153
  // Returns true if the numerical coordinates of two S2LatLng objects are
154
  // close.  Note that since S2LatLng operates on a rectangular space, the
155
  // behavior of ApproxEquals will does not reflect closeness of points on a
156
  // sphere if the points are close to the poles.  For those comparisons,
157
  // consider using GetDistance() instead.
158
  bool ApproxEquals(const S2LatLng& o,
159
0
                    S1Angle max_error = S1Angle::Radians(1e-15)) const {
160
0
    return coords_.aequal(o.coords_, max_error.radians());
161
0
  }
162
163
  // Exports the latitude and longitude in degrees, separated by a comma.
164
  // e.g. "94.518000,150.300000"
165
  std::string ToStringInDegrees() const;
166
167
 private:
168
  // Internal constructor.
169
0
  explicit S2LatLng(const R2Point& coords) : coords_(coords) {}
170
171
  // This is internal to avoid ambiguity about which units are expected.
172
  constexpr S2LatLng(double lat_radians, double lng_radians)
173
0
      : coords_(lat_radians, lng_radians) {}
174
175
  R2Point coords_;
176
};
177
178
// Hasher for S2LatLng.
179
// Does *not* need to be specified explicitly; this will be used by default for
180
// absl::flat_hash_map/set.
181
template <typename H>
182
H AbslHashValue(H h, const S2LatLng& lat_lng) {
183
  return H::combine(std::move(h), lat_lng.coords().x(), lat_lng.coords().y());
184
}
185
186
// Legacy hash functor for S2LatLng. This only exists for backwards
187
// compatibility with old hash types like std::unordered_map that don't use
188
// absl::Hash natively.
189
using S2LatLngHash = absl::Hash<S2LatLng>;
190
191
//////////////////   Implementation details follow   ////////////////////
192
193
constexpr S2LatLng::S2LatLng(S1Angle lat, S1Angle lng)
194
1.69M
    : coords_(lat.radians(), lng.radians()) {}
195
196
0
constexpr S2LatLng::S2LatLng() : coords_(0, 0) {}
197
198
inline constexpr S2LatLng S2LatLng::FromRadians(double lat_radians,
199
0
                                                double lng_radians) {
200
0
  return S2LatLng(lat_radians, lng_radians);
201
0
}
202
203
inline constexpr S2LatLng S2LatLng::FromDegrees(double lat_degrees,
204
1.69M
                                                double lng_degrees) {
205
1.69M
  return S2LatLng(S1Angle::Degrees(lat_degrees), S1Angle::Degrees(lng_degrees));
206
1.69M
}
207
208
0
inline constexpr S2LatLng S2LatLng::FromE5(int32_t lat_e5, int32_t lng_e5) {
209
0
  return S2LatLng(S1Angle::E5(lat_e5), S1Angle::E5(lng_e5));
210
0
}
211
212
0
inline constexpr S2LatLng S2LatLng::FromE6(int32_t lat_e6, int32_t lng_e6) {
213
0
  return S2LatLng(S1Angle::E6(lat_e6), S1Angle::E6(lng_e6));
214
0
}
215
216
0
inline constexpr S2LatLng S2LatLng::FromE7(int32_t lat_e7, int32_t lng_e7) {
217
0
  return S2LatLng(S1Angle::E7(lat_e7), S1Angle::E7(lng_e7));
218
0
}
219
220
inline constexpr S2LatLng S2LatLng::FromUnsignedE6(uint32_t lat_e6,
221
0
                                                   uint32_t lng_e6) {
222
0
  return S2LatLng(S1Angle::UnsignedE6(lat_e6), S1Angle::UnsignedE6(lng_e6));
223
0
}
224
225
inline constexpr S2LatLng S2LatLng::FromUnsignedE7(uint32_t lat_e7,
226
0
                                                   uint32_t lng_e7) {
227
0
  return S2LatLng(S1Angle::UnsignedE7(lat_e7), S1Angle::UnsignedE7(lng_e7));
228
0
}
229
230
0
inline constexpr S2LatLng S2LatLng::Invalid() {
231
  // These coordinates are outside the bounds allowed by is_valid().
232
0
  return S2LatLng(M_PI, 2 * M_PI);
233
0
}
234
235
0
inline S1Angle S2LatLng::Latitude(const S2Point& p) {
236
  // We use atan2 rather than asin because the input vector is not necessarily
237
  // unit length, and atan2 is much more accurate than asin near the poles.
238
  // The "+ 0.0" is to ensure that points with coordinates of -0.0 and +0.0
239
  // (which compare equal) are converted to identical S2LatLng values, since
240
  // even though -0.0 == +0.0 they can be formatted differently.
241
0
  return S1Angle::Radians(atan2(p[2] + 0.0, sqrt(p[0]*p[0] + p[1]*p[1])));
242
0
}
243
244
0
inline S1Angle S2LatLng::Longitude(const S2Point& p) {
245
  // The "+ 0.0" is to ensure that points with coordinates of -0.0 and +0.0
246
  // (which compare equal) are converted to identical S2LatLng values, since
247
  // even though -0.0 == +0.0 and -180 == 180 degrees, they can be formatted
248
  // differently.  Also note that atan2(0, 0) is defined to be zero.
249
0
  return S1Angle::Radians(atan2(p[1] + 0.0, p[0] + 0.0));
250
0
}
251
252
3.38M
inline bool S2LatLng::is_valid() const {
253
3.38M
  return (std::fabs(lat().radians()) <= M_PI_2 &&
254
3.38M
          std::fabs(lng().radians()) <= M_PI);
255
3.38M
}
256
257
0
inline S2LatLng::operator S2Point() const {
258
0
  return ToPoint();
259
0
}
260
261
0
inline S2LatLng operator+(const S2LatLng& a, const S2LatLng& b) {
262
0
  return S2LatLng(a.coords_ + b.coords_);
263
0
}
264
265
0
inline S2LatLng operator-(const S2LatLng& a, const S2LatLng& b) {
266
0
  return S2LatLng(a.coords_ - b.coords_);
267
0
}
268
269
0
inline S2LatLng operator*(double m, const S2LatLng& a) {
270
0
  return S2LatLng(m * a.coords_);
271
0
}
272
273
0
inline S2LatLng operator*(const S2LatLng& a, double m) {
274
0
  return S2LatLng(m * a.coords_);
275
0
}
276
277
std::ostream& operator<<(std::ostream& os, const S2LatLng& ll);
278
279
#endif  // S2_S2LATLNG_H_