/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_ |