| 1 | // Copyright 2005 Google Inc. All Rights Reserved. |
| 2 | |
| 3 | #include "base/logging.h" |
| 4 | #include "strings/stringprintf.h" |
| 5 | #include "s2latlng.h" |
| 6 | |
| 7 | S2LatLng 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 | |
| 14 | S2Point 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 | |
| 31 | S2LatLng::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 | |
| 37 | S1Angle 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 | |
| 60 | string S2LatLng::ToStringInDegrees() const { |
| 61 | S2LatLng pt = Normalized(); |
| 62 | return StringPrintf("%f,%f" , pt.lat().degrees(), pt.lng().degrees()); |
| 63 | } |
| 64 | |
| 65 | void S2LatLng::ToStringInDegrees(string* s) const { |
| 66 | *s = ToStringInDegrees(); |
| 67 | } |
| 68 | |
| 69 | ostream& operator<<(ostream& os, S2LatLng const& ll) { |
| 70 | return os << "[" << ll.lat() << ", " << ll.lng() << "]" ; |
| 71 | } |
| 72 | |