Chris@102: // Boost.Geometry Chris@102: Chris@102: // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. Chris@102: Chris@102: // This file was modified by Oracle on 2014. Chris@102: // Modifications copyright (c) 2014 Oracle and/or its affiliates. Chris@102: Chris@102: // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle Chris@102: Chris@102: // Use, modification and distribution is subject to the Boost Software License, Chris@102: // Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at Chris@102: // http://www.boost.org/LICENSE_1_0.txt) Chris@102: Chris@102: #ifndef BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP Chris@102: #define BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP Chris@102: Chris@102: Chris@102: #include Chris@102: Chris@102: #include Chris@102: #include Chris@102: Chris@102: #include Chris@102: Chris@102: #include Chris@102: Chris@102: Chris@102: #ifndef BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS Chris@102: #define BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS 1000 Chris@102: #endif Chris@102: Chris@102: Chris@102: namespace boost { namespace geometry { namespace detail Chris@102: { Chris@102: Chris@102: /*! Chris@102: \brief The solution of the inverse problem of geodesics on latlong coordinates, after Vincenty, 1975 Chris@102: \author See Chris@102: - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf Chris@102: - http://www.icsm.gov.au/gda/gdav2.3.pdf Chris@102: \author Adapted from various implementations to get it close to the original document Chris@102: - http://www.movable-type.co.uk/scripts/LatLongVincenty.html Chris@102: - http://exogen.case.edu/projects/geopy/source/geopy.distance.html Chris@102: - http://futureboy.homeip.net/fsp/colorize.fsp?fileName=navigation.frink Chris@102: Chris@102: */ Chris@102: template Chris@102: class vincenty_inverse Chris@102: { Chris@102: public: Chris@102: template Chris@102: vincenty_inverse(T1 const& lon1, Chris@102: T1 const& lat1, Chris@102: T2 const& lon2, Chris@102: T2 const& lat2, Chris@102: Spheroid const& spheroid) Chris@102: : is_result_zero(false) Chris@102: { Chris@102: if (math::equals(lat1, lat2) && math::equals(lon1, lon2)) Chris@102: { Chris@102: is_result_zero = true; Chris@102: return; Chris@102: } Chris@102: Chris@102: CT const c1 = 1; Chris@102: CT const c2 = 2; Chris@102: CT const c3 = 3; Chris@102: CT const c4 = 4; Chris@102: CT const c16 = 16; Chris@102: CT const c_e_12 = CT(1e-12); Chris@102: Chris@102: CT const pi = geometry::math::pi(); Chris@102: CT const two_pi = c2 * pi; Chris@102: Chris@102: // lambda: difference in longitude on an auxiliary sphere Chris@102: CT L = lon2 - lon1; Chris@102: CT lambda = L; Chris@102: Chris@102: if (L < -pi) L += two_pi; Chris@102: if (L > pi) L -= two_pi; Chris@102: Chris@102: radius_a = CT(get_radius<0>(spheroid)); Chris@102: radius_b = CT(get_radius<2>(spheroid)); Chris@102: CT const flattening = geometry::detail::flattening(spheroid); Chris@102: Chris@102: // U: reduced latitude, defined by tan U = (1-f) tan phi Chris@102: CT const one_min_f = c1 - flattening; Chris@102: CT const tan_U1 = one_min_f * tan(lat1); // above (1) Chris@102: CT const tan_U2 = one_min_f * tan(lat2); // above (1) Chris@102: Chris@102: // calculate sin U and cos U using trigonometric identities Chris@102: CT const temp_den_U1 = math::sqrt(c1 + math::sqr(tan_U1)); Chris@102: CT const temp_den_U2 = math::sqrt(c1 + math::sqr(tan_U2)); Chris@102: // cos = 1 / sqrt(1 + tan^2) Chris@102: cos_U1 = c1 / temp_den_U1; Chris@102: cos_U2 = c1 / temp_den_U2; Chris@102: // sin = tan / sqrt(1 + tan^2) Chris@102: sin_U1 = tan_U1 / temp_den_U1; Chris@102: sin_U2 = tan_U2 / temp_den_U2; Chris@102: Chris@102: // calculate sin U and cos U directly Chris@102: //CT const U1 = atan(tan_U1); Chris@102: //CT const U2 = atan(tan_U2); Chris@102: //cos_U1 = cos(U1); Chris@102: //cos_U2 = cos(U2); Chris@102: //sin_U1 = tan_U1 * cos_U1; // sin(U1); Chris@102: //sin_U2 = tan_U2 * cos_U2; // sin(U2); Chris@102: Chris@102: CT previous_lambda; Chris@102: Chris@102: int counter = 0; // robustness Chris@102: Chris@102: do Chris@102: { Chris@102: previous_lambda = lambda; // (13) Chris@102: sin_lambda = sin(lambda); Chris@102: cos_lambda = cos(lambda); Chris@102: sin_sigma = math::sqrt(math::sqr(cos_U2 * sin_lambda) + math::sqr(cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda)); // (14) Chris@102: CT cos_sigma = sin_U1 * sin_U2 + cos_U1 * cos_U2 * cos_lambda; // (15) Chris@102: sin_alpha = cos_U1 * cos_U2 * sin_lambda / sin_sigma; // (17) Chris@102: cos2_alpha = c1 - math::sqr(sin_alpha); Chris@102: cos2_sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) Chris@102: Chris@102: CT C = flattening/c16 * cos2_alpha * (c4 + flattening * (c4 - c3 * cos2_alpha)); // (10) Chris@102: sigma = atan2(sin_sigma, cos_sigma); // (16) Chris@102: lambda = L + (c1 - C) * flattening * sin_alpha * Chris@102: (sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * (-c1 + c2 * math::sqr(cos2_sigma_m)))); // (11) Chris@102: Chris@102: ++counter; // robustness Chris@102: Chris@102: } while ( geometry::math::abs(previous_lambda - lambda) > c_e_12 Chris@102: && geometry::math::abs(lambda) < pi Chris@102: && counter < BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS ); // robustness Chris@102: } Chris@102: Chris@102: inline CT distance() const Chris@102: { Chris@102: if ( is_result_zero ) Chris@102: { Chris@102: return CT(0); Chris@102: } Chris@102: Chris@102: // Oops getting hard here Chris@102: // (again, problem is that ttmath cannot divide by doubles, which is OK) Chris@102: CT const c1 = 1; Chris@102: CT const c2 = 2; Chris@102: CT const c3 = 3; Chris@102: CT const c4 = 4; Chris@102: CT const c6 = 6; Chris@102: CT const c47 = 47; Chris@102: CT const c74 = 74; Chris@102: CT const c128 = 128; Chris@102: CT const c256 = 256; Chris@102: CT const c175 = 175; Chris@102: CT const c320 = 320; Chris@102: CT const c768 = 768; Chris@102: CT const c1024 = 1024; Chris@102: CT const c4096 = 4096; Chris@102: CT const c16384 = 16384; Chris@102: Chris@102: //CT sqr_u = cos2_alpha * (math::sqr(radius_a) - math::sqr(radius_b)) / math::sqr(radius_b); // above (1) Chris@102: CT sqr_u = cos2_alpha * ( math::sqr(radius_a / radius_b) - c1 ); // above (1) Chris@102: Chris@102: CT A = c1 + sqr_u/c16384 * (c4096 + sqr_u * (-c768 + sqr_u * (c320 - c175 * sqr_u))); // (3) Chris@102: CT B = sqr_u/c1024 * (c256 + sqr_u * ( -c128 + sqr_u * (c74 - c47 * sqr_u))); // (4) Chris@102: CT delta_sigma = B * sin_sigma * ( cos2_sigma_m + (B/c4) * (cos(sigma)* (-c1 + c2 * cos2_sigma_m) Chris@102: - (B/c6) * cos2_sigma_m * (-c3 + c4 * math::sqr(sin_sigma)) * (-c3 + c4 * cos2_sigma_m))); // (6) Chris@102: Chris@102: return radius_b * A * (sigma - delta_sigma); // (19) Chris@102: } Chris@102: Chris@102: inline CT azimuth12() const Chris@102: { Chris@102: return is_result_zero ? Chris@102: CT(0) : Chris@102: atan2(cos_U2 * sin_lambda, cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda); // (20) Chris@102: } Chris@102: Chris@102: inline CT azimuth21() const Chris@102: { Chris@102: // NOTE: signs of X and Y are different than in the original paper Chris@102: return is_result_zero ? Chris@102: CT(0) : Chris@102: atan2(-cos_U1 * sin_lambda, sin_U1 * cos_U2 - cos_U1 * sin_U2 * cos_lambda); // (21) Chris@102: } Chris@102: Chris@102: private: Chris@102: // alpha: azimuth of the geodesic at the equator Chris@102: CT cos2_alpha; Chris@102: CT sin_alpha; Chris@102: Chris@102: // sigma: angular distance p1,p2 on the sphere Chris@102: // sigma1: angular distance on the sphere from the equator to p1 Chris@102: // sigma_m: angular distance on the sphere from the equator to the midpoint of the line Chris@102: CT sigma; Chris@102: CT sin_sigma; Chris@102: CT cos2_sigma_m; Chris@102: Chris@102: CT sin_lambda; Chris@102: CT cos_lambda; Chris@102: Chris@102: // set only once Chris@102: CT cos_U1; Chris@102: CT cos_U2; Chris@102: CT sin_U1; Chris@102: CT sin_U2; Chris@102: Chris@102: // set only once Chris@102: CT radius_a; Chris@102: CT radius_b; Chris@102: Chris@102: bool is_result_zero; Chris@102: }; Chris@102: Chris@102: }}} // namespace boost::geometry::detail Chris@102: Chris@102: Chris@102: #endif // BOOST_GEOMETRY_ALGORITHMS_DETAIL_VINCENTY_INVERSE_HPP