| // Copyright 2005 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS-IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| |
| // Author: ericv@google.com (Eric Veach) |
| |
| #include "s2/s2latlng.h" |
| |
| #include <algorithm> |
| #include <ostream> |
| |
| #include "base/logging.h" |
| #include "base/strings/stringprintf.h" |
| |
| using std::max; |
| using std::min; |
| |
| S2LatLng S2LatLng::Normalized() const { |
| // remainder(x, 2 * M_PI) reduces its argument to the range [-M_PI, M_PI] |
| // inclusive, which is what we want here. |
| return S2LatLng(max(-M_PI_2, min(M_PI_2, lat().radians())), |
| remainder(lng().radians(), 2 * M_PI)); |
| } |
| |
| S2Point S2LatLng::ToPoint() const { |
| DLOG_IF(ERROR, !is_valid()) |
| << "Invalid S2LatLng in S2LatLng::ToPoint: " << *this; |
| double phi = lat().radians(); |
| double theta = lng().radians(); |
| double cosphi = cos(phi); |
| return S2Point(cos(theta) * cosphi, sin(theta) * cosphi, sin(phi)); |
| } |
| |
| S2LatLng::S2LatLng(S2Point const& p) |
| : coords_(Latitude(p).radians(), Longitude(p).radians()) { |
| // The latitude and longitude are already normalized. |
| DLOG_IF(ERROR, !is_valid()) << "Invalid S2LatLng in constructor: " << *this; |
| } |
| |
| S1Angle S2LatLng::GetDistance(S2LatLng const& o) const { |
| // This implements the Haversine formula, which is numerically stable for |
| // small distances but only gets about 8 digits of precision for very large |
| // distances (e.g. antipodal points). Note that 8 digits is still accurate |
| // to within about 10cm for a sphere the size of the Earth. |
| // |
| // This could be fixed with another sin() and cos() below, but at that point |
| // you might as well just convert both arguments to S2Points and compute the |
| // distance that way (which gives about 15 digits of accuracy for all |
| // distances). |
| |
| DLOG_IF(ERROR, !is_valid()) |
| << "Invalid S2LatLng in S2LatLng::GetDistance: " << *this; |
| |
| DLOG_IF(ERROR, !o.is_valid()) |
| << "Invalid S2LatLng in S2LatLng::GetDistance: " << o; |
| |
| double lat1 = lat().radians(); |
| double lat2 = o.lat().radians(); |
| double lng1 = lng().radians(); |
| double lng2 = o.lng().radians(); |
| double dlat = sin(0.5 * (lat2 - lat1)); |
| double dlng = sin(0.5 * (lng2 - lng1)); |
| double x = dlat * dlat + dlng * dlng * cos(lat1) * cos(lat2); |
| return S1Angle::Radians(2 * asin(sqrt(min(1.0, x)))); |
| } |
| |
| std::string S2LatLng::ToStringInDegrees() const { |
| S2LatLng pt = Normalized(); |
| return base::StringPrintf("%f,%f", pt.lat().degrees(), pt.lng().degrees()); |
| } |
| |
| void S2LatLng::ToStringInDegrees(std::string* s) const { |
| *s = ToStringInDegrees(); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, S2LatLng const& ll) { |
| return os << "[" << ll.lat() << ", " << ll.lng() << "]"; |
| } |