1// Copyright 2005 Google Inc. All Rights Reserved.
2
3#include "base/logging.h"
4#include "strings/stringprintf.h"
5#include "s2latlng.h"
6
7S2LatLng S2LatLng::Normalized() const {
8 // drem(x, 2 * M_PI) reduces its argument to the range [-M_PI, M_PI]
9 // inclusive, which is what we want here.
10 return S2LatLng(max(-M_PI_2, min(M_PI_2, lat().radians())),
11 drem(lng().radians(), 2 * M_PI));
12}
13
14S2Point S2LatLng::ToPoint() const {
15 DCHECK(is_valid());
16 // As of crosstool v14, gcc tries to calculate sin(phi), cos(phi),
17 // sin(theta), cos(theta) on the following section by two sincos()
18 // calls. However, for some inputs, sincos() returns significantly
19 // different values between AMD and Intel.
20 //
21 // As a temporary workaround, "volatile" is added to phi and theta
22 // to prohibit the compiler to use such sincos() call, because sin()
23 // and cos() don't seem to have the problem. See b/3088321 for
24 // details.
25 volatile double phi = lat().radians();
26 volatile double theta = lng().radians();
27 double cosphi = cos(phi);
28 return S2Point(cos(theta) * cosphi, sin(theta) * cosphi, sin(phi));
29}
30
31S2LatLng::S2LatLng(S2Point const& p)
32 : coords_(Latitude(p).radians(), Longitude(p).radians()) {
33 // The latitude and longitude are already normalized.
34 DCHECK(is_valid());
35}
36
37S1Angle S2LatLng::GetDistance(S2LatLng const& o) const {
38 // This implements the Haversine formula, which is numerically stable for
39 // small distances but only gets about 8 digits of precision for very large
40 // distances (e.g. antipodal points). Note that 8 digits is still accurate
41 // to within about 10cm for a sphere the size of the Earth.
42 //
43 // This could be fixed with another sin() and cos() below, but at that point
44 // you might as well just convert both arguments to S2Points and compute the
45 // distance that way (which gives about 15 digits of accuracy for all
46 // distances).
47
48 DCHECK(is_valid());
49 DCHECK(o.is_valid());
50 double lat1 = lat().radians();
51 double lat2 = o.lat().radians();
52 double lng1 = lng().radians();
53 double lng2 = o.lng().radians();
54 double dlat = sin(0.5 * (lat2 - lat1));
55 double dlng = sin(0.5 * (lng2 - lng1));
56 double x = dlat * dlat + dlng * dlng * cos(lat1) * cos(lat2);
57 return S1Angle::Radians(2 * atan2(sqrt(x), sqrt(max(0.0, 1.0 - x))));
58}
59
60string S2LatLng::ToStringInDegrees() const {
61 S2LatLng pt = Normalized();
62 return StringPrintf("%f,%f", pt.lat().degrees(), pt.lng().degrees());
63}
64
65void S2LatLng::ToStringInDegrees(string* s) const {
66 *s = ToStringInDegrees();
67}
68
69ostream& operator<<(ostream& os, S2LatLng const& ll) {
70 return os << "[" << ll.lat() << ", " << ll.lng() << "]";
71}
72