diff options
Diffstat (limited to 'boost/geometry/formulas')
-rw-r--r-- | boost/geometry/formulas/andoyer_inverse.hpp | 27 | ||||
-rw-r--r-- | boost/geometry/formulas/area_formulas.hpp | 578 | ||||
-rw-r--r-- | boost/geometry/formulas/differential_quantities.hpp | 19 | ||||
-rw-r--r-- | boost/geometry/formulas/eccentricity_sqr.hpp | 70 | ||||
-rw-r--r-- | boost/geometry/formulas/flattening.hpp | 70 | ||||
-rw-r--r-- | boost/geometry/formulas/geographic.hpp | 457 | ||||
-rw-r--r-- | boost/geometry/formulas/gnomonic_spheroid.hpp | 3 | ||||
-rw-r--r-- | boost/geometry/formulas/sjoberg_intersection.hpp | 1310 | ||||
-rw-r--r-- | boost/geometry/formulas/spherical.hpp | 152 | ||||
-rw-r--r-- | boost/geometry/formulas/thomas_direct.hpp | 5 | ||||
-rw-r--r-- | boost/geometry/formulas/thomas_inverse.hpp | 5 | ||||
-rw-r--r-- | boost/geometry/formulas/vertex_latitude.hpp | 148 | ||||
-rw-r--r-- | boost/geometry/formulas/vincenty_direct.hpp | 5 | ||||
-rw-r--r-- | boost/geometry/formulas/vincenty_inverse.hpp | 38 |
14 files changed, 2594 insertions, 293 deletions
diff --git a/boost/geometry/formulas/andoyer_inverse.hpp b/boost/geometry/formulas/andoyer_inverse.hpp index 57b5ab5384..902fd7d8f6 100644 --- a/boost/geometry/formulas/andoyer_inverse.hpp +++ b/boost/geometry/formulas/andoyer_inverse.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2015-2016 Oracle and/or its affiliates. +// Copyright (c) 2015-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -20,9 +20,8 @@ #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> - #include <boost/geometry/formulas/differential_quantities.hpp> +#include <boost/geometry/formulas/flattening.hpp> #include <boost/geometry/formulas/result_inverse.hpp> @@ -75,7 +74,7 @@ public: CT const c0 = CT(0); CT const c1 = CT(1); CT const pi = math::pi<CT>(); - CT const f = detail::flattening<CT>(spheroid); + CT const f = formula::flattening<CT>(spheroid); CT const dlon = lon2 - lon1; CT const sin_dlon = sin(dlon); @@ -97,7 +96,7 @@ public: CT const d = acos(cos_d); // [0, pi] CT const sin_d = sin(d); // [-1, 1] - + if ( BOOST_GEOMETRY_CONDITION(EnableDistance) ) { CT const K = math::sqr(sin_lat1-sin_lat2); @@ -138,7 +137,14 @@ public: CT A = c0; CT U = c0; - if ( ! math::equals(cos_lat2, c0) ) + if (math::equals(cos_lat2, c0)) + { + if (sin_lat2 < c0) + { + A = pi; + } + } + else { CT const tan_lat2 = sin_lat2/cos_lat2; CT const M = cos_lat1*tan_lat2-sin_lat1*cos_dlon; @@ -149,7 +155,14 @@ public: CT B = c0; CT V = c0; - if ( ! math::equals(cos_lat1, c0) ) + if (math::equals(cos_lat1, c0)) + { + if (sin_lat1 < c0) + { + B = pi; + } + } + else { CT const tan_lat1 = sin_lat1/cos_lat1; CT const N = cos_lat2*tan_lat1-sin_lat2*cos_dlon; diff --git a/boost/geometry/formulas/area_formulas.hpp b/boost/geometry/formulas/area_formulas.hpp new file mode 100644 index 0000000000..6a0b525e25 --- /dev/null +++ b/boost/geometry/formulas/area_formulas.hpp @@ -0,0 +1,578 @@ +// Boost.Geometry + +// Copyright (c) 2015-2016 Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP +#define BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP + +#include <boost/geometry/formulas/flattening.hpp> +#include <boost/math/special_functions/hypot.hpp> + +namespace boost { namespace geometry { namespace formula +{ + +/*! +\brief Formulas for computing spherical and ellipsoidal polygon area. + The current class computes the area of the trapezoid defined by a segment + the two meridians passing by the endpoints and the equator. +\author See +- Danielsen JS, The area under the geodesic. Surv Rev 30(232): +61–66, 1989 +- Charles F.F Karney, Algorithms for geodesics, 2011 +https://arxiv.org/pdf/1109.4448.pdf +*/ + +template < + typename CT, + std::size_t SeriesOrder = 2, + bool ExpandEpsN = true +> +class area_formulas +{ + +public: + + //TODO: move the following to a more general space to be used by other + // classes as well + /* + Evaluate the polynomial in x using Horner's method. + */ + template <typename NT, typename IteratorType> + static inline NT horner_evaluate(NT x, + IteratorType begin, + IteratorType end) + { + NT result(0); + IteratorType it = end; + do + { + result = result * x + *--it; + } + while (it != begin); + return result; + } + + /* + Clenshaw algorithm for summing trigonometric series + https://en.wikipedia.org/wiki/Clenshaw_algorithm + */ + template <typename NT, typename IteratorType> + static inline NT clenshaw_sum(NT cosx, + IteratorType begin, + IteratorType end) + { + IteratorType it = end; + bool odd = true; + CT b_k, b_k1(0), b_k2(0); + do + { + CT c_k = odd ? *--it : NT(0); + b_k = c_k + NT(2) * cosx * b_k1 - b_k2; + b_k2 = b_k1; + b_k1 = b_k; + odd = !odd; + } + while (it != begin); + + return *begin + b_k1 * cosx - b_k2; + } + + template<typename T> + static inline void normalize(T& x, T& y) + { + T h = boost::math::hypot(x, y); + x /= h; + y /= h; + } + + /* + Generate and evaluate the series expansion of the following integral + + I4 = -integrate( (t(ep2) - t(k2*sin(sigma1)^2)) / (ep2 - k2*sin(sigma1)^2) + * sin(sigma1)/2, sigma1, pi/2, sigma ) + where + + t(x) = sqrt(1+1/x)*asinh(sqrt(x)) + x + + valid for ep2 and k2 small. We substitute k2 = 4 * eps / (1 - eps)^2 + and ep2 = 4 * n / (1 - n)^2 and expand in eps and n. + + The resulting sum of the series is of the form + + sum(C4[l] * cos((2*l+1)*sigma), l, 0, maxpow-1) ) + + The above expansion is performed in Computer Algebra System Maxima. + The C++ code (that yields the function evaluate_coeffs_n below) is generated + by the following Maxima script and is based on script: + http://geographiclib.sourceforge.net/html/geod.mac + + // Maxima script begin + taylordepth:5$ + ataylor(expr,var,ord):=expand(ratdisrep(taylor(expr,var,0,ord)))$ + jtaylor(expr,var1,var2,ord):=block([zz],expand(subst([zz=1], + ratdisrep(taylor(subst([var1=zz*var1,var2=zz*var2],expr),zz,0,ord)))))$ + + compute(maxpow):=block([int,t,intexp,area, x,ep2,k2], + maxpow:maxpow-1, + t : sqrt(1+1/x) * asinh(sqrt(x)) + x, + int:-(tf(ep2) - tf(k2*sin(sigma)^2)) / (ep2 - k2*sin(sigma)^2) + * sin(sigma)/2, + int:subst([tf(ep2)=subst([x=ep2],t), + tf(k2*sin(sigma)^2)=subst([x=k2*sin(sigma)^2],t)], + int), + int:subst([abs(sin(sigma))=sin(sigma)],int), + int:subst([k2=4*eps/(1-eps)^2,ep2=4*n/(1-n)^2],int), + intexp:jtaylor(int,n,eps,maxpow), + area:trigreduce(integrate(intexp,sigma)), + area:expand(area-subst(sigma=%pi/2,area)), + for i:0 thru maxpow do C4[i]:coeff(area,cos((2*i+1)*sigma)), + if expand(area-sum(C4[i]*cos((2*i+1)*sigma),i,0,maxpow)) # 0 + then error("left over terms in I4"), + 'done)$ + + printcode(maxpow):= + block([tab2:" ",tab3:" "], + print(" switch (SeriesOrder) {"), + for nn:1 thru maxpow do block([c], + print(concat(tab2,"case ",string(nn-1),":")), + c:0, + for m:0 thru nn-1 do block( + [q:jtaylor(subst([n=n],C4[m]),n,eps,nn-1), + linel:1200], + for j:m thru nn-1 do ( + print(concat(tab3,"coeffs_n[",c,"] = ", + string(horner(coeff(q,eps,j))),";")), + c:c+1) + ), + print(concat(tab3,"break;"))), + print(" }"), + 'done)$ + + maxpow:6$ + compute(maxpow)$ + printcode(maxpow)$ + // Maxima script end + + In the resulting code we should replace each number x by CT(x) + e.g. using the following scirpt: + sed -e 's/[0-9]\+/CT(&)/g; s/\[CT(/\[/g; s/)\]/\]/g; + s/case\sCT(/case /g; s/):/:/g' + */ + + static inline void evaluate_coeffs_n(CT n, CT coeffs_n[]) + { + + switch (SeriesOrder) { + case 0: + coeffs_n[0] = CT(2)/CT(3); + break; + case 1: + coeffs_n[0] = (CT(10)-CT(4)*n)/CT(15); + coeffs_n[1] = -CT(1)/CT(5); + coeffs_n[2] = CT(1)/CT(45); + break; + case 2: + coeffs_n[0] = (n*(CT(8)*n-CT(28))+CT(70))/CT(105); + coeffs_n[1] = (CT(16)*n-CT(7))/CT(35); + coeffs_n[2] = -CT(2)/CT(105); + coeffs_n[3] = (CT(7)-CT(16)*n)/CT(315); + coeffs_n[4] = -CT(2)/CT(105); + coeffs_n[5] = CT(4)/CT(525); + break; + case 3: + coeffs_n[0] = (n*(n*(CT(4)*n+CT(24))-CT(84))+CT(210))/CT(315); + coeffs_n[1] = ((CT(48)-CT(32)*n)*n-CT(21))/CT(105); + coeffs_n[2] = (-CT(32)*n-CT(6))/CT(315); + coeffs_n[3] = CT(11)/CT(315); + coeffs_n[4] = (n*(CT(32)*n-CT(48))+CT(21))/CT(945); + coeffs_n[5] = (CT(64)*n-CT(18))/CT(945); + coeffs_n[6] = -CT(1)/CT(105); + coeffs_n[7] = (CT(12)-CT(32)*n)/CT(1575); + coeffs_n[8] = -CT(8)/CT(1575); + coeffs_n[9] = CT(8)/CT(2205); + break; + case 4: + coeffs_n[0] = (n*(n*(n*(CT(16)*n+CT(44))+CT(264))-CT(924))+CT(2310))/CT(3465); + coeffs_n[1] = (n*(n*(CT(48)*n-CT(352))+CT(528))-CT(231))/CT(1155); + coeffs_n[2] = (n*(CT(1088)*n-CT(352))-CT(66))/CT(3465); + coeffs_n[3] = (CT(121)-CT(368)*n)/CT(3465); + coeffs_n[4] = CT(4)/CT(1155); + coeffs_n[5] = (n*((CT(352)-CT(48)*n)*n-CT(528))+CT(231))/CT(10395); + coeffs_n[6] = ((CT(704)-CT(896)*n)*n-CT(198))/CT(10395); + coeffs_n[7] = (CT(80)*n-CT(99))/CT(10395); + coeffs_n[8] = CT(4)/CT(1155); + coeffs_n[9] = (n*(CT(320)*n-CT(352))+CT(132))/CT(17325); + coeffs_n[10] = (CT(384)*n-CT(88))/CT(17325); + coeffs_n[11] = -CT(8)/CT(1925); + coeffs_n[12] = (CT(88)-CT(256)*n)/CT(24255); + coeffs_n[13] = -CT(16)/CT(8085); + coeffs_n[14] = CT(64)/CT(31185); + break; + case 5: + coeffs_n[0] = (n*(n*(n*(n*(CT(100)*n+CT(208))+CT(572))+CT(3432))-CT(12012))+CT(30030)) + /CT(45045); + coeffs_n[1] = (n*(n*(n*(CT(64)*n+CT(624))-CT(4576))+CT(6864))-CT(3003))/CT(15015); + coeffs_n[2] = (n*((CT(14144)-CT(10656)*n)*n-CT(4576))-CT(858))/CT(45045); + coeffs_n[3] = ((-CT(224)*n-CT(4784))*n+CT(1573))/CT(45045); + coeffs_n[4] = (CT(1088)*n+CT(156))/CT(45045); + coeffs_n[5] = CT(97)/CT(15015); + coeffs_n[6] = (n*(n*((-CT(64)*n-CT(624))*n+CT(4576))-CT(6864))+CT(3003))/CT(135135); + coeffs_n[7] = (n*(n*(CT(5952)*n-CT(11648))+CT(9152))-CT(2574))/CT(135135); + coeffs_n[8] = (n*(CT(5792)*n+CT(1040))-CT(1287))/CT(135135); + coeffs_n[9] = (CT(468)-CT(2944)*n)/CT(135135); + coeffs_n[10] = CT(1)/CT(9009); + coeffs_n[11] = (n*((CT(4160)-CT(1440)*n)*n-CT(4576))+CT(1716))/CT(225225); + coeffs_n[12] = ((CT(4992)-CT(8448)*n)*n-CT(1144))/CT(225225); + coeffs_n[13] = (CT(1856)*n-CT(936))/CT(225225); + coeffs_n[14] = CT(8)/CT(10725); + coeffs_n[15] = (n*(CT(3584)*n-CT(3328))+CT(1144))/CT(315315); + coeffs_n[16] = (CT(1024)*n-CT(208))/CT(105105); + coeffs_n[17] = -CT(136)/CT(63063); + coeffs_n[18] = (CT(832)-CT(2560)*n)/CT(405405); + coeffs_n[19] = -CT(128)/CT(135135); + coeffs_n[20] = CT(128)/CT(99099); + break; + } + } + + /* + Expand in k2 and ep2. + */ + static inline void evaluate_coeffs_ep(CT ep, CT coeffs_n[]) + { + switch (SeriesOrder) { + case 0: + coeffs_n[0] = CT(2)/CT(3); + break; + case 1: + coeffs_n[0] = (CT(10)-ep)/CT(15); + coeffs_n[1] = -CT(1)/CT(20); + coeffs_n[2] = CT(1)/CT(180); + break; + case 2: + coeffs_n[0] = (ep*(CT(4)*ep-CT(7))+CT(70))/CT(105); + coeffs_n[1] = (CT(4)*ep-CT(7))/CT(140); + coeffs_n[2] = CT(1)/CT(42); + coeffs_n[3] = (CT(7)-CT(4)*ep)/CT(1260); + coeffs_n[4] = -CT(1)/CT(252); + coeffs_n[5] = CT(1)/CT(2100); + break; + case 3: + coeffs_n[0] = (ep*((CT(12)-CT(8)*ep)*ep-CT(21))+CT(210))/CT(315); + coeffs_n[1] = ((CT(12)-CT(8)*ep)*ep-CT(21))/CT(420); + coeffs_n[2] = (CT(3)-CT(2)*ep)/CT(126); + coeffs_n[3] = -CT(1)/CT(72); + coeffs_n[4] = (ep*(CT(8)*ep-CT(12))+CT(21))/CT(3780); + coeffs_n[5] = (CT(2)*ep-CT(3))/CT(756); + coeffs_n[6] = CT(1)/CT(360); + coeffs_n[7] = (CT(3)-CT(2)*ep)/CT(6300); + coeffs_n[8] = -CT(1)/CT(1800); + coeffs_n[9] = CT(1)/CT(17640); + break; + case 4: + coeffs_n[0] = (ep*(ep*(ep*(CT(64)*ep-CT(88))+CT(132))-CT(231))+CT(2310))/CT(3465); + coeffs_n[1] = (ep*(ep*(CT(64)*ep-CT(88))+CT(132))-CT(231))/CT(4620); + coeffs_n[2] = (ep*(CT(16)*ep-CT(22))+CT(33))/CT(1386); + coeffs_n[3] = (CT(8)*ep-CT(11))/CT(792); + coeffs_n[4] = CT(1)/CT(110); + coeffs_n[5] = (ep*((CT(88)-CT(64)*ep)*ep-CT(132))+CT(231))/CT(41580); + coeffs_n[6] = ((CT(22)-CT(16)*ep)*ep-CT(33))/CT(8316); + coeffs_n[7] = (CT(11)-CT(8)*ep)/CT(3960); + coeffs_n[8] = -CT(1)/CT(495); + coeffs_n[9] = (ep*(CT(16)*ep-CT(22))+CT(33))/CT(69300); + coeffs_n[10] = (CT(8)*ep-CT(11))/CT(19800); + coeffs_n[11] = CT(1)/CT(1925); + coeffs_n[12] = (CT(11)-CT(8)*ep)/CT(194040); + coeffs_n[13] = -CT(1)/CT(10780); + coeffs_n[14] = CT(1)/CT(124740); + break; + case 5: + coeffs_n[0] = (ep*(ep*(ep*((CT(832)-CT(640)*ep)*ep-CT(1144))+CT(1716))-CT(3003))+CT(30030))/CT(45045); + coeffs_n[1] = (ep*(ep*((CT(832)-CT(640)*ep)*ep-CT(1144))+CT(1716))-CT(3003))/CT(60060); + coeffs_n[2] = (ep*((CT(208)-CT(160)*ep)*ep-CT(286))+CT(429))/CT(18018); + coeffs_n[3] = ((CT(104)-CT(80)*ep)*ep-CT(143))/CT(10296); + coeffs_n[4] = (CT(13)-CT(10)*ep)/CT(1430); + coeffs_n[5] = -CT(1)/CT(156); + coeffs_n[6] = (ep*(ep*(ep*(CT(640)*ep-CT(832))+CT(1144))-CT(1716))+CT(3003))/CT(540540); + coeffs_n[7] = (ep*(ep*(CT(160)*ep-CT(208))+CT(286))-CT(429))/CT(108108); + coeffs_n[8] = (ep*(CT(80)*ep-CT(104))+CT(143))/CT(51480); + coeffs_n[9] = (CT(10)*ep-CT(13))/CT(6435); + coeffs_n[10] = CT(5)/CT(3276); + coeffs_n[11] = (ep*((CT(208)-CT(160)*ep)*ep-CT(286))+CT(429))/CT(900900); + coeffs_n[12] = ((CT(104)-CT(80)*ep)*ep-CT(143))/CT(257400); + coeffs_n[13] = (CT(13)-CT(10)*ep)/CT(25025); + coeffs_n[14] = -CT(1)/CT(2184); + coeffs_n[15] = (ep*(CT(80)*ep-CT(104))+CT(143))/CT(2522520); + coeffs_n[16] = (CT(10)*ep-CT(13))/CT(140140); + coeffs_n[17] = CT(5)/CT(45864); + coeffs_n[18] = (CT(13)-CT(10)*ep)/CT(1621620); + coeffs_n[19] = -CT(1)/CT(58968); + coeffs_n[20] = CT(1)/CT(792792); + break; + } + } + + /* + Given the set of coefficients coeffs1[] evaluate on var2 and return + the set of coefficients coeffs2[] + */ + static inline void evaluate_coeffs_var2(CT var2, + CT coeffs1[], + CT coeffs2[]){ + std::size_t begin(0), end(0); + for(std::size_t i = 0; i <= SeriesOrder; i++){ + end = begin + SeriesOrder + 1 - i; + coeffs2[i] = ((i==0) ? CT(1) : pow(var2,int(i))) + * horner_evaluate(var2, coeffs1 + begin, coeffs1 + end); + begin = end; + } + } + + /* + Compute the spherical excess of a geodesic (or shperical) segment + */ + template < + bool LongSegment, + typename PointOfSegment + > + static inline CT spherical(PointOfSegment const& p1, + PointOfSegment const& p2) + { + CT excess; + + if(LongSegment) // not for segments parallel to equator + { + CT cbet1 = cos(geometry::get_as_radian<1>(p1)); + CT sbet1 = sin(geometry::get_as_radian<1>(p1)); + CT cbet2 = cos(geometry::get_as_radian<1>(p2)); + CT sbet2 = sin(geometry::get_as_radian<1>(p2)); + + CT omg12 = geometry::get_as_radian<0>(p1) + - geometry::get_as_radian<0>(p2); + CT comg12 = cos(omg12); + CT somg12 = sin(omg12); + + CT alp1 = atan2(cbet1 * sbet2 + - sbet1 * cbet2 * comg12, + cbet2 * somg12); + + CT alp2 = atan2(cbet1 * sbet2 * comg12 + - sbet1 * cbet2, + cbet1 * somg12); + + excess = alp2 - alp1; + + } else { + + // Trapezoidal formula + + CT tan_lat1 = + tan(geometry::get_as_radian<1>(p1) / 2.0); + CT tan_lat2 = + tan(geometry::get_as_radian<1>(p2) / 2.0); + + excess = CT(2.0) + * atan(((tan_lat1 + tan_lat2) / (CT(1) + tan_lat1 * tan_lat2)) + * tan((geometry::get_as_radian<0>(p2) + - geometry::get_as_radian<0>(p1)) / 2)); + } + + return excess; + } + + struct return_type_ellipsoidal + { + return_type_ellipsoidal() + : spherical_term(0), + ellipsoidal_term(0) + {} + + CT spherical_term; + CT ellipsoidal_term; + }; + + /* + Compute the ellipsoidal correction of a geodesic (or shperical) segment + */ + template < + template <typename, bool, bool, bool, bool, bool> class Inverse, + //typename AzimuthStrategy, + typename PointOfSegment, + typename SpheroidConst + > + static inline return_type_ellipsoidal ellipsoidal(PointOfSegment const& p1, + PointOfSegment const& p2, + SpheroidConst spheroid_const) + { + return_type_ellipsoidal result; + + // Azimuth Approximation + + typedef Inverse<CT, false, true, true, false, false> inverse_type; + typedef typename inverse_type::result_type inverse_result; + + inverse_result i_res = inverse_type::apply(get_as_radian<0>(p1), + get_as_radian<1>(p1), + get_as_radian<0>(p2), + get_as_radian<1>(p2), + spheroid_const.m_spheroid); + + CT alp1 = i_res.azimuth; + CT alp2 = i_res.reverse_azimuth; + + // Constants + + CT const ep = spheroid_const.m_ep; + CT const f = formula::flattening<CT>(spheroid_const.m_spheroid); + CT const one_minus_f = CT(1) - f; + std::size_t const series_order_plus_one = SeriesOrder + 1; + std::size_t const series_order_plus_two = SeriesOrder + 2; + + // Basic trigonometric computations + + CT tan_bet1 = tan(get_as_radian<1>(p1)) * one_minus_f; + CT tan_bet2 = tan(get_as_radian<1>(p2)) * one_minus_f; + CT cos_bet1 = cos(atan(tan_bet1)); + CT cos_bet2 = cos(atan(tan_bet2)); + CT sin_bet1 = tan_bet1 * cos_bet1; + CT sin_bet2 = tan_bet2 * cos_bet2; + CT sin_alp1 = sin(alp1); + CT cos_alp1 = cos(alp1); + CT cos_alp2 = cos(alp2); + CT sin_alp0 = sin_alp1 * cos_bet1; + + // Spherical term computation + + CT sin_omg1 = sin_alp0 * sin_bet1; + CT cos_omg1 = cos_alp1 * cos_bet1; + CT sin_omg2 = sin_alp0 * sin_bet2; + CT cos_omg2 = cos_alp2 * cos_bet2; + CT cos_omg12 = cos_omg1 * cos_omg2 + sin_omg1 * sin_omg2; + CT excess; + + bool meridian = get<0>(p2) - get<0>(p1) == CT(0) + || get<1>(p1) == CT(90) || get<1>(p1) == -CT(90) + || get<1>(p2) == CT(90) || get<1>(p2) == -CT(90); + + if (!meridian && cos_omg12 > -CT(0.7) + && sin_bet2 - sin_bet1 < CT(1.75)) // short segment + { + CT sin_omg12 = cos_omg1 * sin_omg2 - sin_omg1 * cos_omg2; + normalize(sin_omg12, cos_omg12); + + CT cos_omg12p1 = CT(1) + cos_omg12; + CT cos_bet1p1 = CT(1) + cos_bet1; + CT cos_bet2p1 = CT(1) + cos_bet2; + excess = CT(2) * atan2(sin_omg12 * (sin_bet1 * cos_bet2p1 + sin_bet2 * cos_bet1p1), + cos_omg12p1 * (sin_bet1 * sin_bet2 + cos_bet1p1 * cos_bet2p1)); + } + else + { + /* + CT sin_alp2 = sin(alp2); + CT sin_alp12 = sin_alp2 * cos_alp1 - cos_alp2 * sin_alp1; + CT cos_alp12 = cos_alp2 * cos_alp1 + sin_alp2 * sin_alp1; + excess = atan2(sin_alp12, cos_alp12); + */ + excess = alp2 - alp1; + } + + result.spherical_term = excess; + + // Ellipsoidal term computation (uses integral approximation) + + CT cos_alp0 = math::sqrt(CT(1) - math::sqr(sin_alp0)); + CT cos_sig1 = cos_alp1 * cos_bet1; + CT cos_sig2 = cos_alp2 * cos_bet2; + CT sin_sig1 = sin_bet1; + CT sin_sig2 = sin_bet2; + + normalize(sin_sig1, cos_sig1); + normalize(sin_sig2, cos_sig2); + + CT coeffs[SeriesOrder + 1]; + const std::size_t coeffs_var_size = (series_order_plus_two + * series_order_plus_one) / 2; + CT coeffs_var[coeffs_var_size]; + + if(ExpandEpsN){ // expand by eps and n + + CT k2 = math::sqr(ep * cos_alp0); + CT sqrt_k2_plus_one = math::sqrt(CT(1) + k2); + CT eps = (sqrt_k2_plus_one - CT(1)) / (sqrt_k2_plus_one + CT(1)); + CT n = f / (CT(2) - f); + + // Generate and evaluate the polynomials on n + // to get the series coefficients (that depend on eps) + evaluate_coeffs_n(n, coeffs_var); + + // Generate and evaluate the polynomials on eps (i.e. var2 = eps) + // to get the final series coefficients + evaluate_coeffs_var2(eps, coeffs_var, coeffs); + + }else{ // expand by k2 and ep + + CT k2 = math::sqr(ep * cos_alp0); + CT ep2 = math::sqr(ep); + + // Generate and evaluate the polynomials on ep2 + evaluate_coeffs_ep(ep2, coeffs_var); + + // Generate and evaluate the polynomials on k2 (i.e. var2 = k2) + evaluate_coeffs_var2(k2, coeffs_var, coeffs); + + } + + // Evaluate the trigonometric sum + CT I12 = clenshaw_sum(cos_sig2, coeffs, coeffs + series_order_plus_one) + - clenshaw_sum(cos_sig1, coeffs, coeffs + series_order_plus_one); + + // The part of the ellipsodal correction that depends on + // point coordinates + result.ellipsoidal_term = cos_alp0 * sin_alp0 * I12; + + return result; + } + + // Keep track whenever a segment crosses the prime meridian + // First normalize to [0,360) + template <typename PointOfSegment, typename StateType> + static inline int crosses_prime_meridian(PointOfSegment const& p1, + PointOfSegment const& p2, + StateType& state) + { + CT const pi + = geometry::math::pi<CT>(); + CT const two_pi + = geometry::math::two_pi<CT>(); + + CT p1_lon = get_as_radian<0>(p1) + - ( floor( get_as_radian<0>(p1) / two_pi ) + * two_pi ); + CT p2_lon = get_as_radian<0>(p2) + - ( floor( get_as_radian<0>(p2) / two_pi ) + * two_pi ); + + CT max_lon = (std::max)(p1_lon, p2_lon); + CT min_lon = (std::min)(p1_lon, p2_lon); + + if(max_lon > pi && min_lon < pi && max_lon - min_lon > pi) + { + state.m_crosses_prime_meridian++; + } + + return state.m_crosses_prime_meridian; + } + +}; + +}}} // namespace boost::geometry::formula + + +#endif // BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP diff --git a/boost/geometry/formulas/differential_quantities.hpp b/boost/geometry/formulas/differential_quantities.hpp index 9a92f14e18..ff2ec539db 100644 --- a/boost/geometry/formulas/differential_quantities.hpp +++ b/boost/geometry/formulas/differential_quantities.hpp @@ -1,6 +1,6 @@ // Boost.Geometry -// Copyright (c) 2016 Oracle and/or its affiliates. +// Copyright (c) 2016-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -64,8 +64,8 @@ public: CT const c1 = 1; CT const one_minus_f = c1 - f; - CT const sin_bet1 = one_minus_f * sin_lat1; - CT const sin_bet2 = one_minus_f * sin_lat2; + CT sin_bet1 = one_minus_f * sin_lat1; + CT sin_bet2 = one_minus_f * sin_lat2; // equator if (math::equals(sin_bet1, c0) && math::equals(sin_bet2, c0)) @@ -89,14 +89,17 @@ public: CT const e2 = f * (c2 - f); CT const ep2 = e2 / math::sqr(one_minus_f); - CT const cos_bet1 = cos_lat1; - CT const cos_bet2 = cos_lat2; - CT const sin_alp1 = sin(azimuth); CT const cos_alp1 = cos(azimuth); //CT const sin_alp2 = sin(reverse_azimuth); CT const cos_alp2 = cos(reverse_azimuth); + CT cos_bet1 = cos_lat1; + CT cos_bet2 = cos_lat2; + + normalize(sin_bet1, cos_bet1); + normalize(sin_bet2, cos_bet2); + CT sin_sig1 = sin_bet1; CT cos_sig1 = cos_alp1 * cos_bet1; CT sin_sig2 = sin_bet2; @@ -112,8 +115,8 @@ public: J12_f(sin_sig1, cos_sig1, sin_sig2, cos_sig2, cos_alp0_sqr, f) : J12_ep_sqr(sin_sig1, cos_sig1, sin_sig2, cos_sig2, cos_alp0_sqr, ep2) ; - CT const dn1 = math::sqrt(c1 + e2 * math::sqr(sin_lat1)); - CT const dn2 = math::sqrt(c1 + e2 * math::sqr(sin_lat2)); + CT const dn1 = math::sqrt(c1 + ep2 * math::sqr(sin_bet1)); + CT const dn2 = math::sqrt(c1 + ep2 * math::sqr(sin_bet2)); if (BOOST_GEOMETRY_CONDITION(EnableReducedLength)) { diff --git a/boost/geometry/formulas/eccentricity_sqr.hpp b/boost/geometry/formulas/eccentricity_sqr.hpp new file mode 100644 index 0000000000..01a9beacb9 --- /dev/null +++ b/boost/geometry/formulas/eccentricity_sqr.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry + +// Copyright (c) 2016 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP +#define BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP + +#include <boost/geometry/core/radius.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template <typename ResultType, typename Geometry, typename Tag = typename tag<Geometry>::type> +struct eccentricity_sqr + : not_implemented<Tag> +{}; + +template <typename ResultType, typename Geometry> +struct eccentricity_sqr<ResultType, Geometry, srs_sphere_tag> +{ + static inline ResultType apply(Geometry const& /*geometry*/) + { + return ResultType(0); + } +}; + +template <typename ResultType, typename Geometry> +struct eccentricity_sqr<ResultType, Geometry, srs_spheroid_tag> +{ + static inline ResultType apply(Geometry const& geometry) + { + // 1 - (b / a)^2 + return ResultType(1) - math::sqr(ResultType(get_radius<2>(geometry)) + / ResultType(get_radius<0>(geometry))); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template <typename ResultType, typename Geometry> +ResultType eccentricity_sqr(Geometry const& geometry) +{ + return formula_dispatch::eccentricity_sqr<ResultType, Geometry>::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_ECCENCRICITY_SQR_HPP diff --git a/boost/geometry/formulas/flattening.hpp b/boost/geometry/formulas/flattening.hpp new file mode 100644 index 0000000000..f94ead65b0 --- /dev/null +++ b/boost/geometry/formulas/flattening.hpp @@ -0,0 +1,70 @@ +// Boost.Geometry + +// Copyright (c) 2014-2016 Oracle and/or its affiliates. + +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP +#define BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP + +#include <boost/geometry/core/radius.hpp> +#include <boost/geometry/core/tag.hpp> +#include <boost/geometry/core/tags.hpp> + +#include <boost/geometry/algorithms/not_implemented.hpp> + +namespace boost { namespace geometry +{ + +#ifndef DOXYGEN_NO_DISPATCH +namespace formula_dispatch +{ + +template <typename ResultType, typename Geometry, typename Tag = typename tag<Geometry>::type> +struct flattening + : not_implemented<Tag> +{}; + +template <typename ResultType, typename Geometry> +struct flattening<ResultType, Geometry, srs_sphere_tag> +{ + static inline ResultType apply(Geometry const& /*geometry*/) + { + return ResultType(0); + } +}; + +template <typename ResultType, typename Geometry> +struct flattening<ResultType, Geometry, srs_spheroid_tag> +{ + static inline ResultType apply(Geometry const& geometry) + { + // (a - b) / a + return ResultType(get_radius<0>(geometry) - get_radius<2>(geometry)) + / ResultType(get_radius<0>(geometry)); + } +}; + +} // namespace formula_dispatch +#endif // DOXYGEN_NO_DISPATCH + +#ifndef DOXYGEN_NO_DETAIL +namespace formula +{ + +template <typename ResultType, typename Geometry> +ResultType flattening(Geometry const& geometry) +{ + return formula_dispatch::flattening<ResultType, Geometry>::apply(geometry); +} + +} // namespace formula +#endif // DOXYGEN_NO_DETAIL + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_FLATTENING_HPP diff --git a/boost/geometry/formulas/geographic.hpp b/boost/geometry/formulas/geographic.hpp new file mode 100644 index 0000000000..f6feb66633 --- /dev/null +++ b/boost/geometry/formulas/geographic.hpp @@ -0,0 +1,457 @@ +// Boost.Geometry + +// Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP +#define BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP + +#include <boost/geometry/core/coordinate_system.hpp> +#include <boost/geometry/core/coordinate_type.hpp> +#include <boost/geometry/core/access.hpp> +#include <boost/geometry/core/radian_access.hpp> + +#include <boost/geometry/arithmetic/arithmetic.hpp> +#include <boost/geometry/arithmetic/cross_product.hpp> +#include <boost/geometry/arithmetic/dot_product.hpp> +#include <boost/geometry/arithmetic/normalize.hpp> + +#include <boost/geometry/formulas/eccentricity_sqr.hpp> +#include <boost/geometry/formulas/flattening.hpp> + +#include <boost/geometry/util/math.hpp> +#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp> +#include <boost/geometry/util/select_coordinate_type.hpp> + +namespace boost { namespace geometry { + +namespace formula { + +template <typename Point3d, typename PointGeo, typename Spheroid> +inline Point3d geo_to_cart3d(PointGeo const& point_geo, Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type calc_t; + + calc_t const c1 = 1; + calc_t const e_sqr = eccentricity_sqr<calc_t>(spheroid); + + calc_t const lon = get_as_radian<0>(point_geo); + calc_t const lat = get_as_radian<1>(point_geo); + + Point3d res; + + calc_t const sin_lat = sin(lat); + + // "unit" spheroid, a = 1 + calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); + calc_t const N_cos_lat = N * cos(lat); + + set<0>(res, N_cos_lat * cos(lon)); + set<1>(res, N_cos_lat * sin(lon)); + set<2>(res, N * (c1 - e_sqr) * sin_lat); + + return res; +} + +template <typename PointGeo, typename Spheroid, typename Point3d> +inline void geo_to_cart3d(PointGeo const& point_geo, Point3d & result, Point3d & north, Point3d & east, Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type calc_t; + + calc_t const c1 = 1; + calc_t const e_sqr = eccentricity_sqr<calc_t>(spheroid); + + calc_t const lon = get_as_radian<0>(point_geo); + calc_t const lat = get_as_radian<1>(point_geo); + + calc_t const sin_lon = sin(lon); + calc_t const cos_lon = cos(lon); + calc_t const sin_lat = sin(lat); + calc_t const cos_lat = cos(lat); + + // "unit" spheroid, a = 1 + calc_t const N = c1 / math::sqrt(c1 - e_sqr * math::sqr(sin_lat)); + calc_t const N_cos_lat = N * cos_lat; + + set<0>(result, N_cos_lat * cos_lon); + set<1>(result, N_cos_lat * sin_lon); + set<2>(result, N * (c1 - e_sqr) * sin_lat); + + set<0>(east, -sin_lon); + set<1>(east, cos_lon); + set<2>(east, 0); + + set<0>(north, -sin_lat * cos_lon); + set<1>(north, -sin_lat * sin_lon); + set<2>(north, cos_lat); +} + +template <typename PointGeo, typename Point3d, typename Spheroid> +inline PointGeo cart3d_to_geo(Point3d const& point_3d, Spheroid const& spheroid) +{ + typedef typename coordinate_type<PointGeo>::type coord_t; + typedef typename coordinate_type<Point3d>::type calc_t; + + calc_t const c1 = 1; + //calc_t const c2 = 2; + calc_t const e_sqr = eccentricity_sqr<calc_t>(spheroid); + + calc_t const x = get<0>(point_3d); + calc_t const y = get<1>(point_3d); + calc_t const z = get<2>(point_3d); + calc_t const xy_l = math::sqrt(math::sqr(x) + math::sqr(y)); + + calc_t const lonr = atan2(y, x); + + // NOTE: Alternative version + // http://www.iag-aig.org/attach/989c8e501d9c5b5e2736955baf2632f5/V60N2_5FT.pdf + // calc_t const lonr = c2 * atan2(y, x + xy_l); + + calc_t const latr = atan2(z, (c1 - e_sqr) * xy_l); + + // NOTE: If h is equal to 0 then there is no need to improve value of latitude + // because then N_i / (N_i + h_i) = 1 + // http://www.navipedia.net/index.php/Ellipsoidal_and_Cartesian_Coordinates_Conversion + + PointGeo res; + + set_from_radian<0>(res, lonr); + set_from_radian<1>(res, latr); + + coord_t lon = get<0>(res); + coord_t lat = get<1>(res); + + math::normalize_spheroidal_coordinates + < + typename coordinate_system<PointGeo>::type::units, + coord_t + >(lon, lat); + + set<0>(res, lon); + set<1>(res, lat); + + return res; +} + +template <typename Point3d, typename Spheroid> +inline Point3d projected_to_xy(Point3d const& point_3d, Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + // len_xy = sqrt(x^2 + y^2) + // r = len_xy - |z / tan(lat)| + // assuming h = 0 + // lat = atan2(z, (1 - e^2) * len_xy); + // |z / tan(lat)| = (1 - e^2) * len_xy + // r = e^2 * len_xy + // x_res = r * cos(lon) = e^2 * len_xy * x / len_xy = e^2 * x + // y_res = r * sin(lon) = e^2 * len_xy * y / len_xy = e^2 * y + + coord_t const c0 = 0; + coord_t const e_sqr = formula::eccentricity_sqr<coord_t>(spheroid); + + Point3d res; + + set<0>(res, e_sqr * get<0>(point_3d)); + set<1>(res, e_sqr * get<1>(point_3d)); + set<2>(res, c0); + + return res; +} + +template <typename Point3d, typename Spheroid> +inline Point3d projected_to_surface(Point3d const& direction, Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + //coord_t const c0 = 0; + coord_t const c2 = 2; + coord_t const c4 = 4; + + // calculate the point of intersection of a ray and spheroid's surface + // the origin is the origin of the coordinate system + //(x*x+y*y)/(a*a) + z*z/(b*b) = 1 + // x = d.x * t + // y = d.y * t + // z = d.z * t + coord_t const dx = get<0>(direction); + coord_t const dy = get<1>(direction); + coord_t const dz = get<2>(direction); + + //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid)); + //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); + // "unit" spheroid, a = 1 + coord_t const a_sqr = 1; + coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + + coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; + coord_t const delta = c4 * param_a; + // delta >= 0 + coord_t const t = math::sqrt(delta) / (c2 * param_a); + + // result = direction * t + Point3d result = direction; + multiply_value(result, t); + + return result; +} + +template <typename Point3d, typename Spheroid> +inline bool projected_to_surface(Point3d const& origin, Point3d const& direction, Point3d & result1, Point3d & result2, Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + coord_t const c0 = 0; + coord_t const c1 = 1; + coord_t const c2 = 2; + coord_t const c4 = 4; + + // calculate the point of intersection of a ray and spheroid's surface + //(x*x+y*y)/(a*a) + z*z/(b*b) = 1 + // x = o.x + d.x * t + // y = o.y + d.y * t + // z = o.z + d.z * t + coord_t const ox = get<0>(origin); + coord_t const oy = get<1>(origin); + coord_t const oz = get<2>(origin); + coord_t const dx = get<0>(direction); + coord_t const dy = get<1>(direction); + coord_t const dz = get<2>(direction); + + //coord_t const a_sqr = math::sqr(get_radius<0>(spheroid)); + //coord_t const b_sqr = math::sqr(get_radius<2>(spheroid)); + // "unit" spheroid, a = 1 + coord_t const a_sqr = 1; + coord_t const b_sqr = math::sqr(get_radius<2>(spheroid) / get_radius<0>(spheroid)); + + coord_t const param_a = (dx*dx + dy*dy) / a_sqr + dz*dz / b_sqr; + coord_t const param_b = c2 * ((ox*dx + oy*dy) / a_sqr + oz*dz / b_sqr); + coord_t const param_c = (ox*ox + oy*oy) / a_sqr + oz*oz / b_sqr - c1; + + coord_t const delta = math::sqr(param_b) - c4 * param_a*param_c; + + // equals() ? + if (delta < c0 || param_a == 0) + { + return false; + } + + // result = origin + direction * t + + coord_t const sqrt_delta = math::sqrt(delta); + coord_t const two_a = c2 * param_a; + + coord_t const t1 = (-param_b + sqrt_delta) / two_a; + result1 = direction; + multiply_value(result1, t1); + add_point(result1, origin); + + coord_t const t2 = (-param_b - sqrt_delta) / two_a; + result2 = direction; + multiply_value(result2, t2); + add_point(result2, origin); + + return true; +} + +template <typename Point3d, typename Spheroid> +inline bool great_elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + Point3d n1 = cross_product(a1, a2); + Point3d n2 = cross_product(b1, b2); + + // intersection direction + Point3d id = cross_product(n1, n2); + coord_t id_len_sqr = dot_product(id, id); + + if (math::equals(id_len_sqr, c0)) + { + return false; + } + + // no need to normalize a1 and a2 because the intersection point on + // the opposite side of the globe is at the same distance from the origin + coord_t cos_a1i = dot_product(a1, id); + coord_t cos_a2i = dot_product(a2, id); + coord_t gri = math::detail::greatest(cos_a1i, cos_a2i); + Point3d neg_id = id; + multiply_value(neg_id, -c1); + coord_t cos_a1ni = dot_product(a1, neg_id); + coord_t cos_a2ni = dot_product(a2, neg_id); + coord_t grni = math::detail::greatest(cos_a1ni, cos_a2ni); + + if (gri >= grni) + { + result = projected_to_surface(id, spheroid); + } + else + { + result = projected_to_surface(neg_id, spheroid); + } + + return true; +} + +template <typename Point3d1, typename Point3d2> +static inline int elliptic_side_value(Point3d1 const& origin, Point3d1 const& norm, Point3d2 const& pt) +{ + typedef typename coordinate_type<Point3d1>::type calc_t; + calc_t c0 = 0; + + // vector oposite to pt - origin + // only for the purpose of assigning origin + Point3d1 vec = origin; + subtract_point(vec, pt); + + calc_t d = dot_product(norm, vec); + + // since the vector is opposite the signs are opposite + return math::equals(d, c0) ? 0 + : d < c0 ? 1 + : -1; // d > 0 +} + +template <typename Point3d, typename Spheroid> +inline bool planes_spheroid_intersection(Point3d const& o1, Point3d const& n1, + Point3d const& o2, Point3d const& n2, + Point3d & ip1, Point3d & ip2, + Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + // Below + // n . (p - o) = 0 + // n . p - n . o = 0 + // n . p + d = 0 + // n . p = h + + // intersection direction + Point3d id = cross_product(n1, n2); + + if (math::equals(dot_product(id, id), c0)) + { + return false; + } + + coord_t dot_n1_n2 = dot_product(n1, n2); + coord_t dot_n1_n2_sqr = math::sqr(dot_n1_n2); + + coord_t h1 = dot_product(n1, o1); + coord_t h2 = dot_product(n2, o2); + + coord_t denom = c1 - dot_n1_n2_sqr; + coord_t C1 = (h1 - h2 * dot_n1_n2) / denom; + coord_t C2 = (h2 - h1 * dot_n1_n2) / denom; + + // C1 * n1 + C2 * n2 + Point3d C1_n1 = n1; + multiply_value(C1_n1, C1); + Point3d C2_n2 = n2; + multiply_value(C2_n2, C2); + Point3d io = C1_n1; + add_point(io, C2_n2); + + if (! projected_to_surface(io, id, ip1, ip2, spheroid)) + { + return false; + } + + return true; +} + + +template <typename Point3d, typename Spheroid> +inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2, + Point3d & v1, Point3d & v2, + Point3d & origin, Point3d & normal, + Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + Point3d xy1 = projected_to_xy(p1, spheroid); + Point3d xy2 = projected_to_xy(p2, spheroid); + + // origin = (xy1 + xy2) / 2 + origin = xy1; + add_point(origin, xy2); + multiply_value(origin, coord_t(0.5)); + + // v1 = p1 - origin + v1 = p1; + subtract_point(v1, origin); + // v2 = p2 - origin + v2 = p2; + subtract_point(v2, origin); + + normal = cross_product(v1, v2); +} + +template <typename Point3d, typename Spheroid> +inline void experimental_elliptic_plane(Point3d const& p1, Point3d const& p2, + Point3d & origin, Point3d & normal, + Spheroid const& spheroid) +{ + Point3d v1, v2; + experimental_elliptic_plane(p1, p2, v1, v2, origin, normal, spheroid); +} + +template <typename Point3d, typename Spheroid> +inline bool experimental_elliptic_intersection(Point3d const& a1, Point3d const& a2, + Point3d const& b1, Point3d const& b2, + Point3d & result, + Spheroid const& spheroid) +{ + typedef typename coordinate_type<Point3d>::type coord_t; + + coord_t c0 = 0; + coord_t c1 = 1; + + Point3d a1v, a2v, o1, n1; + experimental_elliptic_plane(a1, a2, a1v, a2v, o1, n1, spheroid); + Point3d b1v, b2v, o2, n2; + experimental_elliptic_plane(b1, b2, b1v, b2v, o2, n2, spheroid); + + if (! detail::vec_normalize(n1) || ! detail::vec_normalize(n2)) + { + return false; + } + + Point3d ip1_s, ip2_s; + if (! planes_spheroid_intersection(o1, n1, o2, n2, ip1_s, ip2_s, spheroid)) + { + return false; + } + + // NOTE: simplified test, may not work in all cases + coord_t dot_a1i1 = dot_product(a1, ip1_s); + coord_t dot_a2i1 = dot_product(a2, ip1_s); + coord_t gri1 = math::detail::greatest(dot_a1i1, dot_a2i1); + coord_t dot_a1i2 = dot_product(a1, ip2_s); + coord_t dot_a2i2 = dot_product(a2, ip2_s); + coord_t gri2 = math::detail::greatest(dot_a1i2, dot_a2i2); + + result = gri1 >= gri2 ? ip1_s : ip2_s; + + return true; +} + +} // namespace formula + +}} // namespace boost::geometry + +#endif // BOOST_GEOMETRY_FORMULAS_GEOGRAPHIC_HPP diff --git a/boost/geometry/formulas/gnomonic_spheroid.hpp b/boost/geometry/formulas/gnomonic_spheroid.hpp index 3457397b0f..4710c6c063 100644 --- a/boost/geometry/formulas/gnomonic_spheroid.hpp +++ b/boost/geometry/formulas/gnomonic_spheroid.hpp @@ -14,12 +14,11 @@ #include <boost/geometry/core/radius.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> - #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> #include <boost/geometry/formulas/andoyer_inverse.hpp> +#include <boost/geometry/formulas/flattening.hpp> #include <boost/geometry/formulas/thomas_inverse.hpp> #include <boost/geometry/formulas/vincenty_direct.hpp> #include <boost/geometry/formulas/vincenty_inverse.hpp> diff --git a/boost/geometry/formulas/sjoberg_intersection.hpp b/boost/geometry/formulas/sjoberg_intersection.hpp index 03bd4bc97e..92f9e8e78e 100644 --- a/boost/geometry/formulas/sjoberg_intersection.hpp +++ b/boost/geometry/formulas/sjoberg_intersection.hpp @@ -20,19 +20,606 @@ #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> +#include <boost/geometry/formulas/flattening.hpp> +#include <boost/geometry/formulas/spherical.hpp> namespace boost { namespace geometry { namespace formula { /*! +\brief The intersection of two great circles as proposed by Sjoberg. +\see See + - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002 + http://link.springer.com/article/10.1007/s00190-001-0230-9 +*/ +template <typename CT> +struct sjoberg_intersection_spherical_02 +{ + // TODO: if it will be used as standalone formula + // support segments on equator and endpoints on poles + + static inline bool apply(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2, + CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2, + CT & lon, CT & lat) + { + CT tan_lat = 0; + bool res = apply_alt(lon1, lat1, lon_a2, lat_a2, + lon2, lat2, lon_b2, lat_b2, + lon, tan_lat); + + if (res) + { + lat = atan(tan_lat); + } + + return res; + } + + static inline bool apply_alt(CT const& lon1, CT const& lat1, CT const& lon_a2, CT const& lat_a2, + CT const& lon2, CT const& lat2, CT const& lon_b2, CT const& lat_b2, + CT & lon, CT & tan_lat) + { + CT const cos_lon1 = cos(lon1); + CT const sin_lon1 = sin(lon1); + CT const cos_lon2 = cos(lon2); + CT const sin_lon2 = sin(lon2); + CT const sin_lat1 = sin(lat1); + CT const sin_lat2 = sin(lat2); + CT const cos_lat1 = cos(lat1); + CT const cos_lat2 = cos(lat2); + + CT const tan_lat_a2 = tan(lat_a2); + CT const tan_lat_b2 = tan(lat_b2); + + return apply(lon1, lon_a2, lon2, lon_b2, + sin_lon1, cos_lon1, sin_lat1, cos_lat1, + sin_lon2, cos_lon2, sin_lat2, cos_lat2, + tan_lat_a2, tan_lat_b2, + lon, tan_lat); + } + +private: + static inline bool apply(CT const& lon1, CT const& lon_a2, CT const& lon2, CT const& lon_b2, + CT const& sin_lon1, CT const& cos_lon1, CT const& sin_lat1, CT const& cos_lat1, + CT const& sin_lon2, CT const& cos_lon2, CT const& sin_lat2, CT const& cos_lat2, + CT const& tan_lat_a2, CT const& tan_lat_b2, + CT & lon, CT & tan_lat) + { + // NOTE: + // cos_lat_ = 0 <=> segment on equator + // tan_alpha_ = 0 <=> segment vertical + + CT const tan_lat1 = sin_lat1 / cos_lat1; //tan(lat1); + CT const tan_lat2 = sin_lat2 / cos_lat2; //tan(lat2); + + CT const dlon1 = lon_a2 - lon1; + CT const sin_dlon1 = sin(dlon1); + CT const dlon2 = lon_b2 - lon2; + CT const sin_dlon2 = sin(dlon2); + + CT const c0 = 0; + bool const is_vertical1 = math::equals(sin_dlon1, c0); + bool const is_vertical2 = math::equals(sin_dlon2, c0); + + CT tan_alpha1 = 0; + CT tan_alpha2 = 0; + + if (is_vertical1 && is_vertical2) + { + // circles intersect at one of the poles or are collinear + return false; + } + else if (is_vertical1) + { + CT const cos_dlon2 = cos(dlon2); + CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2; + tan_alpha2 = sin_dlon2 / tan_alpha2_x; + + lon = lon1; + } + else if (is_vertical2) + { + CT const cos_dlon1 = cos(dlon1); + CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1; + tan_alpha1 = sin_dlon1 / tan_alpha1_x; + + lon = lon2; + } + else + { + CT const cos_dlon1 = cos(dlon1); + CT const cos_dlon2 = cos(dlon2); + + CT const tan_alpha1_x = cos_lat1 * tan_lat_a2 - sin_lat1 * cos_dlon1; + CT const tan_alpha2_x = cos_lat2 * tan_lat_b2 - sin_lat2 * cos_dlon2; + tan_alpha1 = sin_dlon1 / tan_alpha1_x; + tan_alpha2 = sin_dlon2 / tan_alpha2_x; + + CT const T1 = tan_alpha1 * cos_lat1; + CT const T2 = tan_alpha2 * cos_lat2; + CT const T1T2 = T1*T2; + CT const tan_lon_y = T1 * sin_lon2 - T2 * sin_lon1 + T1T2 * (tan_lat1 * cos_lon1 - tan_lat2 * cos_lon2); + CT const tan_lon_x = T1 * cos_lon2 - T2 * cos_lon1 - T1T2 * (tan_lat1 * sin_lon1 - tan_lat2 * sin_lon2); + + lon = atan2(tan_lon_y, tan_lon_x); + } + + // choose closer result + CT const pi = math::pi<CT>(); + CT const lon_2 = lon > c0 ? lon - pi : lon + pi; + CT const lon_dist1 = (std::max)((std::min)(math::longitude_difference<radian>(lon1, lon), + math::longitude_difference<radian>(lon_a2, lon)), + (std::min)(math::longitude_difference<radian>(lon2, lon), + math::longitude_difference<radian>(lon_b2, lon))); + CT const lon_dist2 = (std::max)((std::min)(math::longitude_difference<radian>(lon1, lon_2), + math::longitude_difference<radian>(lon_a2, lon_2)), + (std::min)(math::longitude_difference<radian>(lon2, lon_2), + math::longitude_difference<radian>(lon_b2, lon_2))); + if (lon_dist2 < lon_dist1) + { + lon = lon_2; + } + + CT const sin_lon = sin(lon); + CT const cos_lon = cos(lon); + + if (math::abs(tan_alpha1) >= math::abs(tan_alpha2)) // pick less vertical segment + { + CT const sin_dlon_1 = sin_lon * cos_lon1 - cos_lon * sin_lon1; + CT const cos_dlon_1 = cos_lon * cos_lon1 + sin_lon * sin_lon1; + CT const lat_y_1 = sin_dlon_1 + tan_alpha1 * sin_lat1 * cos_dlon_1; + CT const lat_x_1 = tan_alpha1 * cos_lat1; + tan_lat = lat_y_1 / lat_x_1; + } + else + { + CT const sin_dlon_2 = sin_lon * cos_lon2 - cos_lon * sin_lon2; + CT const cos_dlon_2 = cos_lon * cos_lon2 + sin_lon * sin_lon2; + CT const lat_y_2 = sin_dlon_2 + tan_alpha2 * sin_lat2 * cos_dlon_2; + CT const lat_x_2 = tan_alpha2 * cos_lat2; + tan_lat = lat_y_2 / lat_x_2; + } + + return true; + } +}; + + +/*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2 + Maxima script: + dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB); + dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B); + S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3); + assume(c_j < 1); + assume(c_j > 0); + L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x)); + L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); + +\see See + - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 + http://link.springer.com/article/10.1007/s00190-007-0204-7 +*/ +template <unsigned int Order, typename CT> +inline CT sjoberg_d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta, + CT const& Cj, CT const& sqrt_1_Cj_sqr, + CT const& e_sqr) +{ + using math::detail::bounded; + + if (Order == 0) + { + return 0; + } + + CT const c1 = 1; + CT const c2 = 2; + + CT const asin_B = asin(bounded(sin_beta / sqrt_1_Cj_sqr, -c1, c1)); + CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr); + CT const L0 = (asin_B - asin_Bj) / c2; + + if (Order == 1) + { + return -Cj * e_sqr * L0; + } + + CT const c0 = 0; + CT const c16 = 16; + + CT const X = sin_beta; + CT const Xj = sin_betaj; + CT const X_sqr = math::sqr(X); + CT const Xj_sqr = math::sqr(Xj); + CT const Cj_sqr = math::sqr(Cj); + CT const Cj_sqr_plus_one = Cj_sqr + c1; + CT const one_minus_Cj_sqr = c1 - Cj_sqr; + CT const sqrt_Y = math::sqrt(bounded(-X_sqr + one_minus_Cj_sqr, c0)); + CT const sqrt_Yj = math::sqrt(-Xj_sqr + one_minus_Cj_sqr); + CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16; + + if (Order == 2) + { + return -Cj * e_sqr * (L0 + e_sqr * L1); + } + + CT const c3 = 3; + CT const c5 = 5; + CT const c128 = 128; + + CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3; + CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5); + CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5); + CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128; + + if (Order == 3) + { + return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2)); + } + + CT const c8 = 8; + CT const c9 = 9; + CT const c10 = 10; + CT const c15 = 15; + CT const c24 = 24; + CT const c26 = 26; + CT const c33 = 33; + CT const c6144 = 6144; + + CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15; + CT const H = -c10 * Cj_sqr - c26; + CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33; + CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I; + CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I; + CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144; + + // Order 4 and higher + return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3))); +} + +/*! +\brief The representation of geodesic as proposed by Sjoberg. +\see See + - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 + http://link.springer.com/article/10.1007/s00190-007-0204-7 + - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant + and the inverse geodetic problem by numerical integration, 2012 + https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml +*/ +template <typename CT, unsigned int Order> +class sjoberg_geodesic +{ + sjoberg_geodesic() {} + + static int sign_C(CT const& alphaj) + { + CT const c0 = 0; + CT const c2 = 2; + CT const pi = math::pi<CT>(); + CT const pi_half = pi / c2; + + return (pi_half < alphaj && alphaj < pi) || (-pi_half < alphaj && alphaj < c0) ? -1 : 1; + } + +public: + sjoberg_geodesic(CT const& lon, CT const& lat, CT const& alpha, CT const& f) + : lonj(lon) + , latj(lat) + , alphaj(alpha) + { + CT const c0 = 0; + CT const c1 = 1; + CT const c2 = 2; + //CT const pi = math::pi<CT>(); + //CT const pi_half = pi / c2; + + one_minus_f = c1 - f; + e_sqr = f * (c2 - f); + + tan_latj = tan(lat); + tan_betaj = one_minus_f * tan_latj; + betaj = atan(tan_betaj); + sin_betaj = sin(betaj); + + cos_betaj = cos(betaj); + sin_alphaj = sin(alphaj); + // Clairaut constant (lower-case in the paper) + Cj = sign_C(alphaj) * cos_betaj * sin_alphaj; + Cj_sqr = math::sqr(Cj); + sqrt_1_Cj_sqr = math::sqrt(c1 - Cj_sqr); + + sign_lon_diff = alphaj >= 0 ? 1 : -1; // || alphaj == -pi ? + //sign_lon_diff = 1; + + is_on_equator = math::equals(sqrt_1_Cj_sqr, c0); + is_Cj_zero = math::equals(Cj, c0); + + t0j = c0; + asin_tj_t0j = c0; + + if (! is_Cj_zero) + { + t0j = sqrt_1_Cj_sqr / Cj; + } + + if (! is_on_equator) + { + //asin_tj_t0j = asin(tan_betaj / t0j); + asin_tj_t0j = asin(tan_betaj * Cj / sqrt_1_Cj_sqr); + } + } + + struct vertex_data + { + //CT beta0j; + CT sin_beta0j; + CT dL0j; + CT lon0j; + }; + + vertex_data get_vertex_data() const + { + CT const c2 = 2; + CT const pi = math::pi<CT>(); + CT const pi_half = pi / c2; + + vertex_data res; + + if (! is_Cj_zero) + { + //res.beta0j = atan(t0j); + //res.sin_beta0j = sin(res.beta0j); + res.sin_beta0j = math::sign(t0j) * sqrt_1_Cj_sqr; + res.dL0j = d_lambda(res.sin_beta0j); + res.lon0j = lonj + sign_lon_diff * (pi_half - asin_tj_t0j + res.dL0j); + } + else + { + //res.beta0j = pi_half; + //res.sin_beta0j = betaj >= 0 ? 1 : -1; + res.sin_beta0j = 1; + res.dL0j = 0; + res.lon0j = lonj; + } + + return res; + } + + bool is_sin_beta_ok(CT const& sin_beta) const + { + CT const c1 = 1; + return math::abs(sin_beta / sqrt_1_Cj_sqr) <= c1; + } + + bool k_diff(CT const& sin_beta, + CT & delta_k) const + { + if (is_Cj_zero) + { + delta_k = 0; + return true; + } + + // beta out of bounds and not close + if (! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + // NOTE: beta may be slightly out of bounds here but d_lambda handles that + CT const dLj = d_lambda(sin_beta); + delta_k = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj); + + return true; + } + + bool lon_diff(CT const& sin_beta, CT const& t, + CT & delta_lon) const + { + using math::detail::bounded; + CT const c1 = 1; + + if (is_Cj_zero) + { + delta_lon = 0; + return true; + } + + CT delta_k = 0; + if (! k_diff(sin_beta, delta_k)) + { + return false; + } + + CT const t_t0j = t / t0j; + // NOTE: t may be slightly out of bounds here + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + delta_lon = sign_lon_diff * asin_t_t0j + delta_k; + + return true; + } + + bool k_diffs(CT const& sin_beta, vertex_data const& vd, + CT & delta_k_before, CT & delta_k_behind, + bool check_sin_beta = true) const + { + CT const pi = math::pi<CT>(); + + if (is_Cj_zero) + { + delta_k_before = 0; + delta_k_behind = sign_lon_diff * pi; + return true; + } + + // beta out of bounds and not close + if (check_sin_beta + && ! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + // NOTE: beta may be slightly out of bounds here but d_lambda handles that + CT const dLj = d_lambda(sin_beta); + delta_k_before = sign_lon_diff * (/*asin_t_t0j*/ - asin_tj_t0j + dLj); + + // This version require no additional dLj calculation + delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + vd.dL0j + (vd.dL0j - dLj)); + + // [Sjoberg12] + //CT const dL101 = d_lambda(sin_betaj, vd.sin_beta0j); + // WARNING: the following call might not work if beta was OoB because only the second argument is bounded + //CT const dL_01 = d_lambda(sin_beta, vd.sin_beta0j); + //delta_k_behind = sign_lon_diff * (pi /*- asin_t_t0j*/ - asin_tj_t0j + dL101 + dL_01); + + return true; + } + + bool lon_diffs(CT const& sin_beta, CT const& t, vertex_data const& vd, + CT & delta_lon_before, CT & delta_lon_behind) const + { + using math::detail::bounded; + CT const c1 = 1; + CT const pi = math::pi<CT>(); + + if (is_Cj_zero) + { + delta_lon_before = 0; + delta_lon_behind = sign_lon_diff * pi; + return true; + } + + CT delta_k_before = 0, delta_k_behind = 0; + if (! k_diffs(sin_beta, vd, delta_k_before, delta_k_behind)) + { + return false; + } + + CT const t_t0j = t / t0j; + // NOTE: t may be slightly out of bounds here + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + CT const sign_asin_t_t0j = sign_lon_diff * asin_t_t0j; + delta_lon_before = sign_asin_t_t0j + delta_k_before; + delta_lon_behind = -sign_asin_t_t0j + delta_k_behind; + + return true; + } + + bool lon(CT const& sin_beta, CT const& t, vertex_data const& vd, + CT & lon_before, CT & lon_behind) const + { + using math::detail::bounded; + CT const c1 = 1; + CT const pi = math::pi<CT>(); + + if (is_Cj_zero) + { + lon_before = lonj; + lon_behind = lonj + sign_lon_diff * pi; + return true; + } + + if (! (is_sin_beta_ok(sin_beta) + || math::equals(math::abs(sin_beta), sqrt_1_Cj_sqr)) ) + { + return false; + } + + CT const t_t0j = t / t0j; + CT const asin_t_t0j = asin(bounded(t_t0j, -c1, c1)); + CT const dLj = d_lambda(sin_beta); + lon_before = lonj + sign_lon_diff * (asin_t_t0j - asin_tj_t0j + dLj); + lon_behind = vd.lon0j + (vd.lon0j - lon_before); + + return true; + } + + + CT lon(CT const& delta_lon) const + { + return lonj + delta_lon; + } + + CT lat(CT const& t) const + { + // t = tan(beta) = (1-f)tan(lat) + return atan(t / one_minus_f); + } + + void vertex(CT & lon, CT & lat) const + { + lon = get_vertex_data().lon0j; + if (! is_Cj_zero) + { + lat = sjoberg_geodesic::lat(t0j); + } + else + { + CT const c2 = 2; + lat = math::pi<CT>() / c2; + } + } + + CT lon_of_equator_intersection() const + { + CT const c0 = 0; + CT const dLj = d_lambda(c0); + CT const asin_tj_t0j = asin(Cj * tan_betaj / sqrt_1_Cj_sqr); + return lonj - asin_tj_t0j + dLj; + } + + CT d_lambda(CT const& sin_beta) const + { + return sjoberg_d_lambda_e_sqr<Order>(sin_betaj, sin_beta, Cj, sqrt_1_Cj_sqr, e_sqr); + } + + // [Sjoberg12] + /*CT d_lambda(CT const& sin_beta1, CT const& sin_beta2) const + { + return sjoberg_d_lambda_e_sqr<Order>(sin_beta1, sin_beta2, Cj, sqrt_1_Cj_sqr, e_sqr); + }*/ + + CT lonj; + CT latj; + CT alphaj; + + CT one_minus_f; + CT e_sqr; + + CT tan_latj; + CT tan_betaj; + CT betaj; + CT sin_betaj; + CT cos_betaj; + CT sin_alphaj; + CT Cj; + CT Cj_sqr; + CT sqrt_1_Cj_sqr; + + int sign_lon_diff; + + bool is_on_equator; + bool is_Cj_zero; + + CT t0j; + CT asin_tj_t0j; +}; + + +/*! \brief The intersection of two geodesics as proposed by Sjoberg. -\author See +\see See - [Sjoberg02] Lars E. Sjoberg, Intersections on the sphere and ellipsoid, 2002 http://link.springer.com/article/10.1007/s00190-001-0230-9 - [Sjoberg07] Lars E. Sjoberg, Geodetic intersection on the ellipsoid, 2007 http://link.springer.com/article/10.1007/s00190-007-0204-7 + - [Sjoberg12] Lars E. Sjoberg, Solutions to the ellipsoidal Clairaut constant + and the inverse geodetic problem by numerical integration, 2012 + https://www.degruyter.com/view/j/jogs.2012.2.issue-3/v10156-011-0037-4/v10156-011-0037-4.xml */ template < @@ -42,9 +629,14 @@ template > class sjoberg_intersection { + typedef sjoberg_geodesic<CT, Order> geodesic_type; typedef Inverse<CT, false, true, false, false, false> inverse_type; typedef typename inverse_type::result_type inverse_result; + static bool const enable_02 = true; + static int const max_iterations_02 = 10; + static int const max_iterations_07 = 20; + public: template <typename T1, typename T2, typename Spheroid> static inline bool apply(T1 const& lona1, T1 const& lata1, @@ -63,320 +655,562 @@ public: CT const lon_b2 = lonb2; CT const lat_b2 = latb2; - CT const alpha1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid).azimuth; - CT const alpha2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid).azimuth; + inverse_result const res1 = inverse_type::apply(lon_a1, lat_a1, lon_a2, lat_a2, spheroid); + inverse_result const res2 = inverse_type::apply(lon_b1, lat_b1, lon_b2, lat_b2, spheroid); - return apply(lon_a1, lat_a1, alpha1, lon_b1, lat_b1, alpha2, lon, lat, spheroid); + return apply(lon_a1, lat_a1, lon_a2, lat_a2, res1.azimuth, + lon_b1, lat_b1, lon_b2, lat_b2, res2.azimuth, + lon, lat, spheroid); } - + + // TODO: Currently may not work correctly if one of the endpoints is the pole template <typename Spheroid> - static inline bool apply(CT const& lon1, CT const& lat1, CT const& alpha1, - CT const& lon2, CT const& lat2, CT const& alpha2, + static inline bool apply(CT const& lon_a1, CT const& lat_a1, CT const& lon_a2, CT const& lat_a2, CT const& alpha_a1, + CT const& lon_b1, CT const& lat_b1, CT const& lon_b2, CT const& lat_b2, CT const& alpha_b1, CT & lon, CT & lat, Spheroid const& spheroid) { // coordinates in radians - // TODO - handle special cases like degenerated segments, equator, poles, etc. - CT const c0 = 0; CT const c1 = 1; - CT const c2 = 2; - CT const pi = math::pi<CT>(); - CT const pi_half = pi / c2; - CT const f = detail::flattening<CT>(spheroid); + CT const f = formula::flattening<CT>(spheroid); CT const one_minus_f = c1 - f; - CT const e_sqr = f * (c2 - f); - - CT const sin_alpha1 = sin(alpha1); - CT const sin_alpha2 = sin(alpha2); - - CT const tan_beta1 = one_minus_f * tan(lat1); - CT const tan_beta2 = one_minus_f * tan(lat2); - CT const beta1 = atan(tan_beta1); - CT const beta2 = atan(tan_beta2); - CT const cos_beta1 = cos(beta1); - CT const cos_beta2 = cos(beta2); - CT const sin_beta1 = sin(beta1); - CT const sin_beta2 = sin(beta2); - - // Clairaut constants (lower-case in the paper) - int const sign_C1 = math::abs(alpha1) <= pi_half ? 1 : -1; - int const sign_C2 = math::abs(alpha2) <= pi_half ? 1 : -1; - // Cj = 1 if on equator - CT const C1 = sign_C1 * cos_beta1 * sin_alpha1; - CT const C2 = sign_C2 * cos_beta2 * sin_alpha2; - - CT const sqrt_1_C1_sqr = math::sqrt(c1 - math::sqr(C1)); - CT const sqrt_1_C2_sqr = math::sqrt(c1 - math::sqr(C2)); - - // handle special case: segments on the equator - bool const on_equator1 = math::equals(sqrt_1_C1_sqr, c0); - bool const on_equator2 = math::equals(sqrt_1_C2_sqr, c0); - if (on_equator1 && on_equator2) + + geodesic_type geod1(lon_a1, lat_a1, alpha_a1, f); + geodesic_type geod2(lon_b1, lat_b1, alpha_b1, f); + + // Cj = 1 if on equator <=> sqrt_1_Cj_sqr = 0 + // Cj = 0 if vertical <=> sqrt_1_Cj_sqr = 1 + + if (geod1.is_on_equator && geod2.is_on_equator) { return false; } - else if (on_equator1) + else if (geod1.is_on_equator) { - CT const dL2 = d_lambda_e_sqr(sin_beta2, c0, C2, sqrt_1_C2_sqr, e_sqr); - CT const asin_t2_t02 = asin(C2 * tan_beta2 / sqrt_1_C2_sqr); + lon = geod2.lon_of_equator_intersection(); lat = c0; - lon = lon2 - asin_t2_t02 + dL2; return true; } - else if (on_equator2) + else if (geod2.is_on_equator) { - CT const dL1 = d_lambda_e_sqr(sin_beta1, c0, C1, sqrt_1_C1_sqr, e_sqr); - CT const asin_t1_t01 = asin(C1 * tan_beta1 / sqrt_1_C1_sqr); + lon = geod1.lon_of_equator_intersection(); lat = c0; - lon = lon1 - asin_t1_t01 + dL1; return true; } - CT const t01 = sqrt_1_C1_sqr / C1; - CT const t02 = sqrt_1_C2_sqr / C2; + // (lon1 - lon2) normalized to (-180, 180] + CT const lon1_minus_lon2 = math::longitude_distance_signed<radian>(geod2.lonj, geod1.lonj); - CT const asin_t1_t01 = asin(tan_beta1 / t01); - CT const asin_t2_t02 = asin(tan_beta2 / t02); - CT const t01_t02 = t01 * t02; - CT const t01_t02_2 = c2 * t01_t02; - CT const sqr_t01_sqr_t02 = math::sqr(t01) + math::sqr(t02); + // vertical segments + if (geod1.is_Cj_zero && geod2.is_Cj_zero) + { + CT const pi = math::pi<CT>(); - CT t = tan_beta1; - int t_id = 0; + // the geodesics are parallel, the intersection point cannot be calculated + if ( math::equals(lon1_minus_lon2, c0) + || math::equals(lon1_minus_lon2 + (lon1_minus_lon2 < c0 ? pi : -pi), c0) ) + { + return false; + } - // find the initial t using simplified spherical solution - // though not entirely since the reduced latitudes and azimuths are spheroidal - // [Sjoberg07] - CT const k_base = lon1 - lon2 + asin_t2_t02 - asin_t1_t01; - + lon = c0; + + // the geodesics intersect at one of the poles + CT const pi_half = pi / CT(2); + CT const abs_lat_a1 = math::abs(lat_a1); + CT const abs_lat_a2 = math::abs(lat_a2); + if (math::equals(abs_lat_a1, abs_lat_a2)) + { + lat = pi_half; + } + else + { + // pick the pole closest to one of the points of the first segment + CT const& closer_lat = abs_lat_a1 > abs_lat_a2 ? lat_a1 : lat_a2; + lat = closer_lat >= 0 ? pi_half : -pi_half; + } + + return true; + } + + CT lon_sph = 0; + + // Starting tan(beta) + CT t = 0; + + /*if (geod1.is_Cj_zero) { - CT const K = sin(k_base); - CT const d1 = sqr_t01_sqr_t02; - //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); - CT const d2 = t01_t02_2 * cos(k_base); - CT const D1 = math::sqrt(d1 - d2); - CT const D2 = math::sqrt(d1 + d2); - CT const K_t01_t02 = K * t01_t02; - - CT const T1 = K_t01_t02 / D1; - CT const T2 = K_t01_t02 / D2; - CT asin_T1_t01 = 0; - CT asin_T1_t02 = 0; - CT asin_T2_t01 = 0; - CT asin_T2_t02 = 0; - - // test 4 possible results - CT l1 = 0, l2 = 0, dl = 0; - bool found = check_t<0>( T1, - lon1, asin_T1_t01 = asin(T1 / t01), asin_t1_t01, - lon2, asin_T1_t02 = asin(T1 / t02), asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<1>(-T1, - lon1, -asin_T1_t01 , asin_t1_t01, - lon2, -asin_T1_t02 , asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<2>( T2, - lon1, asin_T2_t01 = asin(T2 / t01), asin_t1_t01, - lon2, asin_T2_t02 = asin(T2 / t02), asin_t2_t02, - t, l1, l2, dl, t_id) - || check_t<3>(-T2, - lon1, -asin_T2_t01 , asin_t1_t01, - lon2, -asin_T2_t02 , asin_t2_t02, - t, l1, l2, dl, t_id); - - boost::ignore_unused(found); + CT const k_base = lon1_minus_lon2 + geod2.sign_lon_diff * geod2.asin_tj_t0j; + t = sin(k_base) * geod2.t0j; + lon_sph = vertical_intersection_longitude(geod1.lonj, lon_b1, lon_b2); } + else if (geod2.is_Cj_zero) + { + CT const k_base = lon1_minus_lon2 - geod1.sign_lon_diff * geod1.asin_tj_t0j; + t = sin(-k_base) * geod1.t0j; + lon_sph = vertical_intersection_longitude(geod2.lonj, lon_a1, lon_a2); + } + else*/ + { + // TODO: Consider using betas instead of latitudes. + // Some function calls might be saved this way. + CT tan_lat_sph = 0; + sjoberg_intersection_spherical_02<CT>::apply_alt(lon_a1, lat_a1, lon_a2, lat_a2, + lon_b1, lat_b1, lon_b2, lat_b2, + lon_sph, tan_lat_sph); + t = one_minus_f * tan_lat_sph; // tan(beta) + } + + // TODO: no need to calculate atan here if reduced latitudes were used + // instead of latitudes above, in sjoberg_intersection_spherical_02 + CT const beta = atan(t); + + if (enable_02 && newton_method(geod1, geod2, beta, t, lon1_minus_lon2, lon, lat)) + { + return true; + } + + return converge_07(geod1, geod2, beta, t, lon1_minus_lon2, lon_sph, lon, lat); + } + +private: + static inline bool newton_method(geodesic_type const& geod1, geodesic_type const& geod2, // in + CT beta, CT t, CT const& lon1_minus_lon2, // in + CT & lon, CT & lat) // out + { + CT const c0 = 0; + CT const c1 = 1; + + CT const e_sqr = geod1.e_sqr; - // [Sjoberg07] - //int const d2_sign = t_id < 2 ? -1 : 1; - int const t_sign = (t_id % 2) ? -1 : 1; - // [Sjoberg02] - CT const C1_sqr = math::sqr(C1); - CT const C2_sqr = math::sqr(C2); - - CT beta = atan(t); - CT dL1 = 0, dL2 = 0; - CT asin_t_t01 = 0; - CT asin_t_t02 = 0; + CT lon1_diff = 0; + CT lon2_diff = 0; - for (int i = 0; i < 10; ++i) + CT abs_dbeta_last = 0; + + // [Sjoberg02] converges faster than solution in [Sjoberg07] + // Newton-Raphson method + for (int i = 0; i < max_iterations_02; ++i) { CT const sin_beta = sin(beta); - - // integrals approximation - dL1 = d_lambda_e_sqr(sin_beta1, sin_beta, C1, sqrt_1_C1_sqr, e_sqr); - dL2 = d_lambda_e_sqr(sin_beta2, sin_beta, C2, sqrt_1_C2_sqr, e_sqr); - - // [Sjoberg07] - /*CT const k = k_base + dL1 - dL2; - CT const K = sin(k); - CT const d1 = sqr_t01_sqr_t02; - //CT const d2 = t01_t02_2 * math::sqrt(c1 - math::sqr(K)); - CT const d2 = t01_t02_2 * cos(k); - CT const D = math::sqrt(d1 + d2_sign * d2); - CT const t_new = t_sign * K * t01_t02 / D; - CT const dt = math::abs(t_new - t); - t = t_new; - CT const new_beta = atan(t); - CT const dbeta = math::abs(new_beta - beta); - beta = new_beta;*/ - - // [Sjoberg02] - it converges faster - // Newton–Raphson method - asin_t_t01 = asin(t / t01); - asin_t_t02 = asin(t / t02); - CT const R1 = asin_t_t01 + dL1; - CT const R2 = asin_t_t02 + dL2; CT const cos_beta = cos(beta); CT const cos_beta_sqr = math::sqr(cos_beta); CT const G = c1 - e_sqr * cos_beta_sqr; - CT const f1 = C1 / cos_beta * math::sqrt(G / (cos_beta_sqr - C1_sqr)); - CT const f2 = C2 / cos_beta * math::sqrt(G / (cos_beta_sqr - C2_sqr)); - CT const abs_f1 = math::abs(f1); - CT const abs_f2 = math::abs(f2); - CT const dbeta = t_sign * (k_base - R2 + R1) / (abs_f1 + abs_f2); - - if (math::equals(dbeta, CT(0))) + + CT f1 = 0; + CT f2 = 0; + + if (!geod1.is_Cj_zero) + { + bool is_beta_ok = geod1.lon_diff(sin_beta, t, lon1_diff); + + if (is_beta_ok) + { + CT const H = cos_beta_sqr - geod1.Cj_sqr; + f1 = geod1.Cj / cos_beta * math::sqrt(G / H); + } + else + { + return false; + } + } + + if (!geod2.is_Cj_zero) + { + bool is_beta_ok = geod2.lon_diff(sin_beta, t, lon2_diff); + + if (is_beta_ok) + { + CT const H = cos_beta_sqr - geod2.Cj_sqr; + f2 = geod2.Cj / cos_beta * math::sqrt(G / H); + } + else + { + return false; + } + } + + // NOTE: Things may go wrong if the IP is near the vertex + // 1. May converge into the wrong direction (from the other way around). + // This happens when the starting point is on the other side than the vertex + // 2. During converging may "jump" into the other side of the vertex. + // In this case sin_beta/sqrt_1_Cj_sqr and t/t0j is not in [-1, 1] + // 3. f1-f2 may be 0 which means that the intermediate point is on the vertex + // In this case it's not possible to check if this is the correct result + + CT const dbeta_denom = f1 - f2; + //CT const dbeta_denom = math::abs(f1) + math::abs(f2); + + if (math::equals(dbeta_denom, c0)) + { + return false; + } + + // The sign of dbeta is changed WRT [Sjoberg02] + CT const dbeta = (lon1_minus_lon2 + lon1_diff - lon2_diff) / dbeta_denom; + + CT const abs_dbeta = math::abs(dbeta); + if (i > 0 && abs_dbeta > abs_dbeta_last) { + // The algorithm is not converging + // The intersection may be on the other side of the vertex + return false; + } + abs_dbeta_last = abs_dbeta; + + if (math::equals(dbeta, c0)) + { + // Result found break; } + // Because the sign of dbeta is changed WRT [Sjoberg02] dbeta is subtracted here beta = beta - dbeta; + t = tan(beta); } - - // t = tan(beta) = (1-f)tan(lat) - lat = atan(t / one_minus_f); - CT const l1 = lon1 + asin_t_t01 - asin_t1_t01 + dL1; - //CT const l2 = lon2 + asin_t_t02 - asin_t2_t02 + dL2; - lon = l1; + lat = geod1.lat(t); + // NOTE: if Cj is 0 then the result is lonj or lonj+180 + lon = ! geod1.is_Cj_zero + ? geod1.lon(lon1_diff) + : geod2.lon(lon2_diff); return true; } -private: - /*! Approximation of dLambda_j [Sjoberg07], expanded into taylor series in e^2 - Maxima script: - dLI_j(c_j, sinB_j, sinB) := integrate(1 / (sqrt(1 - c_j ^ 2 - x ^ 2)*(1 + sqrt(1 - e2*(1 - x ^ 2)))), x, sinB_j, sinB); - dL_j(c_j, B_j, B) := -e2 * c_j * dLI_j(c_j, B_j, B); - S: taylor(dLI_j(c_j, sinB_j, sinB), e2, 0, 3); - assume(c_j < 1); - assume(c_j > 0); - L1: factor(integrate(sqrt(-x ^ 2 - c_j ^ 2 + 1) / (x ^ 2 + c_j ^ 2 - 1), x)); - L2: factor(integrate(((x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - L3: factor(integrate(((x ^ 4 - 2 * x ^ 2 + 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - L4: factor(integrate(((x ^ 6 - 3 * x ^ 4 + 3 * x ^ 2 - 1)*sqrt(-x ^ 2 - c_j ^ 2 + 1)) / (x ^ 2 + c_j ^ 2 - 1), x)); - */ - static inline CT d_lambda_e_sqr(CT const& sin_betaj, CT const& sin_beta, - CT const& Cj, CT const& sqrt_1_Cj_sqr, - CT const& e_sqr) - { - if (Order == 0) - { - return 0; - } + struct geodesics_type + { + geodesics_type(geodesic_type const& g1, geodesic_type const& g2) + : geod1(g1) + , geod2(g2) + , vertex1(geod1.get_vertex_data()) + , vertex2(geod2.get_vertex_data()) + {} - CT const c2 = 2; - - CT const asin_B = asin(sin_beta / sqrt_1_Cj_sqr); - CT const asin_Bj = asin(sin_betaj / sqrt_1_Cj_sqr); - CT const L0 = (asin_B - asin_Bj) / c2; + geodesic_type const& geod1; + geodesic_type const& geod2; + typename geodesic_type::vertex_data vertex1; + typename geodesic_type::vertex_data vertex2; + }; - if (Order == 1) + struct converge_07_result + { + converge_07_result() + : lon1(0), lon2(0), k1_diff(0), k2_diff(0), t1(0), t2(0) + {} + + CT lon1, lon2; + CT k1_diff, k2_diff; + CT t1, t2; + }; + + static inline bool converge_07(geodesic_type const& geod1, geodesic_type const& geod2, + CT beta, CT t, + CT const& lon1_minus_lon2, CT const& lon_sph, + CT & lon, CT & lat) + { + //CT const c0 = 0; + //CT const c1 = 1; + //CT const c2 = 2; + //CT const pi = math::pi<CT>(); + + geodesics_type geodesics(geod1, geod2); + converge_07_result result; + + // calculate first pair of longitudes + if (!converge_07_step_one(CT(sin(beta)), t, lon1_minus_lon2, geodesics, lon_sph, result, false)) { - return -Cj * e_sqr * L0; + return false; } - CT const c1 = 1; - CT const c16 = 16; + int t_direction = 0; - CT const X = sin_beta; - CT const Xj = sin_betaj; - CT const Cj_sqr = math::sqr(Cj); - CT const Cj_sqr_plus_one = Cj_sqr + c1; - CT const one_minus_Cj_sqr = c1 - Cj_sqr; - CT const sqrt_Y = math::sqrt(-math::sqr(X) + one_minus_Cj_sqr); - CT const sqrt_Yj = math::sqrt(-math::sqr(Xj) + one_minus_Cj_sqr); - CT const L1 = (Cj_sqr_plus_one * (asin_B - asin_Bj) + X * sqrt_Y - Xj * sqrt_Yj) / c16; + CT lon_diff_prev = math::longitude_difference<radian>(result.lon1, result.lon2); - if (Order == 2) + // [Sjoberg07] + for (int i = 2; i < max_iterations_07; ++i) { - return -Cj * e_sqr * (L0 + e_sqr * L1); + // pick t candidates from previous result based on dir + CT t_cand1 = result.t1; + CT t_cand2 = result.t2; + // if direction is 0 the closer one is the first + if (t_direction < 0) + { + t_cand1 = (std::min)(result.t1, result.t2); + t_cand2 = (std::max)(result.t1, result.t2); + } + else if (t_direction > 0) + { + t_cand1 = (std::max)(result.t1, result.t2); + t_cand2 = (std::min)(result.t1, result.t2); + } + else + { + t_direction = t_cand1 < t_cand2 ? -1 : 1; + } + + CT t1 = t; + CT beta1 = beta; + // check if the further calculation is needed + if (converge_07_update(t1, beta1, t_cand1)) + { + break; + } + + bool try_t2 = false; + converge_07_result result_curr; + if (converge_07_step_one(CT(sin(beta1)), t1, lon1_minus_lon2, geodesics, lon_sph, result_curr)) + { + CT const lon_diff1 = math::longitude_difference<radian>(result_curr.lon1, result_curr.lon2); + if (lon_diff_prev > lon_diff1) + { + t = t1; + beta = beta1; + lon_diff_prev = lon_diff1; + result = result_curr; + } + else if (t_cand1 != t_cand2) + { + try_t2 = true; + } + else + { + // the result is not fully correct but it won't be more accurate + break; + } + } + // ! converge_07_step_one + else + { + if (t_cand1 != t_cand2) + { + try_t2 = true; + } + else + { + return false; + } + } + + + if (try_t2) + { + CT t2 = t; + CT beta2 = beta; + // check if the further calculation is needed + if (converge_07_update(t2, beta2, t_cand2)) + { + break; + } + + if (! converge_07_step_one(CT(sin(beta2)), t2, lon1_minus_lon2, geodesics, lon_sph, result_curr)) + { + return false; + } + + CT const lon_diff2 = math::longitude_difference<radian>(result_curr.lon1, result_curr.lon2); + if (lon_diff_prev > lon_diff2) + { + t_direction *= -1; + t = t2; + beta = beta2; + lon_diff_prev = lon_diff2; + result = result_curr; + } + else + { + // the result is not fully correct but it won't be more accurate + break; + } + } } - CT const c3 = 3; - CT const c5 = 5; - CT const c128 = 128; + lat = geod1.lat(t); + lon = ! geod1.is_Cj_zero ? result.lon1 : result.lon2; + math::normalize_longitude<radian>(lon); + + return true; + } + + static inline bool converge_07_update(CT & t, CT & beta, CT const& t_new) + { + CT const c0 = 0; + + CT const beta_new = atan(t_new); + CT const dbeta = beta_new - beta; + beta = beta_new; + t = t_new; - CT const E = Cj_sqr * (c3 * Cj_sqr + c2) + c3; - CT const X_sqr = math::sqr(X); - CT const Xj_sqr = math::sqr(Xj); - CT const F = X * (-c2 * X_sqr + c3 * Cj_sqr + c5); - CT const Fj = Xj * (-c2 * Xj_sqr + c3 * Cj_sqr + c5); - CT const L2 = (E * (asin_B - asin_Bj) + F * sqrt_Y - Fj * sqrt_Yj) / c128; + return math::equals(dbeta, c0); + } - if (Order == 3) + static inline CT const& pick_t(CT const& t1, CT const& t2, int direction) + { + return direction < 0 ? (std::min)(t1, t2) : (std::max)(t1, t2); + } + + static inline bool converge_07_step_one(CT const& sin_beta, + CT const& t, + CT const& lon1_minus_lon2, + geodesics_type const& geodesics, + CT const& lon_sph, + converge_07_result & result, + bool check_sin_beta = true) + { + bool ok = converge_07_one_geod(sin_beta, t, geodesics.geod1, geodesics.vertex1, lon_sph, + result.lon1, result.k1_diff, check_sin_beta) + && converge_07_one_geod(sin_beta, t, geodesics.geod2, geodesics.vertex2, lon_sph, + result.lon2, result.k2_diff, check_sin_beta); + + if (!ok) { - return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * L2)); + return false; } - CT const c8 = 8; - CT const c9 = 9; - CT const c10 = 10; - CT const c15 = 15; - CT const c24 = 24; - CT const c26 = 26; - CT const c33 = 33; - CT const c6144 = 6144; + CT const k = lon1_minus_lon2 + result.k1_diff - result.k2_diff; - CT const G = Cj_sqr * (Cj_sqr * (Cj_sqr * c15 + c9) + c9) + c15; - CT const H = -c10 * Cj_sqr - c26; - CT const I = Cj_sqr * (Cj_sqr * c15 + c24) + c33; - CT const J = X_sqr * (X * (c8 * X_sqr + H)) + X * I; - CT const Jj = Xj_sqr * (Xj * (c8 * Xj_sqr + H)) + Xj * I; - CT const L3 = (G * (asin_B - asin_Bj) + J * sqrt_Y - Jj * sqrt_Yj) / c6144; + // get 2 possible ts one lesser and one greater than t + // t1 is the closer one + calc_ts(t, k, geodesics.geod1, geodesics.geod2, result.t1, result.t2); - // Order 4 and higher - return -Cj * e_sqr * (L0 + e_sqr * (L1 + e_sqr * (L2 + e_sqr * L3))); + return true; } - static inline CT fj(CT const& cos_beta, CT const& cos2_beta, CT const& Cj, CT const& e_sqr) + static inline bool converge_07_one_geod(CT const& sin_beta, CT const& t, + geodesic_type const& geod, + typename geodesic_type::vertex_data const& vertex, + CT const& lon_sph, + CT & lon, CT & k_diff, + bool check_sin_beta) { + using math::detail::bounded; CT const c1 = 1; - CT const Cj_sqr = math::sqr(Cj); - return Cj / cos_beta * math::sqrt((c1 - e_sqr * cos2_beta) / (cos2_beta - Cj_sqr)); + + CT k_diff_before = 0; + CT k_diff_behind = 0; + + bool is_beta_ok = geod.k_diffs(sin_beta, vertex, k_diff_before, k_diff_behind, check_sin_beta); + + if (! is_beta_ok) + { + return false; + } + + CT const asin_t_t0j = ! geod.is_Cj_zero ? asin(bounded(t / geod.t0j, -c1, c1)) : 0; + CT const sign_asin_t_t0j = geod.sign_lon_diff * asin_t_t0j; + + CT const lon_before = geod.lonj + sign_asin_t_t0j + k_diff_before; + CT const lon_behind = geod.lonj - sign_asin_t_t0j + k_diff_behind; + + CT const lon_dist_before = math::longitude_distance_signed<radian>(lon_before, lon_sph); + CT const lon_dist_behind = math::longitude_distance_signed<radian>(lon_behind, lon_sph); + if (math::abs(lon_dist_before) <= math::abs(lon_dist_behind)) + { + k_diff = k_diff_before; + lon = lon_before; + } + else + { + k_diff = k_diff_behind; + lon = lon_behind; + } + + return true; } - template <int TId> - static inline bool check_t(CT const& t, - CT const& lon_a1, CT const& asin_t_t01, CT const& asin_t1_t01, - CT const& lon_b1, CT const& asin_t_t02, CT const& asin_t2_t02, - CT & current_t, CT & current_lon1, CT & current_lon2, CT & current_dlon, - int & t_id) + static inline void calc_ts(CT const& t, CT const& k, + geodesic_type const& geod1, geodesic_type const& geod2, + CT & t1, CT& t2) { - CT const lon1 = lon_a1 + asin_t_t01 - asin_t1_t01; - CT const lon2 = lon_b1 + asin_t_t02 - asin_t2_t02; + CT const c1 = 1; + CT const c2 = 2; - // TODO - true angle difference - CT const dlon = math::abs(lon2 - lon1); + CT const K = sin(k); - bool are_equal = math::equals(dlon, CT(0)); - - if ((TId == 0) || are_equal || dlon < current_dlon) + BOOST_GEOMETRY_ASSERT(!geod1.is_Cj_zero || !geod2.is_Cj_zero); + if (geod1.is_Cj_zero) + { + t1 = K * geod2.t0j; + t2 = -t1; + } + else if (geod2.is_Cj_zero) { - current_t = t; - current_lon1 = lon1; - current_lon2 = lon2; - current_dlon = dlon; - t_id = TId; + t1 = -K * geod1.t0j; + t2 = -t1; } + else + { + CT const A = math::sqr(geod1.t0j) + math::sqr(geod2.t0j); + CT const B = c2 * geod1.t0j * geod2.t0j * math::sqrt(c1 - math::sqr(K)); + + CT const K_t01_t02 = K * geod1.t0j * geod2.t0j; + CT const D1 = math::sqrt(A + B); + CT const D2 = math::sqrt(A - B); + CT const t_new1 = K_t01_t02 / D1; + CT const t_new2 = K_t01_t02 / D2; + CT const t_new3 = -t_new1; + CT const t_new4 = -t_new2; - return are_equal; + // Pick 2 nearest t_new, one greater and one lesser than current t + CT const abs_t_new1 = math::abs(t_new1); + CT const abs_t_new2 = math::abs(t_new2); + CT const abs_t_max = (std::max)(abs_t_new1, abs_t_new2); + t1 = -abs_t_max; // lesser + t2 = abs_t_max; // greater + if (t1 < t) + { + if (t_new1 < t && t_new1 > t1) + t1 = t_new1; + if (t_new2 < t && t_new2 > t1) + t1 = t_new2; + if (t_new3 < t && t_new3 > t1) + t1 = t_new3; + if (t_new4 < t && t_new4 > t1) + t1 = t_new4; + } + if (t2 > t) + { + if (t_new1 > t && t_new1 < t2) + t2 = t_new1; + if (t_new2 > t && t_new2 < t2) + t2 = t_new2; + if (t_new3 > t && t_new3 < t2) + t2 = t_new3; + if (t_new4 > t && t_new4 < t2) + t2 = t_new4; + } + } + + // the first one is the closer one + if (math::abs(t - t2) < math::abs(t - t1)) + { + std::swap(t2, t1); + } + } + + static inline CT fj(CT const& cos_beta, CT const& cos2_beta, CT const& Cj, CT const& e_sqr) + { + CT const c1 = 1; + CT const Cj_sqr = math::sqr(Cj); + return Cj / cos_beta * math::sqrt((c1 - e_sqr * cos2_beta) / (cos2_beta - Cj_sqr)); } + + /*static inline CT vertical_intersection_longitude(CT const& ip_lon, CT const& seg_lon1, CT const& seg_lon2) + { + CT const c0 = 0; + CT const lon_2 = ip_lon > c0 ? ip_lon - pi : ip_lon + pi; + + return (std::min)(math::longitude_difference<radian>(ip_lon, seg_lon1), + math::longitude_difference<radian>(ip_lon, seg_lon2)) + <= + (std::min)(math::longitude_difference<radian>(lon_2, seg_lon1), + math::longitude_difference<radian>(lon_2, seg_lon2)) + ? ip_lon : lon_2; + }*/ }; }}} // namespace boost::geometry::formula diff --git a/boost/geometry/formulas/spherical.hpp b/boost/geometry/formulas/spherical.hpp index 2195bbbe10..ff24c51a88 100644 --- a/boost/geometry/formulas/spherical.hpp +++ b/boost/geometry/formulas/spherical.hpp @@ -1,6 +1,7 @@ // Boost.Geometry // Copyright (c) 2016, Oracle and/or its affiliates. +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle // Use, modification and distribution is subject to the Boost Software License, @@ -27,38 +28,67 @@ namespace boost { namespace geometry { namespace formula { +template <typename T> +struct result_spherical +{ + result_spherical() + : azimuth(0) + , reverse_azimuth(0) + {} + + T azimuth; + T reverse_azimuth; +}; + +template <typename T> +static inline void sph_to_cart3d(T const& lon, T const& lat, T & x, T & y, T & z) +{ + T const cos_lat = cos(lat); + x = cos_lat * cos(lon); + y = cos_lat * sin(lon); + z = sin(lat); +} + template <typename Point3d, typename PointSph> static inline Point3d sph_to_cart3d(PointSph const& point_sph) { typedef typename coordinate_type<Point3d>::type calc_t; - Point3d res; - - calc_t lon = get_as_radian<0>(point_sph); - calc_t lat = get_as_radian<1>(point_sph); + calc_t const lon = get_as_radian<0>(point_sph); + calc_t const lat = get_as_radian<1>(point_sph); + calc_t x, y, z; + sph_to_cart3d(lon, lat, x, y, z); - calc_t const cos_lat = cos(lat); - set<0>(res, cos_lat * cos(lon)); - set<1>(res, cos_lat * sin(lon)); - set<2>(res, sin(lat)); + Point3d res; + set<0>(res, x); + set<1>(res, y); + set<2>(res, z); return res; } +template <typename T> +static inline void cart3d_to_sph(T const& x, T const& y, T const& z, T & lon, T & lat) +{ + lon = atan2(y, x); + lat = asin(z); +} + template <typename PointSph, typename Point3d> static inline PointSph cart3d_to_sph(Point3d const& point_3d) { typedef typename coordinate_type<PointSph>::type coord_t; typedef typename coordinate_type<Point3d>::type calc_t; - PointSph res; - calc_t const x = get<0>(point_3d); calc_t const y = get<1>(point_3d); calc_t const z = get<2>(point_3d); + calc_t lonr, latr; + cart3d_to_sph(x, y, z, lonr, latr); - set_from_radian<0>(res, atan2(y, x)); - set_from_radian<1>(res, asin(z)); + PointSph res; + set_from_radian<0>(res, lonr); + set_from_radian<1>(res, latr); coord_t lon = get<0>(res); coord_t lat = get<1>(res); @@ -89,6 +119,104 @@ static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt) : -1; // d < 0 } +template <typename CT, bool ReverseAzimuth, typename T1, typename T2> +static inline result_spherical<CT> spherical_azimuth(T1 const& lon1, + T1 const& lat1, + T2 const& lon2, + T2 const& lat2) +{ + typedef result_spherical<CT> result_type; + result_type result; + + // http://williams.best.vwh.net/avform.htm#Crs + // https://en.wikipedia.org/wiki/Great-circle_navigation + CT dlon = lon2 - lon1; + + // An optimization which should kick in often for Boxes + //if ( math::equals(dlon, ReturnType(0)) ) + //if ( get<0>(p1) == get<0>(p2) ) + //{ + // return - sin(get_as_radian<1>(p1)) * cos_p2lat); + //} + + CT const cos_dlon = cos(dlon); + CT const sin_dlon = sin(dlon); + CT const cos_lat1 = cos(lat1); + CT const cos_lat2 = cos(lat2); + CT const sin_lat1 = sin(lat1); + CT const sin_lat2 = sin(lat2); + + { + // "An alternative formula, not requiring the pre-computation of d" + // In the formula below dlon is used as "d" + CT const y = sin_dlon * cos_lat2; + CT const x = cos_lat1 * sin_lat2 - sin_lat1 * cos_lat2 * cos_dlon; + result.azimuth = atan2(y, x); + } + + if (ReverseAzimuth) + { + CT const y = sin_dlon * cos_lat1; + CT const x = sin_lat2 * cos_lat1 * cos_dlon - cos_lat2 * sin_lat1; + result.reverse_azimuth = atan2(y, x); + } + + return result; +} + +template <typename ReturnType, typename T1, typename T2> +inline ReturnType spherical_azimuth(T1 const& lon1, T1 const& lat1, + T2 const& lon2, T2 const& lat2) +{ + return spherical_azimuth<ReturnType, false>(lon1, lat1, lon2, lat2).azimuth; +} + +template <typename T> +inline T spherical_azimuth(T const& lon1, T const& lat1, T const& lon2, T const& lat2) +{ + return spherical_azimuth<T, false>(lon1, lat1, lon2, lat2).azimuth; +} + +template <typename T> +inline int azimuth_side_value(T const& azi_a1_p, T const& azi_a1_a2) +{ + T const pi = math::pi<T>(); + T const two_pi = math::two_pi<T>(); + + // instead of the formula from XTD + //calc_t a_diff = asin(sin(azi_a1_p - azi_a1_a2)); + + T a_diff = azi_a1_p - azi_a1_a2; + // normalize, angle in [-pi, pi] + while (a_diff > pi) + a_diff -= two_pi; + while (a_diff < -pi) + a_diff += two_pi; + + // NOTE: in general it shouldn't be required to support the pi/-pi case + // because in non-cartesian systems it makes sense to check the side + // only "between" the endpoints. + // However currently the winding strategy calls the side strategy + // for vertical segments to check if the point is "between the endpoints. + // This could be avoided since the side strategy is not required for that + // because meridian is the shortest path. So a difference of + // longitudes would be sufficient (of course normalized to [-pi, pi]). + + // NOTE: with the above said, the pi/-pi check is temporary + // however in case if this was required + // the geodesics on ellipsoid aren't "symmetrical" + // therefore instead of comparing a_diff to pi and -pi + // one should probably use inverse azimuths and compare + // the difference to 0 as well + + // positive azimuth is on the right side + return math::equals(a_diff, 0) + || math::equals(a_diff, pi) + || math::equals(a_diff, -pi) ? 0 + : a_diff > 0 ? -1 // right + : 1; // left +} + } // namespace formula }} // namespace boost::geometry diff --git a/boost/geometry/formulas/thomas_direct.hpp b/boost/geometry/formulas/thomas_direct.hpp index f8a7f83943..f208167cf5 100644 --- a/boost/geometry/formulas/thomas_direct.hpp +++ b/boost/geometry/formulas/thomas_direct.hpp @@ -20,9 +20,8 @@ #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> - #include <boost/geometry/formulas/differential_quantities.hpp> +#include <boost/geometry/formulas/flattening.hpp> #include <boost/geometry/formulas/result_direct.hpp> @@ -82,7 +81,7 @@ public: CT const a = CT(get_radius<0>(spheroid)); CT const b = CT(get_radius<2>(spheroid)); - CT const f = detail::flattening<CT>(spheroid); + CT const f = formula::flattening<CT>(spheroid); CT const one_minus_f = c1 - f; CT const pi = math::pi<CT>(); diff --git a/boost/geometry/formulas/thomas_inverse.hpp b/boost/geometry/formulas/thomas_inverse.hpp index d68c9de054..0853a36980 100644 --- a/boost/geometry/formulas/thomas_inverse.hpp +++ b/boost/geometry/formulas/thomas_inverse.hpp @@ -20,9 +20,8 @@ #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> - #include <boost/geometry/formulas/differential_quantities.hpp> +#include <boost/geometry/formulas/flattening.hpp> #include <boost/geometry/formulas/result_inverse.hpp> @@ -78,7 +77,7 @@ public: CT const c4 = 4; CT const pi_half = math::pi<CT>() / c2; - CT const f = detail::flattening<CT>(spheroid); + CT const f = formula::flattening<CT>(spheroid); CT const one_minus_f = c1 - f; // CT const tan_theta1 = one_minus_f * tan(lat1); diff --git a/boost/geometry/formulas/vertex_latitude.hpp b/boost/geometry/formulas/vertex_latitude.hpp new file mode 100644 index 0000000000..755067b08d --- /dev/null +++ b/boost/geometry/formulas/vertex_latitude.hpp @@ -0,0 +1,148 @@ +// Boost.Geometry + +// Copyright (c) 2016-2017 Oracle and/or its affiliates. + +// Contributed and/or modified by Vissarion Fysikopoulos, on behalf of Oracle +// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle + +// Use, modification and distribution is subject to the Boost Software License, +// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) + +#ifndef BOOST_GEOMETRY_FORMULAS_MAXIMUM_LATITUDE_HPP +#define BOOST_GEOMETRY_FORMULAS_MAXIMUM_LATITUDE_HPP + +#include <boost/geometry/core/srs.hpp> +#include <boost/geometry/formulas/flattening.hpp> +#include <boost/geometry/formulas/spherical.hpp> +#include <boost/mpl/assert.hpp> + +namespace boost { namespace geometry { namespace formula +{ + +/*! +\brief Algorithm to compute the vertex latitude of a geodesic segment. Vertex is +a point on the geodesic that maximizes (or minimizes) the latitude. +\author See + [Wood96] Wood - Vertex Latitudes on Ellipsoid Geodesics, SIAM Rev., 38(4), + 637–644, 1996 +*/ + +template <typename CT> +class vertex_latitude_on_sphere +{ + +public: + template<typename T1, typename T2> + static inline CT apply(T1 const& lat1, + T2 const& alp1) + { + return std::acos( math::abs(cos(lat1) * sin(alp1)) ); + } +}; + +template <typename CT> +class vertex_latitude_on_spheroid +{ + +public: +/* + * formula based on paper + * [Wood96] Wood - Vertex Latitudes on Ellipsoid Geodesics, SIAM Rev., 38(4), + * 637–644, 1996 + template <typename T1, typename T2, typename Spheroid> + static inline CT apply(T1 const& lat1, + T2 const& alp1, + Spheroid const& spheroid) + { + CT const f = formula::flattening<CT>(spheroid); + + CT const e2 = f * (CT(2) - f); + CT const sin_alp1 = sin(alp1); + CT const sin2_lat1 = math::sqr(sin(lat1)); + CT const cos2_lat1 = CT(1) - sin2_lat1; + + CT const e2_sin2 = CT(1) - e2 * sin2_lat1; + CT const cos2_sin2 = cos2_lat1 * math::sqr(sin_alp1); + CT const vertex_lat = std::asin( math::sqrt((e2_sin2 - cos2_sin2) + / (e2_sin2 - e2 * cos2_sin2))); + return vertex_lat; + } +*/ + + // simpler formula based on Clairaut relation for spheroids + template <typename T1, typename T2, typename Spheroid> + static inline CT apply(T1 const& lat1, + T2 const& alp1, + Spheroid const& spheroid) + { + CT const f = formula::flattening<CT>(spheroid); + + CT const one_minus_f = (CT(1) - f); + + //get the reduced latitude + CT const bet1 = atan( one_minus_f * tan(lat1) ); + + //apply Clairaut relation + CT const betv = vertex_latitude_on_sphere<CT>::apply(bet1, alp1); + + //return the spheroid latitude + return atan( tan(betv) / one_minus_f ); + } + + /* + template <typename T> + inline static void sign_adjustment(CT lat1, CT lat2, CT vertex_lat, T& vrt_result) + { + // signbit returns a non-zero value (true) if the sign is negative; + // and zero (false) otherwise. + bool sign = std::signbit(std::abs(lat1) > std::abs(lat2) ? lat1 : lat2); + + vrt_result.north = sign ? std::max(lat1, lat2) : vertex_lat; + vrt_result.south = sign ? vertex_lat * CT(-1) : std::min(lat1, lat2); + } + + template <typename T> + inline static bool vertex_on_segment(CT alp1, CT alp2, CT lat1, CT lat2, T& vrt_result) + { + CT const half_pi = math::pi<CT>() / CT(2); + + // if the segment does not contain the vertex of the geodesic + // then return the endpoint of max (min) latitude + if ((alp1 < half_pi && alp2 < half_pi) + || (alp1 > half_pi && alp2 > half_pi)) + { + vrt_result.north = std::max(lat1, lat2); + vrt_result.south = std::min(lat1, lat2); + return false; + } + return true; + } + */ +}; + + +template <typename CT, typename CS_Tag> +struct vertex_latitude +{ + BOOST_MPL_ASSERT_MSG + ( + false, NOT_IMPLEMENTED_FOR_THIS_COORDINATE_SYSTEM, (types<CS_Tag>) + ); + +}; + +template <typename CT> +struct vertex_latitude<CT, spherical_equatorial_tag> + : vertex_latitude_on_sphere<CT> +{}; + +template <typename CT> +struct vertex_latitude<CT, geographic_tag> + : vertex_latitude_on_spheroid<CT> +{}; + + +}}} // namespace boost::geometry::formula + +#endif // BOOST_GEOMETRY_FORMULAS_MAXIMUM_LATITUDE_HPP diff --git a/boost/geometry/formulas/vincenty_direct.hpp b/boost/geometry/formulas/vincenty_direct.hpp index f3647ff4e6..1697e5fb63 100644 --- a/boost/geometry/formulas/vincenty_direct.hpp +++ b/boost/geometry/formulas/vincenty_direct.hpp @@ -23,9 +23,8 @@ #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> - #include <boost/geometry/formulas/differential_quantities.hpp> +#include <boost/geometry/formulas/flattening.hpp> #include <boost/geometry/formulas/result_direct.hpp> @@ -85,7 +84,7 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = geometry::detail::flattening<CT>(spheroid); + CT const flattening = formula::flattening<CT>(spheroid); CT const sin_azimuth12 = sin(azimuth12); CT const cos_azimuth12 = cos(azimuth12); diff --git a/boost/geometry/formulas/vincenty_inverse.hpp b/boost/geometry/formulas/vincenty_inverse.hpp index bbda00036b..032e16e291 100644 --- a/boost/geometry/formulas/vincenty_inverse.hpp +++ b/boost/geometry/formulas/vincenty_inverse.hpp @@ -2,8 +2,8 @@ // Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands. -// This file was modified by Oracle on 2014, 2016. -// Modifications copyright (c) 2014-2016 Oracle and/or its affiliates. +// This file was modified by Oracle on 2014, 2016, 2017. +// Modifications copyright (c) 2014-2017 Oracle and/or its affiliates. // Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle @@ -23,9 +23,8 @@ #include <boost/geometry/util/condition.hpp> #include <boost/geometry/util/math.hpp> -#include <boost/geometry/algorithms/detail/flattening.hpp> - #include <boost/geometry/formulas/differential_quantities.hpp> +#include <boost/geometry/formulas/flattening.hpp> #include <boost/geometry/formulas/result_inverse.hpp> @@ -41,7 +40,7 @@ namespace boost { namespace geometry { namespace formula \brief The solution of the inverse problem of geodesics on latlong coordinates, after Vincenty, 1975 \author See - http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf - - http://www.icsm.gov.au/gda/gdav2.3.pdf + - http://www.icsm.gov.au/gda/gda-v_2.4.pdf \author Adapted from various implementations to get it close to the original document - http://www.movable-type.co.uk/scripts/LatLongVincenty.html - http://exogen.case.edu/projects/geopy/source/geopy.distance.html @@ -99,10 +98,10 @@ public: CT const radius_a = CT(get_radius<0>(spheroid)); CT const radius_b = CT(get_radius<2>(spheroid)); - CT const flattening = geometry::detail::flattening<CT>(spheroid); + CT const f = formula::flattening<CT>(spheroid); // U: reduced latitude, defined by tan U = (1-f) tan phi - CT const one_min_f = c1 - flattening; + CT const one_min_f = c1 - f; CT const tan_U1 = one_min_f * tan(lat1); // above (1) CT const tan_U2 = one_min_f * tan(lat2); // above (1) @@ -113,8 +112,9 @@ public: CT const cos_U1 = c1 / temp_den_U1; CT const cos_U2 = c1 / temp_den_U2; // sin = tan / sqrt(1 + tan^2) - CT const sin_U1 = tan_U1 / temp_den_U1; - CT const sin_U2 = tan_U2 / temp_den_U2; + // sin = tan * cos + CT const sin_U1 = tan_U1 * cos_U1; + CT const sin_U2 = tan_U2 * cos_U2; // calculate sin U and cos U directly //CT const U1 = atan(tan_U1); @@ -130,7 +130,8 @@ public: CT sin_sigma; CT sin_alpha; CT cos2_alpha; - CT cos2_sigma_m; + CT cos_2sigma_m; + CT cos2_2sigma_m; CT sigma; int counter = 0; // robustness @@ -144,12 +145,13 @@ public: CT cos_sigma = sin_U1 * sin_U2 + cos_U1 * cos_U2 * cos_lambda; // (15) sin_alpha = cos_U1 * cos_U2 * sin_lambda / sin_sigma; // (17) cos2_alpha = c1 - math::sqr(sin_alpha); - cos2_sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + cos_2sigma_m = math::equals(cos2_alpha, 0) ? 0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18) + cos2_2sigma_m = math::sqr(cos_2sigma_m); - CT C = flattening/c16 * cos2_alpha * (c4 + flattening * (c4 - c3 * cos2_alpha)); // (10) + CT C = f/c16 * cos2_alpha * (c4 + f * (c4 - c3 * cos2_alpha)); // (10) sigma = atan2(sin_sigma, cos_sigma); // (16) - lambda = L + (c1 - C) * flattening * sin_alpha * - (sigma + C * sin_sigma * ( cos2_sigma_m + C * cos_sigma * (-c1 + c2 * math::sqr(cos2_sigma_m)))); // (11) + lambda = L + (c1 - C) * f * sin_alpha * + (sigma + C * sin_sigma * (cos_2sigma_m + C * cos_sigma * (-c1 + c2 * cos2_2sigma_m))); // (11) ++counter; // robustness @@ -182,8 +184,10 @@ public: CT A = c1 + sqr_u/c16384 * (c4096 + sqr_u * (-c768 + sqr_u * (c320 - c175 * sqr_u))); // (3) CT B = sqr_u/c1024 * (c256 + sqr_u * ( -c128 + sqr_u * (c74 - c47 * sqr_u))); // (4) - CT delta_sigma = B * sin_sigma * ( cos2_sigma_m + (B/c4) * (cos(sigma)* (-c1 + c2 * cos2_sigma_m) - - (B/c6) * cos2_sigma_m * (-c3 + c4 * math::sqr(sin_sigma)) * (-c3 + c4 * cos2_sigma_m))); // (6) + CT const cos_sigma = cos(sigma); + CT const sin2_sigma = math::sqr(sin_sigma); + CT delta_sigma = B * sin_sigma * (cos_2sigma_m + (B/c4) * (cos_sigma* (-c1 + c2 * cos2_2sigma_m) + - (B/c6) * cos_2sigma_m * (-c3 + c4 * sin2_sigma) * (-c3 + c4 * cos2_2sigma_m))); // (6) result.distance = radius_b * A * (sigma - delta_sigma); // (19) } @@ -206,7 +210,7 @@ public: typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities; quantities::apply(lon1, lat1, lon2, lat2, result.azimuth, result.reverse_azimuth, - radius_b, flattening, + radius_b, f, result.reduced_length, result.geodesic_scale); } |