Added thirdparty: boost library

This commit is contained in:
Viacheslav Demydiuk
2024-01-06 19:55:56 +02:00
parent bf49f439e1
commit bccd1e7051
15683 changed files with 3239840 additions and 0 deletions
+293
View File
@@ -0,0 +1,293 @@
// Boost.Geometry
// Copyright (c) 2018 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2015-2020 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_ANDOYER_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_ANDOYER_INVERSE_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/formulas/differential_quantities.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_inverse.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief The solution of the inverse problem of geodesics on latlong coordinates,
Forsyth-Andoyer-Lambert type approximation with first order terms.
\author See
- Technical Report: PAUL D. THOMAS, MATHEMATICAL MODELS FOR NAVIGATION SYSTEMS, 1965
http://www.dtic.mil/docs/citations/AD0627893
- Technical Report: PAUL D. THOMAS, SPHEROIDAL GEODESICS, REFERENCE SYSTEMS, AND LOCAL GEOMETRY, 1970
http://www.dtic.mil/docs/citations/AD703541
*/
template <
typename CT,
bool EnableDistance,
bool EnableAzimuth,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false
>
class andoyer_inverse
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
public:
typedef result_inverse<CT> result_type;
template <typename T1, typename T2, typename Spheroid>
static inline result_type apply(T1 const& lon1,
T1 const& lat1,
T2 const& lon2,
T2 const& lat2,
Spheroid const& spheroid)
{
result_type result;
// coordinates in radians
if ( math::equals(lon1, lon2) && math::equals(lat1, lat2) )
{
return result;
}
CT const c0 = CT(0);
CT const c1 = CT(1);
CT const pi = math::pi<CT>();
CT const f = formula::flattening<CT>(spheroid);
CT const dlon = lon2 - lon1;
CT const sin_dlon = sin(dlon);
CT const cos_dlon = cos(dlon);
CT const sin_lat1 = sin(lat1);
CT const cos_lat1 = cos(lat1);
CT const sin_lat2 = sin(lat2);
CT const cos_lat2 = cos(lat2);
// H,G,T = infinity if cos_d = 1 or cos_d = -1
// lat1 == +-90 && lat2 == +-90
// lat1 == lat2 && lon1 == lon2
CT cos_d = sin_lat1*sin_lat2 + cos_lat1*cos_lat2*cos_dlon;
// on some platforms cos_d may be outside valid range
if (cos_d < -c1)
cos_d = -c1;
else if (cos_d > c1)
cos_d = c1;
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);
CT const L = math::sqr(sin_lat1+sin_lat2);
CT const three_sin_d = CT(3) * sin_d;
CT const one_minus_cos_d = c1 - cos_d;
CT const one_plus_cos_d = c1 + cos_d;
// cos_d = 1 means that the points are very close
// cos_d = -1 means that the points are antipodal
CT const H = math::equals(one_minus_cos_d, c0) ?
c0 :
(d + three_sin_d) / one_minus_cos_d;
CT const G = math::equals(one_plus_cos_d, c0) ?
c0 :
(d - three_sin_d) / one_plus_cos_d;
CT const dd = -(f/CT(4))*(H*K+G*L);
CT const a = CT(get_radius<0>(spheroid));
result.distance = a * (d + dd);
}
if ( BOOST_GEOMETRY_CONDITION(CalcAzimuths) )
{
// sin_d = 0 <=> antipodal points (incl. poles) or very close
if (math::equals(sin_d, c0))
{
// T = inf
// dA = inf
// azimuth = -inf
// TODO: The following azimuths are inconsistent with distance
// i.e. according to azimuths below a segment with antipodal endpoints
// travels through the north pole, however the distance returned above
// is the length of a segment traveling along the equator.
// Furthermore, this special case handling is only done in andoyer
// formula.
// The most correct way of fixing it is to handle antipodal regions
// correctly and consistently across all formulas.
// points very close
if (cos_d >= c0)
{
result.azimuth = c0;
result.reverse_azimuth = c0;
}
// antipodal points
else
{
// Set azimuth to 0 unless the first endpoint is the north pole
if (! math::equals(sin_lat1, c1))
{
result.azimuth = c0;
result.reverse_azimuth = pi;
}
else
{
result.azimuth = pi;
result.reverse_azimuth = c0;
}
}
}
else
{
CT const c2 = CT(2);
CT A = c0;
CT U = 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;
A = atan2(sin_dlon, M);
CT const sin_2A = sin(c2*A);
U = (f/ c2)*math::sqr(cos_lat1)*sin_2A;
}
CT B = c0;
CT V = 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;
B = atan2(sin_dlon, N);
CT const sin_2B = sin(c2*B);
V = (f/ c2)*math::sqr(cos_lat2)*sin_2B;
}
CT const T = d / sin_d;
// even with sin_d == 0 checked above if the second point
// is somewhere in the antipodal area T may still be great
// therefore dA and dB may be great and the resulting azimuths
// may be some more or less arbitrary angles
if (BOOST_GEOMETRY_CONDITION(CalcFwdAzimuth))
{
CT const dA = V*T - U;
result.azimuth = A - dA;
normalize_azimuth(result.azimuth, A, dA);
}
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
CT const dB = -U*T + V;
if (B >= 0)
result.reverse_azimuth = pi - B - dB;
else
result.reverse_azimuth = -pi - B - dB;
normalize_azimuth(result.reverse_azimuth, B, dB);
}
}
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
CT const b = CT(get_radius<2>(spheroid));
typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 1> quantities;
quantities::apply(dlon, sin_lat1, cos_lat1, sin_lat2, cos_lat2,
result.azimuth, result.reverse_azimuth,
b, f,
result.reduced_length, result.geodesic_scale);
}
return result;
}
private:
static inline void normalize_azimuth(CT & azimuth, CT const& A, CT const& dA)
{
CT const c0 = 0;
if (A >= c0) // A indicates Eastern hemisphere
{
if (dA >= c0) // A altered towards 0
{
if (azimuth < c0)
{
azimuth = c0;
}
}
else // dA < 0, A altered towards pi
{
CT const pi = math::pi<CT>();
if (azimuth > pi)
{
azimuth = pi;
}
}
}
else // A indicates Western hemisphere
{
if (dA <= c0) // A altered towards 0
{
if (azimuth > c0)
{
azimuth = c0;
}
}
else // dA > 0, A altered towards -pi
{
CT const minus_pi = -math::pi<CT>();
if (azimuth < minus_pi)
{
azimuth = minus_pi;
}
}
}
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_ANDOYER_INVERSE_HPP
+615
View File
@@ -0,0 +1,615 @@
// Boost.Geometry
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2015-2022 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_AREA_FORMULAS_HPP
#define BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/mean_radius.hpp>
#include <boost/geometry/formulas/karney_inverse.hpp>
#include <boost/geometry/util/math.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):
6166, 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 const& 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 const& 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 const& 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 const& 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[]
*/
template <typename CoeffsType>
static inline void evaluate_coeffs_var2(CT const& var2,
CoeffsType const 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) : math::pow(var2, int(i)))
* horner_evaluate(var2, coeffs1 + begin, coeffs1 + end);
begin = end;
}
}
static inline CT trapezoidal_formula(CT lat1r, CT lat2r, CT lon21r)
{
CT const c1 = CT(1);
CT const c2 = CT(2);
CT const tan_lat1 = tan(lat1r / c2);
CT const tan_lat2 = tan(lat2r / c2);
return c2 * atan(((tan_lat1 + tan_lat2) / (c1 + tan_lat1 * tan_lat2))* tan(lon21r / c2));
}
/*
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 const pi = math::pi<CT>();
CT excess;
CT const lon1r = get_as_radian<0>(p1);
CT const lat1r = get_as_radian<1>(p1);
CT const lon2r = get_as_radian<0>(p2);
CT const lat2r = get_as_radian<1>(p2);
CT lon12r = lon2r - lon1r;
math::normalize_longitude<radian, CT>(lon12r);
if (lon12r == pi || lon12r == -pi)
{
return pi;
}
if (BOOST_GEOMETRY_CONDITION(LongSegment) && lat1r != lat2r) // not for segments parallel to equator
{
CT const cbet1 = cos(lat1r);
CT const sbet1 = sin(lat1r);
CT const cbet2 = cos(lat2r);
CT const sbet2 = sin(lat2r);
CT const omg12 = lon2r - lon1r;
CT const comg12 = cos(omg12);
CT const somg12 = sin(omg12);
CT const cbet1_sbet2 = cbet1 * sbet2;
CT const sbet1_cbet2 = sbet1 * cbet2;
CT const alp1 = atan2(cbet1_sbet2 - sbet1_cbet2 * comg12, cbet2 * somg12);
CT const alp2 = atan2(cbet1_sbet2 * comg12 - sbet1_cbet2, cbet1 * somg12);
excess = alp2 - alp1;
} else {
excess = trapezoidal_formula(lat1r, lat2r, lon12r);
}
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 PointOfSegment,
typename SpheroidConst
>
static inline auto ellipsoidal(PointOfSegment const& p1,
PointOfSegment const& p2,
SpheroidConst const& spheroid_const)
{
return_type_ellipsoidal result;
CT const lon1r = get_as_radian<0>(p1);
CT const lat1r = get_as_radian<1>(p1);
CT const lon2r = get_as_radian<0>(p2);
CT const lat2r = get_as_radian<1>(p2);
// Azimuth Approximation
using inverse_type = Inverse<CT, true, true, true, false, false>;
auto i_res = inverse_type::apply(lon1r, lat1r, lon2r, lat2r, spheroid_const.m_spheroid);
CT const alp1 = i_res.azimuth;
CT const alp2 = i_res.reverse_azimuth;
// Constants
CT const c0 = CT(0);
CT const c1 = CT(1);
CT const c2 = CT(2);
CT const pi = math::pi<CT>();
CT const half_pi = pi / c2;
CT const ep = spheroid_const.m_ep;
CT const one_minus_f = c1 - spheroid_const.m_f;
// Basic trigonometric computations
// the compiler could optimize here using sincos function
// TODO: optimization: those quantities are already computed in inverse formula
// at least in some inverse formulas, so do not compute them again here
/*
CT sin_bet1 = sin(lat1r);
CT cos_bet1 = cos(lat1r);
CT sin_bet2 = sin(lat2r);
CT cos_bet2 = cos(lat2r);
sin_bet1 *= one_minus_f;
sin_bet2 *= one_minus_f;
normalize(sin_bet1, cos_bet1);
normalize(sin_bet2, cos_bet2);
*/
CT const tan_bet1 = tan(lat1r) * one_minus_f;
CT const tan_bet2 = tan(lat2r) * one_minus_f;
CT const cos_bet1 = cos(atan(tan_bet1));
CT const cos_bet2 = cos(atan(tan_bet2));
CT const sin_bet1 = tan_bet1 * cos_bet1;
CT const sin_bet2 = tan_bet2 * cos_bet2;
CT const sin_alp1 = sin(alp1);
CT const cos_alp1 = cos(alp1);
CT const cos_alp2 = cos(alp2);
CT const sin_alp0 = sin_alp1 * cos_bet1;
// Spherical term computation
CT excess;
CT lon12r = lon2r - lon1r;
math::normalize_longitude<radian, CT>(lon12r);
// Comparing with "==" works with all test cases here, but could potential create numerical issues
if (lon12r == pi || lon12r == -pi)
{
result.spherical_term = pi;
}
else
{
bool const meridian = lon12r == c0
|| lat1r == half_pi || lat1r == -half_pi
|| lat2r == half_pi || lat2r == -half_pi;
if (!meridian && (i_res.distance)
< mean_radius<CT>(spheroid_const.m_spheroid) / CT(638)) // short segment
{
excess = trapezoidal_formula(lat1r, lat2r, lon12r);
}
else
{
/* in some cases this formula gives more accurate results
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));
*/
excess = alp2 - alp1;
}
result.spherical_term = excess;
}
// Ellipsoidal term computation (uses integral approximation)
CT const cos_alp0 = math::sqrt(c1 - math::sqr(sin_alp0));
//CT const cos_alp0 = hypot(cos_alp1, sin_alp1 * sin_bet1);
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];
if (ExpandEpsN) // expand by eps and n
{
CT const k2 = math::sqr(ep * cos_alp0);
CT const sqrt_k2_plus_one = math::sqrt(c1 + k2);
CT const eps = (sqrt_k2_plus_one - c1) / (sqrt_k2_plus_one + c1);
// Generate and evaluate the polynomials on eps (i.e. var2 = eps)
// to get the final series coefficients
evaluate_coeffs_var2(eps, spheroid_const.m_coeffs_var, coeffs);
}
else
{ // expand by k2 and ep
CT const k2 = math::sqr(ep * cos_alp0);
CT const ep2 = math::sqr(ep);
CT coeffs_var[((SeriesOrder+2)*(SeriesOrder+1))/2];
// 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
constexpr auto series_order_plus_one = SeriesOrder + 1;
CT const 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;
}
// Check whenever a segment crosses the prime meridian
// First normalize to [0,360)
template <typename PointOfSegment>
static inline bool crosses_prime_meridian(PointOfSegment const& p1,
PointOfSegment const& p2)
{
CT const pi = geometry::math::pi<CT>();
CT const two_pi = geometry::math::two_pi<CT>();
CT const lon1r = get_as_radian<0>(p1);
CT const lon2r = get_as_radian<0>(p2);
CT lon12 = lon2r - lon1r;
math::normalize_longitude<radian, CT>(lon12);
// Comparing with "==" works with all test cases here, but could potential create numerical issues
if (lon12 == pi || lon12 == -pi)
{
return true;
}
CT const p1_lon = lon1r - ( std::floor( lon1r / two_pi ) * two_pi );
CT const p2_lon = lon2r - ( std::floor( lon2r / two_pi ) * two_pi );
CT const max_lon = (std::max)(p1_lon, p2_lon);
CT const min_lon = (std::min)(p1_lon, p2_lon);
return max_lon > pi && min_lon < pi && max_lon - min_lon > pi;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_AREA_FORMULAS_HPP
+96
View File
@@ -0,0 +1,96 @@
// Boost.Geometry
// Copyright (c) 2017 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_AUTHALIC_RADIUS_SQR_HPP
#define BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/formulas/eccentricity_sqr.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/algorithms/not_implemented.hpp>
#include <boost/math/special_functions/atanh.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DISPATCH
namespace formula_dispatch
{
template <typename ResultType, typename Geometry, typename Tag = typename tag<Geometry>::type>
struct authalic_radius_sqr
: not_implemented<Tag>
{};
template <typename ResultType, typename Geometry>
struct authalic_radius_sqr<ResultType, Geometry, srs_sphere_tag>
{
static inline ResultType apply(Geometry const& geometry)
{
return math::sqr<ResultType>(get_radius<0>(geometry));
}
};
template <typename ResultType, typename Geometry>
struct authalic_radius_sqr<ResultType, Geometry, srs_spheroid_tag>
{
static inline ResultType apply(Geometry const& geometry)
{
ResultType const a2 = math::sqr<ResultType>(get_radius<0>(geometry));
ResultType const e2 = formula::eccentricity_sqr<ResultType>(geometry);
return apply(a2, e2);
}
static inline ResultType apply(ResultType const& a2, ResultType const& e2)
{
ResultType const c0 = 0;
if (math::equals(e2, c0))
{
return a2;
}
ResultType const e = math::sqrt(e2);
ResultType const c2 = 2;
//ResultType const b2 = math::sqr(get_radius<2>(geometry));
//return a2 / c2 + b2 * boost::math::atanh(e) / (c2 * e);
ResultType const c1 = 1;
return (a2 / c2) * ( c1 + (c1 - e2) * boost::math::atanh(e) / e );
}
};
} // namespace formula_dispatch
#endif // DOXYGEN_NO_DISPATCH
#ifndef DOXYGEN_NO_DETAIL
namespace formula
{
template <typename ResultType, typename Geometry>
inline ResultType authalic_radius_sqr(Geometry const& geometry)
{
return formula_dispatch::authalic_radius_sqr<ResultType, Geometry>::apply(geometry);
}
} // namespace formula
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_FORMULAS_AUTHALIC_RADIUS_SQR_HPP
+308
View File
@@ -0,0 +1,308 @@
// Boost.Geometry
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2016-2019 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_INVERSE_DIFFERENTIAL_QUANTITIES_HPP
#define BOOST_GEOMETRY_FORMULAS_INVERSE_DIFFERENTIAL_QUANTITIES_HPP
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief The solution of a part of the inverse problem - differential quantities.
\author See
- Charles F.F Karney, Algorithms for geodesics, 2011
https://arxiv.org/pdf/1109.4448.pdf
*/
template <
typename CT,
bool EnableReducedLength,
bool EnableGeodesicScale,
unsigned int Order = 2,
bool ApproxF = true
>
class differential_quantities
{
public:
static inline void apply(CT const& lon1, CT const& lat1,
CT const& lon2, CT const& lat2,
CT const& azimuth, CT const& reverse_azimuth,
CT const& b, CT const& f,
CT & reduced_length, CT & geodesic_scale)
{
CT const dlon = lon2 - lon1;
CT const sin_lat1 = sin(lat1);
CT const cos_lat1 = cos(lat1);
CT const sin_lat2 = sin(lat2);
CT const cos_lat2 = cos(lat2);
apply(dlon, sin_lat1, cos_lat1, sin_lat2, cos_lat2,
azimuth, reverse_azimuth,
b, f,
reduced_length, geodesic_scale);
}
static inline void apply(CT const& dlon,
CT const& sin_lat1, CT const& cos_lat1,
CT const& sin_lat2, CT const& cos_lat2,
CT const& azimuth, CT const& reverse_azimuth,
CT const& b, CT const& f,
CT & reduced_length, CT & geodesic_scale)
{
CT const c0 = 0;
CT const c1 = 1;
CT const one_minus_f = c1 - f;
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))
{
CT const sig_12 = dlon / one_minus_f;
if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
{
BOOST_GEOMETRY_ASSERT((-math::pi<CT>() <= azimuth && azimuth <= math::pi<CT>()));
int azi_sign = math::sign(azimuth) >= 0 ? 1 : -1; // for antipodal
CT m12 = azi_sign * sin(sig_12) * b;
reduced_length = m12;
}
if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
CT M12 = cos(sig_12);
geodesic_scale = M12;
}
}
else
{
CT const c2 = 2;
CT const e2 = f * (c2 - f);
CT const ep2 = e2 / math::sqr(one_minus_f);
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;
CT cos_sig2 = cos_alp2 * cos_bet2;
normalize(sin_sig1, cos_sig1);
normalize(sin_sig2, cos_sig2);
CT const sin_alp0 = sin_alp1 * cos_bet1;
CT const cos_alp0_sqr = c1 - math::sqr(sin_alp0);
CT const J12 = BOOST_GEOMETRY_CONDITION(ApproxF) ?
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 + ep2 * math::sqr(sin_bet1));
CT const dn2 = math::sqrt(c1 + ep2 * math::sqr(sin_bet2));
if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
{
CT const m12_b = dn2 * (cos_sig1 * sin_sig2)
- dn1 * (sin_sig1 * cos_sig2)
- cos_sig1 * cos_sig2 * J12;
CT const m12 = m12_b * b;
reduced_length = m12;
}
if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
CT const cos_sig12 = cos_sig1 * cos_sig2 + sin_sig1 * sin_sig2;
CT const t = ep2 * (cos_bet1 - cos_bet2) * (cos_bet1 + cos_bet2) / (dn1 + dn2);
CT const M12 = cos_sig12 + (t * sin_sig2 - cos_sig2 * J12) * sin_sig1 / dn1;
geodesic_scale = M12;
}
}
}
private:
/*! Approximation of J12, expanded into taylor series in f
Maxima script:
ep2: f * (2-f) / ((1-f)^2);
k2: ca02 * ep2;
assume(f < 1);
assume(sig > 0);
I1(sig):= integrate(sqrt(1 + k2 * sin(s)^2), s, 0, sig);
I2(sig):= integrate(1/sqrt(1 + k2 * sin(s)^2), s, 0, sig);
J(sig):= I1(sig) - I2(sig);
S: taylor(J(sig), f, 0, 3);
S1: factor( 2*integrate(sin(s)^2,s,0,sig)*ca02*f );
S2: factor( ((integrate(-6*ca02^2*sin(s)^4+6*ca02*sin(s)^2,s,0,sig)+integrate(-2*ca02^2*sin(s)^4+6*ca02*sin(s)^2,s,0,sig))*f^2)/4 );
S3: factor( ((integrate(30*ca02^3*sin(s)^6-54*ca02^2*sin(s)^4+24*ca02*sin(s)^2,s,0,sig)+integrate(6*ca02^3*sin(s)^6-18*ca02^2*sin(s)^4+24*ca02*sin(s)^2,s,0,sig))*f^3)/12 );
*/
static inline CT J12_f(CT const& sin_sig1, CT const& cos_sig1,
CT const& sin_sig2, CT const& cos_sig2,
CT const& cos_alp0_sqr, CT const& f)
{
if (BOOST_GEOMETRY_CONDITION(Order == 0))
{
return 0;
}
CT const c2 = 2;
CT const sig_12 = atan2(cos_sig1 * sin_sig2 - sin_sig1 * cos_sig2,
cos_sig1 * cos_sig2 + sin_sig1 * sin_sig2);
CT const sin_2sig1 = c2 * cos_sig1 * sin_sig1; // sin(2sig1)
CT const sin_2sig2 = c2 * cos_sig2 * sin_sig2; // sin(2sig2)
CT const sin_2sig_12 = sin_2sig2 - sin_2sig1;
CT const L1 = sig_12 - sin_2sig_12 / c2;
if (BOOST_GEOMETRY_CONDITION(Order == 1))
{
return cos_alp0_sqr * f * L1;
}
CT const sin_4sig1 = c2 * sin_2sig1 * (math::sqr(cos_sig1) - math::sqr(sin_sig1)); // sin(4sig1)
CT const sin_4sig2 = c2 * sin_2sig2 * (math::sqr(cos_sig2) - math::sqr(sin_sig2)); // sin(4sig2)
CT const sin_4sig_12 = sin_4sig2 - sin_4sig1;
CT const c8 = 8;
CT const c12 = 12;
CT const c16 = 16;
CT const c24 = 24;
CT const L2 = -( cos_alp0_sqr * sin_4sig_12
+ (-c8 * cos_alp0_sqr + c12) * sin_2sig_12
+ (c12 * cos_alp0_sqr - c24) * sig_12)
/ c16;
if (BOOST_GEOMETRY_CONDITION(Order == 2))
{
return cos_alp0_sqr * f * (L1 + f * L2);
}
CT const c4 = 4;
CT const c9 = 9;
CT const c48 = 48;
CT const c60 = 60;
CT const c64 = 64;
CT const c96 = 96;
CT const c128 = 128;
CT const c144 = 144;
CT const cos_alp0_quad = math::sqr(cos_alp0_sqr);
CT const sin3_2sig1 = math::sqr(sin_2sig1) * sin_2sig1;
CT const sin3_2sig2 = math::sqr(sin_2sig2) * sin_2sig2;
CT const sin3_2sig_12 = sin3_2sig2 - sin3_2sig1;
CT const A = (c9 * cos_alp0_quad - c12 * cos_alp0_sqr) * sin_4sig_12;
CT const B = c4 * cos_alp0_quad * sin3_2sig_12;
CT const C = (-c48 * cos_alp0_quad + c96 * cos_alp0_sqr - c64) * sin_2sig_12;
CT const D = (c60 * cos_alp0_quad - c144 * cos_alp0_sqr + c128) * sig_12;
CT const L3 = (A + B + C + D) / c64;
// Order 3 and higher
return cos_alp0_sqr * f * (L1 + f * (L2 + f * L3));
}
/*! Approximation of J12, expanded into taylor series in e'^2
Maxima script:
k2: ca02 * ep2;
assume(sig > 0);
I1(sig):= integrate(sqrt(1 + k2 * sin(s)^2), s, 0, sig);
I2(sig):= integrate(1/sqrt(1 + k2 * sin(s)^2), s, 0, sig);
J(sig):= I1(sig) - I2(sig);
S: taylor(J(sig), ep2, 0, 3);
S1: factor( integrate(sin(s)^2,s,0,sig)*ca02*ep2 );
S2: factor( (integrate(sin(s)^4,s,0,sig)*ca02^2*ep2^2)/2 );
S3: factor( (3*integrate(sin(s)^6,s,0,sig)*ca02^3*ep2^3)/8 );
*/
static inline CT J12_ep_sqr(CT const& sin_sig1, CT const& cos_sig1,
CT const& sin_sig2, CT const& cos_sig2,
CT const& cos_alp0_sqr, CT const& ep_sqr)
{
if (BOOST_GEOMETRY_CONDITION(Order == 0))
{
return 0;
}
CT const c2 = 2;
CT const c4 = 4;
CT const c2a0ep2 = cos_alp0_sqr * ep_sqr;
CT const sig_12 = atan2(cos_sig1 * sin_sig2 - sin_sig1 * cos_sig2,
cos_sig1 * cos_sig2 + sin_sig1 * sin_sig2); // sig2 - sig1
CT const sin_2sig1 = c2 * cos_sig1 * sin_sig1; // sin(2sig1)
CT const sin_2sig2 = c2 * cos_sig2 * sin_sig2; // sin(2sig2)
CT const sin_2sig_12 = sin_2sig2 - sin_2sig1;
CT const L1 = (c2 * sig_12 - sin_2sig_12) / c4;
if (BOOST_GEOMETRY_CONDITION(Order == 1))
{
return c2a0ep2 * L1;
}
CT const c8 = 8;
CT const c64 = 64;
CT const sin_4sig1 = c2 * sin_2sig1 * (math::sqr(cos_sig1) - math::sqr(sin_sig1)); // sin(4sig1)
CT const sin_4sig2 = c2 * sin_2sig2 * (math::sqr(cos_sig2) - math::sqr(sin_sig2)); // sin(4sig2)
CT const sin_4sig_12 = sin_4sig2 - sin_4sig1;
CT const L2 = (sin_4sig_12 - c8 * sin_2sig_12 + 12 * sig_12) / c64;
if (BOOST_GEOMETRY_CONDITION(Order == 2))
{
return c2a0ep2 * (L1 + c2a0ep2 * L2);
}
CT const sin3_2sig1 = math::sqr(sin_2sig1) * sin_2sig1;
CT const sin3_2sig2 = math::sqr(sin_2sig2) * sin_2sig2;
CT const sin3_2sig_12 = sin3_2sig2 - sin3_2sig1;
CT const c9 = 9;
CT const c48 = 48;
CT const c60 = 60;
CT const c512 = 512;
CT const L3 = (c9 * sin_4sig_12 + c4 * sin3_2sig_12 - c48 * sin_2sig_12 + c60 * sig_12) / c512;
// Order 3 and higher
return c2a0ep2 * (L1 + c2a0ep2 * (L2 + c2a0ep2 * L3));
}
static inline void normalize(CT & x, CT & y)
{
CT const len = math::sqrt(math::sqr(x) + math::sqr(y));
x /= len;
y /= len;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_INVERSE_DIFFERENTIAL_QUANTITIES_HPP
+72
View File
@@ -0,0 +1,72 @@
// Boost.Geometry
// Copyright (c) 2016, 2018 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/algorithms/not_implemented.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/util/math.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
+70
View File
@@ -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
+457
View File
@@ -0,0 +1,457 @@
// Boost.Geometry
// Copyright (c) 2016-2021, 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/formulas/unit_spheroid.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(formula::unit_spheroid_b<coord_t>(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(formula::unit_spheroid_b<coord_t>(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;
coord_t const t2 = (-param_b - sqrt_delta) / two_a;
geometry::detail::for_each_dimension<Point3d>([&](auto index)
{
set<index>(result1, get<index>(origin) + get<index>(direction) * t1);
set<index>(result2, get<index>(origin) + get<index>(direction) * t2);
});
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 io;
geometry::detail::for_each_dimension<Point3d>([&](auto index)
{
set<index>(io, C1 * get<index>(n1) + C2 * get<index>(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
// v1 = p1 - origin
// v2 = p2 - origin
coord_t const half = coord_t(0.5);
geometry::detail::for_each_dimension<Point3d>([&](auto index)
{
coord_t const o = (get<index>(xy1) + get<index>(xy2)) * half;
set<index>(origin, o);
set<index>(v1, get<index>(p1) - o);
set<index>(v2, get<index>(p1) - o);
});
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 (! geometry::detail::vec_normalize(n1) || ! geometry::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
+149
View File
@@ -0,0 +1,149 @@
// Boost.Geometry
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// 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_GNOMONIC_INTERSECTION_HPP
#define BOOST_GEOMETRY_FORMULAS_GNOMONIC_INTERSECTION_HPP
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/arithmetic/cross_product.hpp>
#include <boost/geometry/formulas/gnomonic_spheroid.hpp>
#include <boost/geometry/geometries/point.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief The intersection of two geodesics using spheroidal gnomonic projection
as proposed by Karney.
\author See
- Charles F.F Karney, Algorithms for geodesics, 2011
https://arxiv.org/pdf/1109.4448.pdf
- GeographicLib forum thread: Intersection between two geodesic lines
https://sourceforge.net/p/geographiclib/discussion/1026621/thread/21aaff9f/
*/
template
<
typename CT,
template <typename, bool, bool, bool, bool, bool> class Inverse,
template <typename, bool, bool, bool, bool> class Direct
>
class gnomonic_intersection
{
public:
template <typename T1, typename T2, typename Spheroid>
static inline bool apply(T1 const& lona1, T1 const& lata1,
T1 const& lona2, T1 const& lata2,
T2 const& lonb1, T2 const& latb1,
T2 const& lonb2, T2 const& latb2,
CT & lon, CT & lat,
Spheroid const& spheroid)
{
CT const lon_a1 = lona1;
CT const lat_a1 = lata1;
CT const lon_a2 = lona2;
CT const lat_a2 = lata2;
CT const lon_b1 = lonb1;
CT const lat_b1 = latb1;
CT const lon_b2 = lonb2;
CT const lat_b2 = latb2;
return apply(lon_a1, lat_a1, lon_a2, lat_a2, lon_b1, lat_b1, lon_b2, lat_b2, lon, lat, spheroid);
}
template <typename Spheroid>
static inline bool apply(CT const& lona1, CT const& lata1,
CT const& lona2, CT const& lata2,
CT const& lonb1, CT const& latb1,
CT const& lonb2, CT const& latb2,
CT & lon, CT & lat,
Spheroid const& spheroid)
{
typedef gnomonic_spheroid<CT, Inverse, Direct> gnom_t;
lon = (lona1 + lona2 + lonb1 + lonb2) / 4;
lat = (lata1 + lata2 + latb1 + latb2) / 4;
// TODO: consider normalizing lon
for (int i = 0; i < 10; ++i)
{
CT xa1, ya1, xa2, ya2;
CT xb1, yb1, xb2, yb2;
CT x, y;
CT lon1, lat1;
bool ok = gnom_t::forward(lon, lat, lona1, lata1, xa1, ya1, spheroid)
&& gnom_t::forward(lon, lat, lona2, lata2, xa2, ya2, spheroid)
&& gnom_t::forward(lon, lat, lonb1, latb1, xb1, yb1, spheroid)
&& gnom_t::forward(lon, lat, lonb2, latb2, xb2, yb2, spheroid)
&& intersect(xa1, ya1, xa2, ya2, xb1, yb1, xb2, yb2, x, y)
&& gnom_t::inverse(lon, lat, x, y, lon1, lat1, spheroid);
if (! ok)
{
return false;
}
if (math::equals(lat1, lat) && math::equals(lon1, lon))
{
break;
}
lat = lat1;
lon = lon1;
}
// NOTE: true is also returned if the number of iterations is too great
// which means that the accuracy of the result is low
return true;
}
private:
static inline bool intersect(CT const& xa1, CT const& ya1, CT const& xa2, CT const& ya2,
CT const& xb1, CT const& yb1, CT const& xb2, CT const& yb2,
CT & x, CT & y)
{
typedef model::point<CT, 3, cs::cartesian> v3d_t;
CT const c0 = 0;
CT const c1 = 1;
v3d_t const va1(xa1, ya1, c1);
v3d_t const va2(xa2, ya2, c1);
v3d_t const vb1(xb1, yb1, c1);
v3d_t const vb2(xb2, yb2, c1);
v3d_t const la = cross_product(va1, va2);
v3d_t const lb = cross_product(vb1, vb2);
v3d_t const p = cross_product(la, lb);
CT const z = get<2>(p);
if (math::equals(z, c0))
{
// degenerated or collinear segments
return false;
}
x = get<0>(p) / z;
y = get<1>(p) / z;
return true;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_GNOMONIC_INTERSECTION_HPP
+125
View File
@@ -0,0 +1,125 @@
// 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_GNOMONIC_SPHEROID_HPP
#define BOOST_GEOMETRY_FORMULAS_GNOMONIC_SPHEROID_HPP
#include <boost/geometry/core/radius.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>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief Gnomonic projection on spheroid (ellipsoid of revolution).
\author See
- Charles F.F Karney, Algorithms for geodesics, 2011
https://arxiv.org/pdf/1109.4448.pdf
*/
template <
typename CT,
template <typename, bool, bool, bool, bool ,bool> class Inverse,
template <typename, bool, bool, bool, bool> class Direct
>
class gnomonic_spheroid
{
typedef Inverse<CT, false, true, true, true, true> inverse_type;
typedef typename inverse_type::result_type inverse_result;
typedef Direct<CT, false, false, true, true> direct_quantities_type;
typedef Direct<CT, true, false, false, false> direct_coordinates_type;
typedef typename direct_coordinates_type::result_type direct_result;
public:
template <typename Spheroid>
static inline bool forward(CT const& lon0, CT const& lat0,
CT const& lon, CT const& lat,
CT & x, CT & y,
Spheroid const& spheroid)
{
inverse_result i_res = inverse_type::apply(lon0, lat0, lon, lat, spheroid);
CT const& m = i_res.reduced_length;
CT const& M = i_res.geodesic_scale;
if (math::smaller_or_equals(M, CT(0)))
{
return false;
}
CT rho = m / M;
x = sin(i_res.azimuth) * rho;
y = cos(i_res.azimuth) * rho;
return true;
}
template <typename Spheroid>
static inline bool inverse(CT const& lon0, CT const& lat0,
CT const& x, CT const& y,
CT & lon, CT & lat,
Spheroid const& spheroid)
{
CT const a = get_radius<0>(spheroid);
CT const ds_threshold = a * std::numeric_limits<CT>::epsilon(); // TODO: 0 for non-fundamental type
CT const azimuth = atan2(x, y);
CT const rho = math::sqrt(math::sqr(x) + math::sqr(y)); // use hypot?
CT distance = a * atan(rho / a);
bool found = false;
for (int i = 0 ; i < 10 ; ++i)
{
direct_result d_res = direct_quantities_type::apply(lon0, lat0, distance, azimuth, spheroid);
CT const& m = d_res.reduced_length;
CT const& M = d_res.geodesic_scale;
if (math::smaller_or_equals(M, CT(0)))
{
// found = false;
return found;
}
CT const drho = m / M - rho; // rho = m / M
CT const ds = drho * math::sqr(M); // drho/ds = 1/M^2
distance -= ds;
// ds_threshold may be 0
if (math::abs(ds) <= ds_threshold)
{
found = true;
break;
}
}
if (found)
{
direct_result d_res = direct_coordinates_type::apply(lon0, lat0, distance, azimuth, spheroid);
lon = d_res.lon2;
lat = d_res.lat2;
}
return found;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_GNOMONIC_SPHEROID_HPP
@@ -0,0 +1,111 @@
// Boost.Geometry
// Copyright (c) 2019-2023 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_INTERPOLATE_POINT_SPHERICAL_HPP
#define BOOST_GEOMETRY_FORMULAS_INTERPOLATE_POINT_SPHERICAL_HPP
#include <boost/geometry/arithmetic/normalize.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/geometries/point.hpp>
namespace boost { namespace geometry { namespace formula
{
template <typename CalculationType>
class interpolate_point_spherical
{
typedef model::point<CalculationType, 3, cs::cartesian> point3d_t;
public :
template <typename Point>
void compute_angle(Point const& p0,
Point const& p1,
CalculationType& angle01)
{
m_xyz0 = formula::sph_to_cart3d<point3d_t>(p0);
m_xyz1 = formula::sph_to_cart3d<point3d_t>(p1);
CalculationType const dot01 = geometry::dot_product(m_xyz0, m_xyz1);
angle01 = acos(dot01);
}
template <typename Point>
void compute_axis(Point const& p0,
CalculationType const& angle01)
{
CalculationType const c0 = 0, c1 = 1;
CalculationType const pi = math::pi<CalculationType>();
if (! math::equals(angle01, pi))
{
m_axis = geometry::cross_product(m_xyz0, m_xyz1);
geometry::detail::vec_normalize(m_axis);
}
else // antipodal
{
CalculationType const half_pi = math::half_pi<CalculationType>();
CalculationType const lat = geometry::get_as_radian<1>(p0);
if (math::equals(lat, half_pi))
{
// pointing east, segment lies on prime meridian, going south
m_axis = point3d_t(c0, c1, c0);
}
else if (math::equals(lat, -half_pi))
{
// pointing west, segment lies on prime meridian, going north
m_axis = point3d_t(c0, -c1, c0);
}
else
{
// lon rotated west by pi/2 at equator
CalculationType const lon = geometry::get_as_radian<0>(p0);
m_axis = point3d_t(sin(lon), -cos(lon), c0);
}
}
}
template <typename Point>
void compute_point(CalculationType const& a, Point& p)
{
CalculationType const c1 = 1;
// Axis-Angle rotation
// see: https://en.wikipedia.org/wiki/Axis-angle_representation
CalculationType const cos_a = cos(a);
CalculationType const sin_a = sin(a);
// cos_a * v
point3d_t s1 = m_xyz0;
geometry::multiply_value(s1, cos_a);
// sin_a * (n x v)
point3d_t s2 = geometry::cross_product(m_axis, m_xyz0);
geometry::multiply_value(s2, sin_a);
// (1 - cos_a)(n.v) * n
point3d_t s3 = m_axis;
geometry::multiply_value(s3, (c1 - cos_a) *
geometry::dot_product(m_axis, m_xyz0));
// v_rot = cos_a * v + sin_a * (n x v) + (1 - cos_a)(n.v) * e
point3d_t v_rot = s1;
geometry::add_point(v_rot, s2);
geometry::add_point(v_rot, s3);
p = formula::cart3d_to_sph<Point>(v_rot);
}
private :
point3d_t m_xyz0;
point3d_t m_xyz1;
point3d_t m_axis;
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_INTERPOLATE_POINT_SPHERICAL_HPP
+259
View File
@@ -0,0 +1,259 @@
// Boost.Geometry
// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// Contributed and/or modified by Adeel Ahmad,
// as part of Google Summer of Code 2018 program.
// This file was modified by Oracle on 2018-2022.
// Modifications copyright (c) 2018-2022 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)
// This file is converted from GeographicLib, https://geographiclib.sourceforge.io
// GeographicLib is originally written by Charles Karney.
// Author: Charles Karney (2008-2017)
// Last updated version of GeographicLib: 1.49
// Original copyright notice:
// Copyright (c) Charles Karney (2008-2017) <charles@karney.com> and licensed
// under the MIT/X11 License. For more information, see
// https://geographiclib.sourceforge.io
#ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP
#define BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/hypot.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_direct.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/util/series_expansion.hpp>
namespace boost { namespace geometry { namespace formula
{
namespace se = series_expansion;
/*!
\brief The solution of the direct problem of geodesics on latlong coordinates,
after Karney (2011).
\author See
- Charles F.F Karney, Algorithms for geodesics, 2011
https://arxiv.org/pdf/1109.4448.pdf
*/
template <
typename CT,
bool EnableCoordinates = true,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false,
size_t SeriesOrder = 8
>
class karney_direct
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcCoordinates = EnableCoordinates || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcCoordinates || CalcQuantities;
public:
typedef result_direct<CT> result_type;
template <typename T, typename Dist, typename Azi, typename Spheroid>
static inline result_type apply(T const& lo1,
T const& la1,
Dist const& distance,
Azi const& azimuth12,
Spheroid const& spheroid)
{
result_type result;
CT lon1 = lo1 * math::r2d<CT>();
CT const lat1 = la1 * math::r2d<CT>();
Azi azi12 = azimuth12 * math::r2d<CT>();
math::normalize_azimuth<degree, Azi>(azi12);
CT const c0 = 0;
CT const c1 = 1;
CT const c2 = 2;
CT const b = CT(get_radius<2>(spheroid));
CT const f = formula::flattening<CT>(spheroid);
CT const one_minus_f = c1 - f;
CT const two_minus_f = c2 - f;
CT const n = f / two_minus_f;
CT const e2 = f * two_minus_f;
CT const ep2 = e2 / math::sqr(one_minus_f);
CT sin_alpha1, cos_alpha1;
math::sin_cos_degrees<CT>(azi12, sin_alpha1, cos_alpha1);
// Find the reduced latitude.
CT sin_beta1, cos_beta1;
math::sin_cos_degrees<CT>(lat1, sin_beta1, cos_beta1);
sin_beta1 *= one_minus_f;
math::normalize_unit_vector<CT>(sin_beta1, cos_beta1);
cos_beta1 = (std::max)(c0, cos_beta1);
// Obtain alpha 0 by solving the spherical triangle.
CT const sin_alpha0 = sin_alpha1 * cos_beta1;
CT const cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);
CT const k2 = math::sqr(cos_alpha0) * ep2;
CT const epsilon = k2 / (c2 * (c1 + math::sqrt(c1 + k2)) + k2);
// Find the coefficients for A1 by computing the
// series expansion using Horner scehme.
CT const expansion_A1 = se::evaluate_A1<SeriesOrder>(epsilon);
// Index zero element of coeffs_C1 is unused.
se::coeffs_C1<SeriesOrder, CT> const coeffs_C1(epsilon);
// Tau is an integration variable.
CT const tau12 = distance / (b * (c1 + expansion_A1));
CT const sin_tau12 = sin(tau12);
CT const cos_tau12 = cos(tau12);
CT sin_sigma1 = sin_beta1;
CT sin_omega1 = sin_alpha0 * sin_beta1;
CT cos_sigma1, cos_omega1;
cos_sigma1 = cos_omega1 = sin_beta1 != c0 || cos_alpha1 != c0 ? cos_beta1 * cos_alpha1 : c1;
math::normalize_unit_vector<CT>(sin_sigma1, cos_sigma1);
CT const B11 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);
CT const sin_B11 = sin(B11);
CT const cos_B11 = cos(B11);
CT const sin_tau1 = sin_sigma1 * cos_B11 + cos_sigma1 * sin_B11;
CT const cos_tau1 = cos_sigma1 * cos_B11 - sin_sigma1 * sin_B11;
// Index zero element of coeffs_C1p is unused.
se::coeffs_C1p<SeriesOrder, CT> const coeffs_C1p(epsilon);
CT const B12 = - se::sin_cos_series(sin_tau1 * cos_tau12 + cos_tau1 * sin_tau12,
cos_tau1 * cos_tau12 - sin_tau1 * sin_tau12,
coeffs_C1p);
CT const sigma12 = tau12 - (B12 - B11);
CT const sin_sigma12 = sin(sigma12);
CT const cos_sigma12 = cos(sigma12);
CT const sin_sigma2 = sin_sigma1 * cos_sigma12 + cos_sigma1 * sin_sigma12;
CT const cos_sigma2 = cos_sigma1 * cos_sigma12 - sin_sigma1 * sin_sigma12;
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
CT const sin_alpha2 = sin_alpha0;
CT const cos_alpha2 = cos_alpha0 * cos_sigma2;
result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);
}
if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
// Find the latitude at the second point.
CT const sin_beta2 = cos_alpha0 * sin_sigma2;
CT const cos_beta2 = boost::math::hypot(sin_alpha0, cos_alpha0 * cos_sigma2);
result.lat2 = atan2(sin_beta2, one_minus_f * cos_beta2);
// Find the longitude at the second point.
CT const sin_omega2 = sin_alpha0 * sin_sigma2;
CT const cos_omega2 = cos_sigma2;
CT const omega12 = atan2(sin_omega2 * cos_omega1 - cos_omega2 * sin_omega1,
cos_omega2 * cos_omega1 + sin_omega2 * sin_omega1);
se::coeffs_A3<SeriesOrder, CT> const coeffs_A3(n);
CT const A3 = math::horner_evaluate(epsilon, coeffs_A3.begin(), coeffs_A3.end());
CT const A3c = -f * sin_alpha0 * A3;
se::coeffs_C3<SeriesOrder, CT> const coeffs_C3(n, epsilon);
CT const B31 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);
CT const sin_cos_res = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C3);
CT const lam12 = omega12 + A3c * (sigma12 + (sin_cos_res - B31));
// Convert to degrees to get the longitudinal difference.
CT lon12 = lam12 * math::r2d<CT>();
// Add the longitude at first point to the longitudinal
// difference and normalize the result.
math::normalize_longitude<degree, CT>(lon1);
math::normalize_longitude<degree, CT>(lon12);
result.lon2 = lon1 + lon12;
// For longitudes close to the antimeridian the result can be out
// of range. Therefore normalize.
// In other formulas this has to be done at the end because
// otherwise differential quantities are calculated incorrectly.
// But here it's ok since result.lon2 is not used after this point.
math::normalize_longitude<degree, CT>(result.lon2);
result.lon2 *= math::d2r<CT>();
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
// Evaluate the coefficients for C2.
// Index zero element of coeffs_C2 is unused.
se::coeffs_C2<SeriesOrder, CT> const coeffs_C2(epsilon);
CT const B21 = se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);
CT const B22 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2);
// Find the coefficients for A2 by computing the
// series expansion using Horner scehme.
CT const expansion_A2 = se::evaluate_A2<SeriesOrder>(epsilon);
CT const AB1 = (c1 + expansion_A1) * (B12 - B11);
CT const AB2 = (c1 + expansion_A2) * (B22 - B21);
CT const J12 = (expansion_A1 - expansion_A2) * sigma12 + (AB1 - AB2);
CT const dn1 = math::sqrt(c1 + ep2 * math::sqr(sin_beta1));
CT const dn2 = math::sqrt(c1 + k2 * math::sqr(sin_sigma2));
// Find the reduced length.
result.reduced_length = b * ((dn2 * (cos_sigma1 * sin_sigma2) -
dn1 * (sin_sigma1 * cos_sigma2)) -
cos_sigma1 * cos_sigma2 * J12);
// Find the geodesic scale.
CT const t = k2 * (sin_sigma2 - sin_sigma1) * (sin_sigma2 + sin_sigma1) / (dn1 + dn2);
result.geodesic_scale = cos_sigma12 + (t * sin_sigma2 - cos_sigma2 * J12) *
sin_sigma1 / dn1;
}
return result;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_KARNEY_DIRECT_HPP
+993
View File
@@ -0,0 +1,993 @@
// Boost.Geometry
// Copyright (c) 2018 Adeel Ahmad, Islamabad, Pakistan.
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// Contributed and/or modified by Adeel Ahmad, as part of Google Summer of Code 2018 program.
// This file was modified by Oracle on 2019-2021.
// Modifications copyright (c) 2019-2021 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)
// This file is converted from GeographicLib, https://geographiclib.sourceforge.io
// GeographicLib is originally written by Charles Karney.
// Author: Charles Karney (2008-2017)
// Last updated version of GeographicLib: 1.49
// Original copyright notice:
// Copyright (c) Charles Karney (2008-2017) <charles@karney.com> and licensed
// under the MIT/X11 License. For more information, see
// https://geographiclib.sourceforge.io
#ifndef BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP
#include <boost/core/invoke_swap.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/math/special_functions/hypot.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/precise_math.hpp>
#include <boost/geometry/util/series_expansion.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_inverse.hpp>
namespace boost { namespace geometry { namespace math {
/*!
\brief The exact difference of two angles reduced to (-180deg, 180deg].
*/
template<typename T>
inline T difference_angle(T const& x, T const& y, T& e)
{
auto res1 = boost::geometry::detail::precise_math::two_sum(
std::remainder(-x, T(360)), std::remainder(y, T(360)));
normalize_azimuth<degree, T>(res1[0]);
// Here y - x = d + t (mod 360), exactly, where d is in (-180,180] and
// abs(t) <= eps (eps = 2^-45 for doubles). The only case where the
// addition of t takes the result outside the range (-180,180] is d = 180
// and t > 0. The case, d = -180 + eps, t = -eps, can't happen, since
// sum_error would have returned the exact result in such a case (i.e., given t = 0).
auto res2 = boost::geometry::detail::precise_math::two_sum(
res1[0] == 180 && res1[1] > 0 ? -180 : res1[0], res1[1]);
e = res2[1];
return res2[0];
}
}}} // namespace boost::geometry::math
namespace boost { namespace geometry { namespace formula
{
namespace se = series_expansion;
namespace detail
{
template <
typename CT,
bool EnableDistance,
bool EnableAzimuth,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false,
size_t SeriesOrder = 8
>
class karney_inverse
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
public:
typedef result_inverse<CT> result_type;
template <typename T1, typename T2, typename Spheroid>
static inline result_type apply(T1 const& lo1,
T1 const& la1,
T2 const& lo2,
T2 const& la2,
Spheroid const& spheroid)
{
static CT const c0 = 0;
static CT const c0_001 = 0.001;
static CT const c0_1 = 0.1;
static CT const c1 = 1;
static CT const c2 = 2;
static CT const c3 = 3;
static CT const c8 = 8;
static CT const c16 = 16;
static CT const c90 = 90;
static CT const c180 = 180;
static CT const c200 = 200;
static CT const pi = math::pi<CT>();
static CT const d2r = math::d2r<CT>();
static CT const r2d = math::r2d<CT>();
result_type result;
CT lat1 = la1 * r2d;
CT lat2 = la2 * r2d;
CT lon1 = lo1 * r2d;
CT lon2 = lo2 * r2d;
CT const a = CT(get_radius<0>(spheroid));
CT const b = CT(get_radius<2>(spheroid));
CT const f = formula::flattening<CT>(spheroid);
CT const one_minus_f = c1 - f;
CT const two_minus_f = c2 - f;
CT const tol0 = std::numeric_limits<CT>::epsilon();
CT const tol1 = c200 * tol0;
CT const tol2 = sqrt(tol0);
// Check on bisection interval.
CT const tol_bisection = tol0 * tol2;
CT const etol2 = c0_1 * tol2 /
sqrt((std::max)(c0_001, std::abs(f)) * (std::min)(c1, c1 - f / c2) / c2);
CT tiny = std::sqrt((std::numeric_limits<CT>::min)());
CT const n = f / two_minus_f;
CT const e2 = f * two_minus_f;
CT const ep2 = e2 / math::sqr(one_minus_f);
// Compute the longitudinal difference.
CT lon12_error;
CT lon12 = math::difference_angle(lon1, lon2, lon12_error);
int lon12_sign = lon12 >= 0 ? 1 : -1;
// Make points close to the meridian to lie on it.
lon12 = lon12_sign * lon12;
lon12_error = (c180 - lon12) - lon12_sign * lon12_error;
// Convert to radians.
CT lam12 = lon12 * d2r;
CT sin_lam12;
CT cos_lam12;
if (lon12 > c90)
{
math::sin_cos_degrees(lon12_error, sin_lam12, cos_lam12);
cos_lam12 *= -c1;
}
else
{
math::sin_cos_degrees(lon12, sin_lam12, cos_lam12);
}
// Make points close to the equator to lie on it.
lat1 = math::round_angle(std::abs(lat1) > c90 ? c90 : lat1);
lat2 = math::round_angle(std::abs(lat2) > c90 ? c90 : lat2);
// Arrange points in a canonical form, as explained in
// paper, Algorithms for geodesics, Eq. (44):
//
// 0 <= lon12 <= 180
// -90 <= lat1 <= 0
// lat1 <= lat2 <= -lat1
int swap_point = std::abs(lat1) < std::abs(lat2) ? -1 : 1;
if (swap_point < 0)
{
lon12_sign *= -1;
boost::core::invoke_swap(lat1, lat2);
}
// Enforce lat1 to be <= 0.
int lat_sign = lat1 < 0 ? 1 : -1;
lat1 *= lat_sign;
lat2 *= lat_sign;
CT sin_beta1, cos_beta1;
math::sin_cos_degrees(lat1, sin_beta1, cos_beta1);
sin_beta1 *= one_minus_f;
math::normalize_unit_vector<CT>(sin_beta1, cos_beta1);
cos_beta1 = (std::max)(tiny, cos_beta1);
CT sin_beta2, cos_beta2;
math::sin_cos_degrees(lat2, sin_beta2, cos_beta2);
sin_beta2 *= one_minus_f;
math::normalize_unit_vector<CT>(sin_beta2, cos_beta2);
cos_beta2 = (std::max)(tiny, cos_beta2);
// If cos_beta1 < -sin_beta1, then cos_beta2 - cos_beta1 is a
// sensitive measure of the |beta1| - |beta2|. Alternatively,
// (cos_beta1 >= -sin_beta1), abs(sin_beta2) + sin_beta1 is
// a better measure.
// Sometimes these quantities vanish and in that case we
// force beta2 = +/- bet1a exactly.
if (cos_beta1 < -sin_beta1)
{
if (cos_beta1 == cos_beta2)
{
sin_beta2 = sin_beta2 < 0 ? sin_beta1 : -sin_beta1;
}
}
else
{
if (std::abs(sin_beta2) == -sin_beta1)
{
cos_beta2 = cos_beta1;
}
}
CT const dn1 = sqrt(c1 + ep2 * math::sqr(sin_beta1));
CT const dn2 = sqrt(c1 + ep2 * math::sqr(sin_beta2));
CT sigma12;
CT m12x = c0;
CT s12x;
CT M21;
// Index zero element of coeffs_C1 is unused.
se::coeffs_C1<SeriesOrder, CT> const coeffs_C1(n);
bool meridian = lat1 == -90 || sin_lam12 == 0;
CT cos_alpha1, sin_alpha1;
CT cos_alpha2, sin_alpha2;
if (meridian)
{
// Endpoints lie on a single full meridian.
// Point to the target latitude.
cos_alpha1 = cos_lam12;
sin_alpha1 = sin_lam12;
// Heading north at the target.
cos_alpha2 = c1;
sin_alpha2 = c0;
CT sin_sigma1 = sin_beta1;
CT cos_sigma1 = cos_alpha1 * cos_beta1;
CT sin_sigma2 = sin_beta2;
CT cos_sigma2 = cos_alpha2 * cos_beta2;
sigma12 = std::atan2((std::max)(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);
CT dummy;
meridian_length(n, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
sin_sigma2, cos_sigma2, dn2,
cos_beta1, cos_beta2, s12x,
m12x, dummy, result.geodesic_scale,
M21, coeffs_C1);
if (sigma12 < c1 || m12x >= c0)
{
if (sigma12 < c3 * tiny)
{
sigma12 = m12x = s12x = c0;
}
m12x *= b;
s12x *= b;
}
else
{
// m12 < 0, i.e., prolate and too close to anti-podal.
meridian = false;
}
}
CT omega12;
if (!meridian && sin_beta1 == c0 &&
(f <= c0 || lon12_error >= f * c180))
{
// Points lie on the equator.
cos_alpha1 = cos_alpha2 = c0;
sin_alpha1 = sin_alpha2 = c1;
s12x = a * lam12;
sigma12 = omega12 = lam12 / one_minus_f;
m12x = b * sin(sigma12);
if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
result.geodesic_scale = cos(sigma12);
}
}
else if (!meridian)
{
// If point1 and point2 belong within a hemisphere bounded by a
// meridian and geodesic is neither meridional nor equatorial.
// Find the starting point for Newton's method.
CT dnm = c1;
sigma12 = newton_start(sin_beta1, cos_beta1, dn1,
sin_beta2, cos_beta2, dn2,
lam12, sin_lam12, cos_lam12,
sin_alpha1, cos_alpha1,
sin_alpha2, cos_alpha2,
dnm, coeffs_C1, ep2,
tol1, tol2, etol2,
n, f);
if (sigma12 >= c0)
{
// Short lines case (newton_start sets sin_alpha2, cos_alpha2, dnm).
s12x = sigma12 * b * dnm;
m12x = math::sqr(dnm) * b * sin(sigma12 / dnm);
if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
result.geodesic_scale = cos(sigma12 / dnm);
}
// Convert to radians.
omega12 = lam12 / (one_minus_f * dnm);
}
else
{
// Apply the Newton's method.
CT sin_sigma1 = c0, cos_sigma1 = c0;
CT sin_sigma2 = c0, cos_sigma2 = c0;
CT eps = c0, diff_omega12 = c0;
// Bracketing range.
CT sin_alpha1a = tiny, cos_alpha1a = c1;
CT sin_alpha1b = tiny, cos_alpha1b = -c1;
size_t iteration = 0;
size_t max_iterations = 20 + std::numeric_limits<size_t>::digits + 10;
for (bool tripn = false, tripb = false;
iteration < max_iterations;
++iteration)
{
CT dv = c0;
CT v = lambda12(sin_beta1, cos_beta1, dn1,
sin_beta2, cos_beta2, dn2,
sin_alpha1, cos_alpha1,
sin_lam12, cos_lam12,
sin_alpha2, cos_alpha2,
sigma12,
sin_sigma1, cos_sigma1,
sin_sigma2, cos_sigma2,
eps, diff_omega12,
iteration < max_iterations,
dv, f, n, ep2, tiny, coeffs_C1);
// Reversed test to allow escape with NaNs.
if (tripb || !(std::abs(v) >= (tripn ? c8 : c1) * tol0))
break;
// Update bracketing values.
if (v > c0 && (iteration > max_iterations ||
cos_alpha1 / sin_alpha1 > cos_alpha1b / sin_alpha1b))
{
sin_alpha1b = sin_alpha1;
cos_alpha1b = cos_alpha1;
}
else if (v < c0 && (iteration > max_iterations ||
cos_alpha1 / sin_alpha1 < cos_alpha1a / sin_alpha1a))
{
sin_alpha1a = sin_alpha1;
cos_alpha1a = cos_alpha1;
}
if (iteration < max_iterations && dv > c0)
{
CT diff_alpha1 = -v / dv;
CT sin_diff_alpha1 = sin(diff_alpha1);
CT cos_diff_alpha1 = cos(diff_alpha1);
CT nsin_alpha1 = sin_alpha1 * cos_diff_alpha1 +
cos_alpha1 * sin_diff_alpha1;
if (nsin_alpha1 > c0 && std::abs(diff_alpha1) < pi)
{
cos_alpha1 = cos_alpha1 * cos_diff_alpha1 - sin_alpha1 * sin_diff_alpha1;
sin_alpha1 = nsin_alpha1;
math::normalize_unit_vector<CT>(sin_alpha1, cos_alpha1);
// In some regimes we don't get quadratic convergence because
// slope -> 0. So use convergence conditions based on epsilon
// instead of sqrt(epsilon).
tripn = std::abs(v) <= c16 * tol0;
continue;
}
}
// Either dv was not positive or updated value was outside legal
// range. Use the midpoint of the bracket as the next estimate.
// This mechanism is not needed for the WGS84 ellipsoid, but it does
// catch problems with more eeccentric ellipsoids. Its efficacy is
// such for the WGS84 test set with the starting guess set to alp1 =
// 90deg:
// the WGS84 test set: mean = 5.21, sd = 3.93, max = 24
// WGS84 and random input: mean = 4.74, sd = 0.99
sin_alpha1 = (sin_alpha1a + sin_alpha1b) / c2;
cos_alpha1 = (cos_alpha1a + cos_alpha1b) / c2;
math::normalize_unit_vector<CT>(sin_alpha1, cos_alpha1);
tripn = false;
tripb = (std::abs(sin_alpha1a - sin_alpha1) + (cos_alpha1a - cos_alpha1) < tol_bisection ||
std::abs(sin_alpha1 - sin_alpha1b) + (cos_alpha1 - cos_alpha1b) < tol_bisection);
}
CT dummy;
se::coeffs_C1<SeriesOrder, CT> const coeffs_C1_eps(eps);
// Ensure that the reduced length and geodesic scale are computed in
// a "canonical" way, with the I2 integral.
meridian_length(eps, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
sin_sigma2, cos_sigma2, dn2,
cos_beta1, cos_beta2, s12x,
m12x, dummy, result.geodesic_scale,
M21, coeffs_C1_eps);
m12x *= b;
s12x *= b;
}
}
if (swap_point < 0)
{
boost::core::invoke_swap(sin_alpha1, sin_alpha2);
boost::core::invoke_swap(cos_alpha1, cos_alpha2);
boost::core::invoke_swap(result.geodesic_scale, M21);
}
sin_alpha1 *= swap_point * lon12_sign;
cos_alpha1 *= swap_point * lat_sign;
sin_alpha2 *= swap_point * lon12_sign;
cos_alpha2 *= swap_point * lat_sign;
if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
{
result.reduced_length = m12x;
}
if (BOOST_GEOMETRY_CONDITION(CalcAzimuths))
{
if (BOOST_GEOMETRY_CONDITION(CalcFwdAzimuth))
{
result.azimuth = atan2(sin_alpha1, cos_alpha1);
}
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
result.reverse_azimuth = atan2(sin_alpha2, cos_alpha2);
}
}
if (BOOST_GEOMETRY_CONDITION(EnableDistance))
{
result.distance = s12x;
}
return result;
}
template <typename CoeffsC1>
static inline void meridian_length(CT const& epsilon, CT const& ep2, CT const& sigma12,
CT const& sin_sigma1, CT const& cos_sigma1, CT const& dn1,
CT const& sin_sigma2, CT const& cos_sigma2, CT const& dn2,
CT const& cos_beta1, CT const& cos_beta2,
CT& s12x, CT& m12x, CT& m0,
CT& M12, CT& M21,
CoeffsC1 const& coeffs_C1)
{
static CT const c1 = 1;
CT A12x = 0, J12 = 0;
CT expansion_A1, expansion_A2;
// Evaluate the coefficients for C2.
se::coeffs_C2<SeriesOrder, CT> coeffs_C2(epsilon);
if (BOOST_GEOMETRY_CONDITION(EnableDistance) ||
BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
// Find the coefficients for A1 by computing the
// series expansion using Horner scehme.
expansion_A1 = se::evaluate_A1<SeriesOrder>(epsilon);
if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
// Find the coefficients for A2 by computing the
// series expansion using Horner scehme.
expansion_A2 = se::evaluate_A2<SeriesOrder>(epsilon);
A12x = expansion_A1 - expansion_A2;
expansion_A2 += c1;
}
expansion_A1 += c1;
}
if (BOOST_GEOMETRY_CONDITION(EnableDistance))
{
CT B1 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C1)
- se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C1);
s12x = expansion_A1 * (sigma12 + B1);
if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
CT B2 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)
- se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2);
J12 = A12x * sigma12 + (expansion_A1 * B1 - expansion_A2 * B2);
}
}
else if (BOOST_GEOMETRY_CONDITION(EnableReducedLength) ||
BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
for (size_t i = 1; i <= SeriesOrder; ++i)
{
coeffs_C2[i] = expansion_A1 * coeffs_C1[i] -
expansion_A2 * coeffs_C2[i];
}
J12 = A12x * sigma12 +
(se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C2)
- se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C2));
}
if (BOOST_GEOMETRY_CONDITION(EnableReducedLength))
{
m0 = A12x;
m12x = dn2 * (cos_sigma1 * sin_sigma2) -
dn1 * (sin_sigma1 * cos_sigma2) -
cos_sigma1 * cos_sigma2 * J12;
}
if (BOOST_GEOMETRY_CONDITION(EnableGeodesicScale))
{
CT cos_sigma12 = cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2;
CT t = ep2 * (cos_beta1 - cos_beta2) *
(cos_beta1 + cos_beta2) / (dn1 + dn2);
M12 = cos_sigma12 + (t * sin_sigma2 - cos_sigma2 * J12) * sin_sigma1 / dn1;
M21 = cos_sigma12 - (t * sin_sigma1 - cos_sigma1 * J12) * sin_sigma2 / dn2;
}
}
/*
Return a starting point for Newton's method in sin_alpha1 and
cos_alpha1 (function value is -1). If Newton's method
doesn't need to be used, return also sin_alpha2 and
cos_alpha2 and function value is sig12.
*/
template <typename CoeffsC1>
static inline CT newton_start(CT const& sin_beta1, CT const& cos_beta1, CT const& dn1,
CT const& sin_beta2, CT const& cos_beta2, CT dn2,
CT const& lam12, CT const& sin_lam12, CT const& cos_lam12,
CT& sin_alpha1, CT& cos_alpha1,
CT& sin_alpha2, CT& cos_alpha2,
CT& dnm, CoeffsC1 const& coeffs_C1, CT const& ep2,
CT const& tol1, CT const& tol2, CT const& etol2, CT const& n,
CT const& f)
{
static CT const c0 = 0;
static CT const c0_01 = 0.01;
static CT const c0_1 = 0.1;
static CT const c0_5 = 0.5;
static CT const c1 = 1;
static CT const c2 = 2;
static CT const c6 = 6;
static CT const c1000 = 1000;
static CT const pi = math::pi<CT>();
CT const one_minus_f = c1 - f;
CT const x_thresh = c1000 * tol2;
// Return a starting point for Newton's method in sin_alpha1
// and cos_alpha1 (function value is -1). If Newton's method
// doesn't need to be used, return also sin_alpha2 and
// cos_alpha2 and function value is sig12.
CT sig12 = -c1;
// bet12 = bet2 - bet1 in [0, pi); beta12a = bet2 + bet1 in (-pi, 0]
CT sin_beta12 = sin_beta2 * cos_beta1 - cos_beta2 * sin_beta1;
CT cos_beta12 = cos_beta2 * cos_beta1 + sin_beta2 * sin_beta1;
CT sin_beta12a = sin_beta2 * cos_beta1 + cos_beta2 * sin_beta1;
bool shortline = cos_beta12 >= c0 && sin_beta12 < c0_5 &&
cos_beta2 * lam12 < c0_5;
CT sin_omega12, cos_omega12;
if (shortline)
{
CT sin_beta_m2 = math::sqr(sin_beta1 + sin_beta2);
sin_beta_m2 /= sin_beta_m2 + math::sqr(cos_beta1 + cos_beta2);
dnm = math::sqrt(c1 + ep2 * sin_beta_m2);
CT omega12 = lam12 / (one_minus_f * dnm);
sin_omega12 = sin(omega12);
cos_omega12 = cos(omega12);
}
else
{
sin_omega12 = sin_lam12;
cos_omega12 = cos_lam12;
}
sin_alpha1 = cos_beta2 * sin_omega12;
cos_alpha1 = cos_omega12 >= c0 ?
sin_beta12 + cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 + cos_omega12) :
sin_beta12a - cos_beta2 * sin_beta1 * math::sqr(sin_omega12) / (c1 - cos_omega12);
CT sin_sigma12 = boost::math::hypot(sin_alpha1, cos_alpha1);
CT cos_sigma12 = sin_beta1 * sin_beta2 + cos_beta1 * cos_beta2 * cos_omega12;
if (shortline && sin_sigma12 < etol2)
{
sin_alpha2 = cos_beta1 * sin_omega12;
cos_alpha2 = sin_beta12 - cos_beta1 * sin_beta2 *
(cos_omega12 >= c0 ? math::sqr(sin_omega12) /
(c1 + cos_omega12) : c1 - cos_omega12);
math::normalize_unit_vector<CT>(sin_alpha2, cos_alpha2);
// Set return value.
sig12 = atan2(sin_sigma12, cos_sigma12);
}
// Skip astroid calculation if too eccentric.
else if (std::abs(n) > c0_1 ||
cos_sigma12 >= c0 ||
sin_sigma12 >= c6 * std::abs(n) * pi *
math::sqr(cos_beta1))
{
// Nothing to do, zeroth order spherical approximation will do.
}
else
{
// Scale lam12 and bet2 to x, y coordinate system where antipodal
// point is at origin and singular point is at y = 0, x = -1.
CT lambda_scale, beta_scale;
CT y;
volatile CT x;
CT lam12x = atan2(-sin_lam12, -cos_lam12);
if (f >= c0)
{
CT k2 = math::sqr(sin_beta1) * ep2;
CT eps = k2 / (c2 * (c1 + sqrt(c1 + k2)) + k2);
se::coeffs_A3<SeriesOrder, CT> const coeffs_A3(n);
CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());
lambda_scale = f * cos_beta1 * A3 * pi;
beta_scale = lambda_scale * cos_beta1;
x = lam12x / lambda_scale;
y = sin_beta12a / beta_scale;
}
else
{
CT cos_beta12a = cos_beta2 * cos_beta1 - sin_beta2 * sin_beta1;
CT beta12a = atan2(sin_beta12a, cos_beta12a);
CT m12b = c0;
CT m0 = c1;
CT dummy;
meridian_length(n, ep2, pi + beta12a,
sin_beta1, -cos_beta1, dn1,
sin_beta2, cos_beta2, dn2,
cos_beta1, cos_beta2, dummy,
m12b, m0, dummy, dummy, coeffs_C1);
x = -c1 + m12b / (cos_beta1 * cos_beta2 * m0 * pi);
beta_scale = x < -c0_01
? sin_beta12a / x
: -f * math::sqr(cos_beta1) * pi;
lambda_scale = beta_scale / cos_beta1;
y = lam12x / lambda_scale;
}
if (y > -tol1 && x > -c1 - x_thresh)
{
// Strip near cut.
if (f >= c0)
{
sin_alpha1 = (std::min)(c1, -CT(x));
cos_alpha1 = - math::sqrt(c1 - math::sqr(sin_alpha1));
}
else
{
cos_alpha1 = (std::max)(CT(x > -tol1 ? c0 : -c1), CT(x));
sin_alpha1 = math::sqrt(c1 - math::sqr(cos_alpha1));
}
}
else
{
// Solve the astroid problem.
CT k = astroid(CT(x), y);
CT omega12a = lambda_scale * (f >= c0 ? -x * k /
(c1 + k) : -y * (c1 + k) / k);
sin_omega12 = sin(omega12a);
cos_omega12 = -cos(omega12a);
// Update spherical estimate of alpha1 using omgega12 instead of lam12.
sin_alpha1 = cos_beta2 * sin_omega12;
cos_alpha1 = sin_beta12a - cos_beta2 * sin_beta1 *
math::sqr(sin_omega12) / (c1 - cos_omega12);
}
}
// Sanity check on starting guess. Backwards check allows NaN through.
if (!(sin_alpha1 <= c0))
{
math::normalize_unit_vector<CT>(sin_alpha1, cos_alpha1);
}
else
{
sin_alpha1 = c1;
cos_alpha1 = c0;
}
return sig12;
}
/*
Solve the astroid problem using the equation:
κ4 + 2κ3 + (1 x2 y 2 )κ2 2y 2 κ y 2 = 0.
For details, please refer to Eq. (65) in,
Geodesics on an ellipsoid of revolution, Charles F.F Karney,
https://arxiv.org/abs/1102.1215
*/
static inline CT astroid(CT const& x, CT const& y)
{
static CT const c0 = 0;
static CT const c1 = 1;
static CT const c2 = 2;
static CT const c3 = 3;
static CT const c4 = 4;
static CT const c6 = 6;
CT k;
CT p = math::sqr(x);
CT q = math::sqr(y);
CT r = (p + q - c1) / c6;
if (!(q == c0 && r <= c0))
{
// Avoid possible division by zero when r = 0 by multiplying
// equations for s and t by r^3 and r, respectively.
CT S = p * q / c4;
CT r2 = math::sqr(r);
CT r3 = r * r2;
// The discriminant of the quadratic equation for T3. This is
// zero on the evolute curve p^(1/3)+q^(1/3) = 1.
CT discriminant = S * (S + c2 * r3);
CT u = r;
if (discriminant >= c0)
{
CT T3 = S + r3;
// Pick the sign on the sqrt to maximize abs(T3). This minimizes
// loss of precision due to cancellation. The result is unchanged
// because of the way the T is used in definition of u.
T3 += T3 < c0 ? -std::sqrt(discriminant) : std::sqrt(discriminant);
CT T = std::cbrt(T3);
// T can be zero; but then r2 / T -> 0.
u += T + (T != c0 ? r2 / T : c0);
}
else
{
CT ang = std::atan2(std::sqrt(-discriminant), -(S + r3));
// There are three possible cube roots. We choose the root which avoids
// cancellation. Note that discriminant < 0 implies that r < 0.
u += c2 * r * cos(ang / c3);
}
CT v = std::sqrt(math::sqr(u) + q);
// Avoid loss of accuracy when u < 0.
CT uv = u < c0 ? q / (v - u) : u + v;
CT w = (uv - q) / (c2 * v);
// Rearrange expression for k to avoid loss of accuracy due to
// subtraction. Division by 0 not possible because uv > 0, w >= 0.
k = uv / (std::sqrt(uv + math::sqr(w)) + w);
}
else // q == 0 && r <= 0
{
// y = 0 with |x| <= 1. Handle this case directly.
// For y small, positive root is k = abs(y)/sqrt(1-x^2).
k = c0;
}
return k;
}
template <typename CoeffsC1>
static inline CT lambda12(CT const& sin_beta1, CT const& cos_beta1, CT const& dn1,
CT const& sin_beta2, CT const& cos_beta2, CT const& dn2,
CT const& sin_alpha1, CT cos_alpha1,
CT const& sin_lam120, CT const& cos_lam120,
CT& sin_alpha2, CT& cos_alpha2,
CT& sigma12,
CT& sin_sigma1, CT& cos_sigma1,
CT& sin_sigma2, CT& cos_sigma2,
CT& eps, CT& diff_omega12,
bool diffp, CT& diff_lam12,
CT const& f, CT const& n, CT const& ep2, CT const& tiny,
CoeffsC1 const& coeffs_C1)
{
static CT const c0 = 0;
static CT const c1 = 1;
static CT const c2 = 2;
CT const one_minus_f = c1 - f;
if (sin_beta1 == c0 && cos_alpha1 == c0)
{
// Break degeneracy of equatorial line.
cos_alpha1 = -tiny;
}
CT sin_alpha0 = sin_alpha1 * cos_beta1;
CT cos_alpha0 = boost::math::hypot(cos_alpha1, sin_alpha1 * sin_beta1);
CT sin_omega1, cos_omega1;
CT sin_omega2, cos_omega2;
CT sin_omega12, cos_omega12;
CT lam12;
sin_sigma1 = sin_beta1;
sin_omega1 = sin_alpha0 * sin_beta1;
cos_sigma1 = cos_omega1 = cos_alpha1 * cos_beta1;
math::normalize_unit_vector<CT>(sin_sigma1, cos_sigma1);
// Enforce symmetries in the case abs(beta2) = -beta1.
// Otherwise, this can yield singularities in the Newton iteration.
// sin(alpha2) * cos(beta2) = sin(alpha0).
sin_alpha2 = cos_beta2 != cos_beta1 ?
sin_alpha0 / cos_beta2 : sin_alpha1;
cos_alpha2 = cos_beta2 != cos_beta1 || std::abs(sin_beta2) != -sin_beta1 ?
sqrt(math::sqr(cos_alpha1 * cos_beta1) +
(cos_beta1 < -sin_beta1 ?
(cos_beta2 - cos_beta1) * (cos_beta1 + cos_beta2) :
(sin_beta1 - sin_beta2) * (sin_beta1 + sin_beta2))) / cos_beta2 :
std::abs(cos_alpha1);
sin_sigma2 = sin_beta2;
sin_omega2 = sin_alpha0 * sin_beta2;
cos_sigma2 = cos_omega2 =
(cos_alpha2 * cos_beta2);
// Break degeneracy of equatorial line.
math::normalize_unit_vector<CT>(sin_sigma2, cos_sigma2);
// sig12 = sig2 - sig1, limit to [0, pi].
sigma12 = atan2((std::max)(c0, cos_sigma1 * sin_sigma2 - sin_sigma1 * cos_sigma2),
cos_sigma1 * cos_sigma2 + sin_sigma1 * sin_sigma2);
// omg12 = omg2 - omg1, limit to [0, pi].
sin_omega12 = (std::max)(c0, cos_omega1 * sin_omega2 - sin_omega1 * cos_omega2);
cos_omega12 = cos_omega1 * cos_omega2 + sin_omega1 * sin_omega2;
// eta = omg12 - lam120.
CT eta = atan2(sin_omega12 * cos_lam120 - cos_omega12 * sin_lam120,
cos_omega12 * cos_lam120 + sin_omega12 * sin_lam120);
CT B312;
CT k2 = math::sqr(cos_alpha0) * ep2;
eps = k2 / (c2 * (c1 + std::sqrt(c1 + k2)) + k2);
se::coeffs_C3<SeriesOrder, CT> const coeffs_C3(n, eps);
B312 = se::sin_cos_series(sin_sigma2, cos_sigma2, coeffs_C3)
- se::sin_cos_series(sin_sigma1, cos_sigma1, coeffs_C3);
se::coeffs_A3<SeriesOrder, CT> const coeffs_A3(n);
CT const A3 = math::horner_evaluate(eps, coeffs_A3.begin(), coeffs_A3.end());
diff_omega12 = -f * A3 * sin_alpha0 * (sigma12 + B312);
lam12 = eta + diff_omega12;
if (diffp)
{
if (cos_alpha2 == c0)
{
diff_lam12 = - c2 * one_minus_f * dn1 / sin_beta1;
}
else
{
CT dummy;
meridian_length(eps, ep2, sigma12, sin_sigma1, cos_sigma1, dn1,
sin_sigma2, cos_sigma2, dn2,
cos_beta1, cos_beta2, dummy,
diff_lam12, dummy, dummy,
dummy, coeffs_C1);
diff_lam12 *= one_minus_f / (cos_alpha2 * cos_beta2);
}
}
return lam12;
}
};
} // namespace detail
/*!
\brief The solution of the inverse problem of geodesics on latlong coordinates,
after Karney (2011).
\author See
- Charles F.F Karney, Algorithms for geodesics, 2011
https://arxiv.org/pdf/1109.4448.pdf
*/
template <
typename CT,
bool EnableDistance,
bool EnableAzimuth,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false
>
struct karney_inverse
: detail::karney_inverse
<
CT,
EnableDistance,
EnableAzimuth,
EnableReverseAzimuth,
EnableReducedLength,
EnableGeodesicScale
>
{};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_KARNEY_INVERSE_HPP
+71
View File
@@ -0,0 +1,71 @@
// Boost.Geometry
// Copyright (c) 2017 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_MEAN_RADIUS_HPP
#define BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_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 mean_radius
: not_implemented<Tag>
{};
template <typename ResultType, typename Geometry>
struct mean_radius<ResultType, Geometry, srs_sphere_tag>
{
static inline ResultType apply(Geometry const& geometry)
{
return ResultType(get_radius<0>(geometry));
}
};
template <typename ResultType, typename Geometry>
struct mean_radius<ResultType, Geometry, srs_spheroid_tag>
{
static inline ResultType apply(Geometry const& geometry)
{
// (2*a + b) / 3
return (ResultType(2) * ResultType(get_radius<0>(geometry))
+ ResultType(get_radius<2>(geometry)))
/ ResultType(3);
}
};
} // namespace formula_dispatch
#endif // DOXYGEN_NO_DISPATCH
#ifndef DOXYGEN_NO_DETAIL
namespace formula
{
template <typename ResultType, typename Geometry>
inline ResultType mean_radius(Geometry const& geometry)
{
return formula_dispatch::mean_radius<ResultType, Geometry>::apply(geometry);
}
} // namespace formula
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_FORMULAS_MEAN_RADIUS_HPP
+171
View File
@@ -0,0 +1,171 @@
// Boost.Geometry
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2018 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_MERIDIAN_DIRECT_HPP
#define BOOST_GEOMETRY_FORMULAS_MERIDIAN_DIRECT_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/formulas/differential_quantities.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/meridian_inverse.hpp>
#include <boost/geometry/formulas/quarter_meridian.hpp>
#include <boost/geometry/formulas/result_direct.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief Compute the direct geodesic problem on a meridian
*/
template <
typename CT,
bool EnableCoordinates = true,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false,
unsigned int Order = 4
>
class meridian_direct
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
static const bool CalcCoordinates = EnableCoordinates || CalcRevAzimuth;
public:
typedef result_direct<CT> result_type;
template <typename T, typename Dist, typename Spheroid>
static inline result_type apply(T const& lo1,
T const& la1,
Dist const& distance,
bool north,
Spheroid const& spheroid)
{
result_type result;
CT const half_pi = math::half_pi<CT>();
CT const pi = math::pi<CT>();
CT const one_and_a_half_pi = pi + half_pi;
CT const c0 = 0;
CT azimuth = north ? c0 : pi;
if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
CT s0 = meridian_inverse<CT, Order>::apply(la1, spheroid);
int signed_distance = north ? distance : -distance;
result.lon2 = lo1;
result.lat2 = apply(s0 + signed_distance, spheroid);
}
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
result.reverse_azimuth = azimuth;
if (result.lat2 > half_pi &&
result.lat2 < one_and_a_half_pi)
{
result.reverse_azimuth = pi;
}
else if (result.lat2 > -one_and_a_half_pi &&
result.lat2 < -half_pi)
{
result.reverse_azimuth = c0;
}
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
CT const b = CT(get_radius<2>(spheroid));
CT const f = formula::flattening<CT>(spheroid);
boost::geometry::math::normalize_spheroidal_coordinates
<
boost::geometry::radian,
double
>(result.lon2, result.lat2);
typedef differential_quantities
<
CT,
EnableReducedLength,
EnableGeodesicScale,
Order
> quantities;
quantities::apply(lo1, la1, result.lon2, result.lat2,
azimuth, result.reverse_azimuth,
b, f,
result.reduced_length, result.geodesic_scale);
}
return result;
}
// https://en.wikipedia.org/wiki/Meridian_arc#The_inverse_meridian_problem_for_the_ellipsoid
// latitudes are assumed to be in radians and in [-pi/2,pi/2]
template <typename T, typename Spheroid>
static CT apply(T m, Spheroid const& spheroid)
{
CT const f = formula::flattening<CT>(spheroid);
CT n = f / (CT(2) - f);
CT mp = formula::quarter_meridian<CT>(spheroid);
CT mu = geometry::math::pi<CT>()/CT(2) * m / mp;
if (BOOST_GEOMETRY_CONDITION(Order == 0))
{
return mu;
}
CT H2 = 1.5 * n;
if (BOOST_GEOMETRY_CONDITION(Order == 1))
{
return mu + H2 * sin(2*mu);
}
CT n2 = n * n;
CT H4 = 1.3125 * n2;
if (BOOST_GEOMETRY_CONDITION(Order == 2))
{
return mu + H2 * sin(2*mu) + H4 * sin(4*mu);
}
CT n3 = n2 * n;
H2 -= 0.84375 * n3;
CT H6 = 1.572916667 * n3;
if (BOOST_GEOMETRY_CONDITION(Order == 3))
{
return mu + H2 * sin(2*mu) + H4 * sin(4*mu) + H6 * sin(6*mu);
}
CT n4 = n2 * n2;
H4 -= 1.71875 * n4;
CT H8 = 2.142578125 * n4;
// Order 4 or higher
return mu + H2 * sin(2*mu) + H4 * sin(4*mu) + H6 * sin(6*mu) + H8 * sin(8*mu);
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_MERIDIAN_DIRECT_HPP
+177
View File
@@ -0,0 +1,177 @@
// Boost.Geometry
// Copyright (c) 2023 Adam Wulkiewicz, Lodz, Poland.
// Copyright (c) 2017-2018 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_MERIDIAN_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_MERIDIAN_INVERSE_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/meridian_segment.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief Compute the arc length of an ellipse.
*/
template <typename CT, unsigned int Order = 1>
class meridian_inverse
{
public :
struct result
{
result()
: distance(0)
, meridian(false)
{}
CT distance;
bool meridian;
};
template <typename T>
static bool meridian_not_crossing_pole(T lat1, T lat2, CT diff)
{
CT half_pi = math::pi<CT>()/CT(2);
return math::equals(diff, CT(0)) ||
(math::equals(lat2, half_pi) && math::equals(lat1, -half_pi));
}
static bool meridian_crossing_pole(CT diff)
{
return math::equals(math::abs(diff), math::pi<CT>());
}
template <typename T, typename Spheroid>
static CT meridian_not_crossing_pole_dist(T lat1, T lat2, Spheroid const& spheroid)
{
return math::abs(apply(lat2, spheroid) - apply(lat1, spheroid));
}
template <typename T, typename Spheroid>
static CT meridian_crossing_pole_dist(T lat1, T lat2, Spheroid const& spheroid)
{
CT c0 = 0;
CT half_pi = math::pi<CT>()/CT(2);
CT lat_sign = 1;
if (lat1+lat2 < c0)
{
lat_sign = CT(-1);
}
return math::abs(lat_sign * CT(2) * apply(half_pi, spheroid)
- apply(lat1, spheroid) - apply(lat2, spheroid));
}
template <typename T, typename Spheroid>
static result apply(T lon1, T lat1, T lon2, T lat2, Spheroid const& spheroid)
{
result res;
CT diff = geometry::math::longitude_distance_signed<geometry::radian>(lon1, lon2);
if (lat1 > lat2)
{
std::swap(lat1, lat2);
}
if ( meridian_not_crossing_pole(lat1, lat2, diff) )
{
res.distance = meridian_not_crossing_pole_dist(lat1, lat2, spheroid);
res.meridian = true;
}
else if ( meridian_crossing_pole(diff) )
{
res.distance = meridian_crossing_pole_dist(lat1, lat2, spheroid);
res.meridian = true;
}
return res;
}
// Distance computation on meridians using series approximations
// to elliptic integrals. Formula to compute distance from lattitude 0 to lat
// https://en.wikipedia.org/wiki/Meridian_arc
// latitudes are assumed to be in radians and in [-pi/2,pi/2]
template <typename T, typename Spheroid>
static CT apply(T lat, Spheroid const& spheroid)
{
CT const a = get_radius<0>(spheroid);
CT const f = formula::flattening<CT>(spheroid);
CT n = f / (CT(2) - f);
CT M = a/(1+n);
CT C0 = 1;
if (BOOST_GEOMETRY_CONDITION(Order == 0))
{
return M * C0 * lat;
}
CT C2 = -1.5 * n;
if (BOOST_GEOMETRY_CONDITION(Order == 1))
{
return M * (C0 * lat + C2 * sin(2*lat));
}
CT n2 = n * n;
C0 += .25 * n2;
CT C4 = 0.9375 * n2;
if (BOOST_GEOMETRY_CONDITION(Order == 2))
{
return M * (C0 * lat + C2 * sin(2*lat) + C4 * sin(4*lat));
}
CT n3 = n2 * n;
C2 += 0.1875 * n3;
CT C6 = -0.729166667 * n3;
if (BOOST_GEOMETRY_CONDITION(Order == 3))
{
return M * (C0 * lat + C2 * sin(2*lat) + C4 * sin(4*lat)
+ C6 * sin(6*lat));
}
CT n4 = n2 * n2;
C4 -= 0.234375 * n4;
CT C8 = 0.615234375 * n4;
if (BOOST_GEOMETRY_CONDITION(Order == 4))
{
return M * (C0 * lat + C2 * sin(2*lat) + C4 * sin(4*lat)
+ C6 * sin(6*lat) + C8 * sin(8*lat));
}
CT n5 = n4 * n;
C6 += 0.227864583 * n5;
CT C10 = -0.54140625 * n5;
// Order 5 or higher
return M * (C0 * lat + C2 * sin(2*lat) + C4 * sin(4*lat)
+ C6 * sin(6*lat) + C8 * sin(8*lat) + C10 * sin(10*lat));
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_MERIDIAN_INVERSE_HPP
+72
View File
@@ -0,0 +1,72 @@
// Boost.Geometry
// Copyright (c) 2017-2018 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_MERIDIAN_SEGMENT_HPP
#define BOOST_GEOMETRY_FORMULAS_MERIDIAN_SEGMENT_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief Test if a segment is meridian or not.
*/
class meridian_segment
{
public :
enum SegmentType {NonMeridian, MeridianCrossingPole, MeridianNotCrossingPole};
template <typename T>
static inline SegmentType is_meridian(T lon1, T lat1, T lon2, T lat2)
{
SegmentType res = NonMeridian;
T diff = geometry::math::longitude_distance_signed<geometry::radian>(lon1, lon2);
if ( meridian_not_crossing_pole(lat1, lat2, diff) )
{
res = MeridianNotCrossingPole;
}
else if ( meridian_crossing_pole(diff) )
{
res = MeridianCrossingPole;
}
return res;
}
template <typename T>
static bool meridian_not_crossing_pole(T lat1, T lat2, T diff)
{
T half_pi = math::half_pi<T>();
return math::equals(diff, T(0)) ||
(math::equals(lat2, half_pi) && math::equals(lat1, -half_pi));
}
template <typename T>
static bool meridian_crossing_pole(T diff)
{
return math::equals(math::abs(diff), math::pi<T>());
}
};
}}} // namespace boost::geometry::formula
#endif //BOOST_GEOMETRY_FORMULAS_MERIDIAN_SEGMENT_HPP
+112
View File
@@ -0,0 +1,112 @@
// Boost.Geometry
// Copyright (c) 2018 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_QUARTER_MERIDIAN_HPP
#define BOOST_GEOMETRY_FORMULAS_QUARTER_MERIDIAN_HPP
#include <boost/geometry/algorithms/not_implemented.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/core/tag.hpp>
#include <boost/geometry/core/tags.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/util/math.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DISPATCH
namespace formula_dispatch
{
template <typename ResultType, typename Geometry, typename Tag = typename tag<Geometry>::type>
struct quarter_meridian
: not_implemented<Tag>
{};
template <typename ResultType, typename Geometry>
struct quarter_meridian<ResultType, Geometry, srs_spheroid_tag>
{
//https://en.wikipedia.org/wiki/Meridian_arc#Generalized_series
//http://www.wolframalpha.com/input/?i=(sum(((2*j-3)!!%2F(2*j)!!)%5E2*n%5E(2*j),j,0,8))
static inline ResultType apply(Geometry const& geometry)
{
//order 8 expansion
ResultType const C[] =
{
1073741824,
268435456,
16777216,
4194304,
1638400,
802816,
451584,
278784,
184041
};
ResultType const c2 = 2;
ResultType const c4 = 4;
ResultType const f = formula::flattening<ResultType>(geometry);
ResultType const n = f / (c2 - f);
ResultType const ab4 = (get_radius<0>(geometry)
+ get_radius<2>(geometry)) / c4;
return geometry::math::pi<ResultType>() * ab4 *
horner_evaluate(n*n, C, C+8) / C[0];
}
private :
//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);
if (begin == end)
{
return result;
}
IteratorType it = end;
do
{
result = result * x + *--it;
}
while (it != begin);
return result;
}
};
} // namespace formula_dispatch
#endif // DOXYGEN_NO_DISPATCH
#ifndef DOXYGEN_NO_DETAIL
namespace formula
{
template <typename ResultType, typename Geometry>
ResultType quarter_meridian(Geometry const& geometry)
{
return formula_dispatch::quarter_meridian<ResultType, Geometry>::apply(geometry);
}
} // namespace formula
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_FORMULAS_QUARTER_MERIDIAN_HPP
+39
View File
@@ -0,0 +1,39 @@
// 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_RESULT_DIRECT_HPP
#define BOOST_GEOMETRY_FORMULAS_RESULT_DIRECT_HPP
namespace boost { namespace geometry { namespace formula
{
template <typename T>
struct result_direct
{
result_direct()
: lon2(0)
, lat2(0)
, reverse_azimuth(0)
, reduced_length(0)
, geodesic_scale(1)
{}
T lon2;
T lat2;
T reverse_azimuth;
T reduced_length;
T geodesic_scale;
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_RESULT_DIRECT_HPP
+39
View File
@@ -0,0 +1,39 @@
// Boost.Geometry
// Copyright (c) 2015-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_RESULT_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_RESULT_INVERSE_HPP
namespace boost { namespace geometry { namespace formula
{
template <typename T>
struct result_inverse
{
result_inverse()
: distance(0)
, azimuth(0)
, reverse_azimuth(0)
, reduced_length(0)
, geodesic_scale(1)
{}
T distance;
T azimuth;
T reverse_azimuth;
T reduced_length;
T geodesic_scale;
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_RESULT_INVERSE_HPP
File diff suppressed because it is too large Load Diff
+285
View File
@@ -0,0 +1,285 @@
// Boost.Geometry
// Copyright (c) 2016-2020, 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_SPHERICAL_HPP
#define BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
#include <boost/geometry/core/coordinate_system.hpp>
#include <boost/geometry/core/coordinate_type.hpp>
#include <boost/geometry/core/cs.hpp>
#include <boost/geometry/core/access.hpp>
#include <boost/geometry/core/radian_access.hpp>
#include <boost/geometry/core/radius.hpp>
//#include <boost/geometry/arithmetic/arithmetic.hpp>
#include <boost/geometry/arithmetic/cross_product.hpp>
#include <boost/geometry/arithmetic/dot_product.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/util/select_coordinate_type.hpp>
#include <boost/geometry/formulas/result_direct.hpp>
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;
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);
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;
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);
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);
math::normalize_spheroidal_coordinates
<
typename geometry::detail::cs_angular_units<PointSph>::type,
coord_t
>(lon, lat);
set<0>(res, lon);
set<1>(res, lat);
return res;
}
// -1 right
// 1 left
// 0 on
template <typename Point3d1, typename Point3d2>
static inline int sph_side_value(Point3d1 const& norm, Point3d2 const& pt)
{
typedef typename select_coordinate_type<Point3d1, Point3d2>::type calc_t;
calc_t c0 = 0;
calc_t d = dot_product(norm, pt);
return math::equals(d, c0) ? 0
: d > c0 ? 1
: -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 c0 = 0;
T const pi = math::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]
math::detail::normalize_angle_loop<radian>(a_diff);
// 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, c0)
|| math::equals(a_diff, pi)
|| math::equals(a_diff, -pi) ? 0
: a_diff > 0 ? -1 // right
: 1; // left
}
template
<
bool Coordinates,
bool ReverseAzimuth,
typename CT,
typename Sphere
>
inline result_direct<CT> spherical_direct(CT const& lon1,
CT const& lat1,
CT const& sig12,
CT const& alp1,
Sphere const& sphere)
{
result_direct<CT> result;
CT const sin_alp1 = sin(alp1);
CT const sin_lat1 = sin(lat1);
CT const cos_alp1 = cos(alp1);
CT const cos_lat1 = cos(lat1);
CT const norm = math::sqrt(cos_alp1 * cos_alp1 + sin_alp1 * sin_alp1
* sin_lat1 * sin_lat1);
CT const alp0 = atan2(sin_alp1 * cos_lat1, norm);
CT const sig1 = atan2(sin_lat1, cos_alp1 * cos_lat1);
CT const sig2 = sig1 + sig12 / get_radius<0>(sphere);
CT const cos_sig2 = cos(sig2);
CT const sin_alp0 = sin(alp0);
CT const cos_alp0 = cos(alp0);
if (Coordinates)
{
CT const sin_sig2 = sin(sig2);
CT const sin_sig1 = sin(sig1);
CT const cos_sig1 = cos(sig1);
CT const norm2 = math::sqrt(cos_alp0 * cos_alp0 * cos_sig2 * cos_sig2
+ sin_alp0 * sin_alp0);
CT const lat2 = atan2(cos_alp0 * sin_sig2, norm2);
CT const omg1 = atan2(sin_alp0 * sin_sig1, cos_sig1);
CT const lon2 = atan2(sin_alp0 * sin_sig2, cos_sig2);
result.lon2 = lon1 + lon2 - omg1;
result.lat2 = lat2;
// For longitudes close to the antimeridian the result can be out
// of range. Therefore normalize.
math::detail::normalize_angle_cond<radian>(result.lon2);
}
if (ReverseAzimuth)
{
CT const alp2 = atan2(sin_alp0, cos_alp0 * cos_sig2);
result.reverse_azimuth = alp2;
}
return result;
}
} // namespace formula
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_FORMULAS_SPHERICAL_HPP
+274
View File
@@ -0,0 +1,274 @@
// Boost.Geometry
// Copyright (c) 2016-2020 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_THOMAS_DIRECT_HPP
#define BOOST_GEOMETRY_FORMULAS_THOMAS_DIRECT_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/assert.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/formulas/differential_quantities.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_direct.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief The solution of the direct problem of geodesics on latlong coordinates,
Forsyth-Andoyer-Lambert type approximation with first/second order terms.
\author See
- Technical Report: PAUL D. THOMAS, MATHEMATICAL MODELS FOR NAVIGATION SYSTEMS, 1965
http://www.dtic.mil/docs/citations/AD0627893
- Technical Report: PAUL D. THOMAS, SPHEROIDAL GEODESICS, REFERENCE SYSTEMS, AND LOCAL GEOMETRY, 1970
http://www.dtic.mil/docs/citations/AD0703541
*/
template <
typename CT,
bool SecondOrder = true,
bool EnableCoordinates = true,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false
>
class thomas_direct
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcCoordinates = EnableCoordinates || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcCoordinates || CalcQuantities;
public:
typedef result_direct<CT> result_type;
template <typename T, typename Dist, typename Azi, typename Spheroid>
static inline result_type apply(T const& lo1,
T const& la1,
Dist const& distance,
Azi const& azimuth12,
Spheroid const& spheroid)
{
result_type result;
CT const lon1 = lo1;
CT const lat1 = la1;
CT const c0 = 0;
CT const c1 = 1;
CT const c2 = 2;
CT const c4 = 4;
CT const a = CT(get_radius<0>(spheroid));
CT const b = CT(get_radius<2>(spheroid));
CT const f = formula::flattening<CT>(spheroid);
CT const one_minus_f = c1 - f;
CT const pi = math::pi<CT>();
CT const pi_half = pi / c2;
BOOST_GEOMETRY_ASSERT(-pi <= azimuth12 && azimuth12 <= pi);
// keep azimuth small - experiments show low accuracy
// if the azimuth is closer to (+-)180 deg.
CT azi12_alt = azimuth12;
CT lat1_alt = lat1;
bool alter_result = vflip_if_south(lat1, azimuth12, lat1_alt, azi12_alt);
CT const theta1 = math::equals(lat1_alt, pi_half) ? lat1_alt :
math::equals(lat1_alt, -pi_half) ? lat1_alt :
atan(one_minus_f * tan(lat1_alt));
CT const sin_theta1 = sin(theta1);
CT const cos_theta1 = cos(theta1);
CT const sin_a12 = sin(azi12_alt);
CT const cos_a12 = cos(azi12_alt);
CT const M = cos_theta1 * sin_a12; // cos_theta0
CT const theta0 = acos(M);
CT const sin_theta0 = sin(theta0);
CT const N = cos_theta1 * cos_a12;
CT const C1 = f * M; // lower-case c1 in the technical report
CT const C2 = f * (c1 - math::sqr(M)) / c4; // lower-case c2 in the technical report
CT D = 0;
CT P = 0;
if ( BOOST_GEOMETRY_CONDITION(SecondOrder) )
{
D = (c1 - C2) * (c1 - C2 - C1 * M);
P = C2 * (c1 + C1 * M / c2) / D;
}
else
{
D = c1 - c2 * C2 - C1 * M;
P = C2 / D;
}
// special case for equator:
// sin_theta0 = 0 <=> lat1 = 0 ^ |azimuth12| = pi/2
// NOTE: in this case it doesn't matter what's the value of cos_sigma1 because
// theta1=0, theta0=0, M=1|-1, C2=0 so X=0 and Y=0 so d_sigma=d
// cos_a12=0 so N=0, therefore
// lat2=0, azi21=pi/2|-pi/2
// d_eta = atan2(sin_d_sigma, cos_d_sigma)
// H = C1 * d_sigma
CT const cos_sigma1 = math::equals(sin_theta0, c0)
? c1
: normalized1_1(sin_theta1 / sin_theta0);
CT const sigma1 = acos(cos_sigma1);
CT const d = distance / (a * D);
CT const u = 2 * (sigma1 - d);
CT const cos_d = cos(d);
CT const sin_d = sin(d);
CT const cos_u = cos(u);
CT const sin_u = sin(u);
CT const W = c1 - c2 * P * cos_u;
CT const V = cos_u * cos_d - sin_u * sin_d;
CT const Y = c2 * P * V * W * sin_d;
CT X = 0;
CT d_sigma = d - Y;
if ( BOOST_GEOMETRY_CONDITION(SecondOrder) )
{
X = math::sqr(C2) * sin_d * cos_d * (2 * math::sqr(V) - c1);
d_sigma += X;
}
CT const sin_d_sigma = sin(d_sigma);
CT const cos_d_sigma = cos(d_sigma);
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
result.reverse_azimuth = atan2(M, N * cos_d_sigma - sin_theta1 * sin_d_sigma);
if (alter_result)
{
vflip_rev_azi(result.reverse_azimuth, azimuth12);
}
}
if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
CT const S_sigma = c2 * sigma1 - d_sigma;
CT cos_S_sigma = 0;
CT H = C1 * d_sigma;
if ( BOOST_GEOMETRY_CONDITION(SecondOrder) )
{
cos_S_sigma = cos(S_sigma);
H = H * (c1 - C2) - C1 * C2 * sin_d_sigma * cos_S_sigma;
}
CT const d_eta = atan2(sin_d_sigma * sin_a12, cos_theta1 * cos_d_sigma - sin_theta1 * sin_d_sigma * cos_a12);
CT const d_lambda = d_eta - H;
result.lon2 = lon1 + d_lambda;
if (! math::equals(M, c0))
{
CT const sin_a21 = sin(result.reverse_azimuth);
CT const tan_theta2 = (sin_theta1 * cos_d_sigma + N * sin_d_sigma) * sin_a21 / M;
result.lat2 = atan(tan_theta2 / one_minus_f);
}
else
{
CT const sigma2 = S_sigma - sigma1;
//theta2 = asin(cos(sigma2)) <=> sin_theta0 = 1
// NOTE: cos(sigma2) defines the sign of tan_theta2
CT const tan_theta2 = cos(sigma2) / math::abs(sin(sigma2));
result.lat2 = atan(tan_theta2 / one_minus_f);
}
if (alter_result)
{
result.lat2 = -result.lat2;
}
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities;
quantities::apply(lon1, lat1, result.lon2, result.lat2,
azimuth12, result.reverse_azimuth,
b, f,
result.reduced_length, result.geodesic_scale);
}
if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
// For longitudes close to the antimeridian the result can be out
// of range. Therefore normalize.
// It has to be done at the end because otherwise differential
// quantities are calculated incorrectly.
math::detail::normalize_angle_cond<radian>(result.lon2);
}
return result;
}
private:
static inline bool vflip_if_south(CT const& lat1, CT const& azi12, CT & lat1_alt, CT & azi12_alt)
{
CT const c2 = 2;
CT const pi = math::pi<CT>();
CT const pi_half = pi / c2;
if (azi12 > pi_half)
{
azi12_alt = pi - azi12;
lat1_alt = -lat1;
return true;
}
else if (azi12 < -pi_half)
{
azi12_alt = -pi - azi12;
lat1_alt = -lat1;
return true;
}
return false;
}
static inline void vflip_rev_azi(CT & rev_azi, CT const& azimuth12)
{
CT const c0 = 0;
CT const pi = math::pi<CT>();
if (rev_azi == c0)
{
rev_azi = azimuth12 >= 0 ? pi : -pi;
}
else if (rev_azi > c0)
{
rev_azi = pi - rev_azi;
}
else
{
rev_azi = -pi - rev_azi;
}
}
static inline CT normalized1_1(CT const& value)
{
CT const c1 = 1;
return value > c1 ? c1 :
value < -c1 ? -c1 :
value;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_THOMAS_DIRECT_HPP
+220
View File
@@ -0,0 +1,220 @@
// Boost.Geometry
// Copyright (c) 2015-2018 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_THOMAS_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_THOMAS_INVERSE_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/formulas/differential_quantities.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_inverse.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief The solution of the inverse problem of geodesics on latlong coordinates,
Forsyth-Andoyer-Lambert type approximation with second order terms.
\author See
- Technical Report: PAUL D. THOMAS, MATHEMATICAL MODELS FOR NAVIGATION SYSTEMS, 1965
http://www.dtic.mil/docs/citations/AD0627893
- Technical Report: PAUL D. THOMAS, SPHEROIDAL GEODESICS, REFERENCE SYSTEMS, AND LOCAL GEOMETRY, 1970
http://www.dtic.mil/docs/citations/AD0703541
*/
template <
typename CT,
bool EnableDistance,
bool EnableAzimuth,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false
>
class thomas_inverse
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
public:
typedef result_inverse<CT> result_type;
template <typename T1, typename T2, typename Spheroid>
static inline result_type apply(T1 const& lon1,
T1 const& lat1,
T2 const& lon2,
T2 const& lat2,
Spheroid const& spheroid)
{
result_type result;
// coordinates in radians
if ( math::equals(lon1, lon2) && math::equals(lat1, lat2) )
{
return result;
}
CT const c0 = 0;
CT const c1 = 1;
CT const c2 = 2;
CT const c4 = 4;
CT const pi_half = math::pi<CT>() / c2;
CT const f = formula::flattening<CT>(spheroid);
CT const one_minus_f = c1 - f;
// CT const tan_theta1 = one_minus_f * tan(lat1);
// CT const tan_theta2 = one_minus_f * tan(lat2);
// CT const theta1 = atan(tan_theta1);
// CT const theta2 = atan(tan_theta2);
CT const theta1 = math::equals(lat1, pi_half) ? lat1 :
math::equals(lat1, -pi_half) ? lat1 :
atan(one_minus_f * tan(lat1));
CT const theta2 = math::equals(lat2, pi_half) ? lat2 :
math::equals(lat2, -pi_half) ? lat2 :
atan(one_minus_f * tan(lat2));
CT const theta_m = (theta1 + theta2) / c2;
CT const d_theta_m = (theta2 - theta1) / c2;
CT const d_lambda = lon2 - lon1;
CT const d_lambda_m = d_lambda / c2;
CT const sin_theta_m = sin(theta_m);
CT const cos_theta_m = cos(theta_m);
CT const sin_d_theta_m = sin(d_theta_m);
CT const cos_d_theta_m = cos(d_theta_m);
CT const sin2_theta_m = math::sqr(sin_theta_m);
CT const cos2_theta_m = math::sqr(cos_theta_m);
CT const sin2_d_theta_m = math::sqr(sin_d_theta_m);
CT const cos2_d_theta_m = math::sqr(cos_d_theta_m);
CT const sin_d_lambda_m = sin(d_lambda_m);
CT const sin2_d_lambda_m = math::sqr(sin_d_lambda_m);
CT const H = cos2_theta_m - sin2_d_theta_m;
CT const L = sin2_d_theta_m + H * sin2_d_lambda_m;
CT const cos_d = c1 - c2 * L;
CT const d = acos(cos_d);
CT const sin_d = sin(d);
CT const one_minus_L = c1 - L;
if ( math::equals(sin_d, c0)
|| math::equals(L, c0)
|| math::equals(one_minus_L, c0) )
{
return result;
}
CT const U = c2 * sin2_theta_m * cos2_d_theta_m / one_minus_L;
CT const V = c2 * sin2_d_theta_m * cos2_theta_m / L;
CT const X = U + V;
CT const Y = U - V;
CT const T = d / sin_d;
CT const D = c4 * math::sqr(T);
CT const E = c2 * cos_d;
CT const A = D * E;
CT const B = c2 * D;
CT const C = T - (A - E) / c2;
CT const f_sqr = math::sqr(f);
CT const f_sqr_per_64 = f_sqr / CT(64);
if ( BOOST_GEOMETRY_CONDITION(EnableDistance) )
{
CT const n1 = X * (A + C*X);
CT const n2 = Y * (B + E*Y);
CT const n3 = D*X*Y;
CT const delta1d = f * (T*X-Y) / c4;
CT const delta2d = f_sqr_per_64 * (n1 - n2 + n3);
CT const a = get_radius<0>(spheroid);
//result.distance = a * sin_d * (T - delta1d);
result.distance = a * sin_d * (T - delta1d + delta2d);
}
if ( BOOST_GEOMETRY_CONDITION(CalcAzimuths) )
{
// NOTE: if both cos_latX == 0 then below we'd have 0 * INF
// it's a situation when the endpoints are on the poles +-90 deg
// in this case the azimuth could either be 0 or +-pi
// but above always 0 is returned
CT const F = c2*Y-E*(c4-X);
CT const M = CT(32)*T-(CT(20)*T-A)*X-(B+c4)*Y;
CT const G = f*T/c2 + f_sqr_per_64 * M;
// TODO:
// If d_lambda is close to 90 or -90 deg then tan(d_lambda) is big
// and F is small. The result is not accurate.
// In the edge case the result may be 2 orders of magnitude less
// accurate than Andoyer's.
CT const tan_d_lambda = tan(d_lambda);
CT const Q = -(F*G*tan_d_lambda) / c4;
CT const d_lambda_m_p = (d_lambda + Q) / c2;
CT const tan_d_lambda_m_p = tan(d_lambda_m_p);
CT const v = atan2(cos_d_theta_m, sin_theta_m * tan_d_lambda_m_p);
CT const u = atan2(-sin_d_theta_m, cos_theta_m * tan_d_lambda_m_p);
CT const pi = math::pi<CT>();
if (BOOST_GEOMETRY_CONDITION(CalcFwdAzimuth))
{
CT alpha1 = v + u;
if (alpha1 > pi)
{
alpha1 -= c2 * pi;
}
result.azimuth = alpha1;
}
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
CT alpha2 = pi - (v - u);
if (alpha2 > pi)
{
alpha2 -= c2 * pi;
}
result.reverse_azimuth = alpha2;
}
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities;
quantities::apply(lon1, lat1, lon2, lat2,
result.azimuth, result.reverse_azimuth,
get_radius<2>(spheroid), f,
result.reduced_length, result.geodesic_scale);
}
return result;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_THOMAS_INVERSE_HPP
+43
View File
@@ -0,0 +1,43 @@
// Boost.Geometry
// Copyright (c) 2017 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_UNIT_SPHEROID_HPP
#define BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP
#include <boost/geometry/core/radius.hpp>
namespace boost { namespace geometry
{
#ifndef DOXYGEN_NO_DETAIL
namespace formula
{
template <typename ResultType, typename Spheroid>
inline ResultType unit_spheroid_b(Spheroid const& spheroid)
{
return ResultType(get_radius<2>(spheroid))
/ ResultType(get_radius<0>(spheroid));
}
template <typename ResultSpheroid, typename Spheroid>
inline ResultSpheroid unit_spheroid(Spheroid const& spheroid)
{
typedef typename radius_type<ResultSpheroid>::type radius_t;
return ResultSpheroid(radius_t(1),
unit_spheroid_b<radius_t>(spheroid));
}
} // namespace formula
#endif // DOXYGEN_NO_DETAIL
}} // namespace boost::geometry
#endif // BOOST_GEOMETRY_FORMULAS_UNIT_SPHEROID_HPP
+147
View File
@@ -0,0 +1,147 @@
// Boost.Geometry
// Copyright (c) 2016-2020 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/static_assert.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/spherical.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),
637644, 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),
* 637644, 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_GEOMETRY_STATIC_ASSERT_FALSE(
"Not implemented for this coordinate system.",
CT, 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
+352
View File
@@ -0,0 +1,352 @@
// Boost.Geometry
// Copyright (c) 2016-2020 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_LONGITUDE_HPP
#define BOOST_GEOMETRY_FORMULAS_MAXIMUM_LONGITUDE_HPP
#include <boost/geometry/core/static_assert.hpp>
#include <boost/geometry/formulas/spherical.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/math/special_functions/hypot.hpp>
namespace boost { namespace geometry { namespace formula
{
/*!
\brief Algorithm to compute the vertex longitude of a geodesic segment. Vertex is
a point on the geodesic that maximizes (or minimizes) the latitude. The algorithm
is given the vertex latitude.
*/
//Classes for spesific CS
template <typename CT>
class vertex_longitude_on_sphere
{
public:
template <typename T>
static inline CT apply(T const& lat1, //segment point 1
T const& lat2, //segment point 2
T const& lat3, //vertex latitude
T const& sin_l12,
T const& cos_l12) //lon1 -lon2
{
//https://en.wikipedia.org/wiki/Great-circle_navigation#Finding_way-points
CT const A = sin(lat1) * cos(lat2) * cos(lat3) * sin_l12;
CT const B = sin(lat1) * cos(lat2) * cos(lat3) * cos_l12
- cos(lat1) * sin(lat2) * cos(lat3);
CT lon = atan2(B, A);
return lon + math::pi<CT>();
}
};
template <typename CT>
class vertex_longitude_on_spheroid
{
template<typename T>
static inline void normalize(T& x, T& y)
{
T h = boost::math::hypot(x, y);
x /= h;
y /= h;
}
public:
template <typename T, typename Spheroid>
static inline CT apply(T const& lat1, //segment point 1
T const& lat2, //segment point 2
T const& lat3, //vertex latitude
T& alp1,
Spheroid const& spheroid)
{
// We assume that segment points lay on different side w.r.t.
// the vertex
// Constants
CT const c0 = 0;
CT const c2 = 2;
CT const half_pi = math::pi<CT>() / c2;
if (math::equals(lat1, half_pi)
|| math::equals(lat2, half_pi)
|| math::equals(lat1, -half_pi)
|| math::equals(lat2, -half_pi))
{
// one segment point is the pole
return c0;
}
// More constants
CT const f = flattening<CT>(spheroid);
CT const pi = math::pi<CT>();
CT const c1 = 1;
CT const cminus1 = -1;
// First, compute longitude on auxiliary sphere
CT const one_minus_f = c1 - f;
CT const bet1 = atan(one_minus_f * tan(lat1));
CT const bet2 = atan(one_minus_f * tan(lat2));
CT const bet3 = atan(one_minus_f * tan(lat3));
CT cos_bet1 = cos(bet1);
CT cos_bet2 = cos(bet2);
CT const sin_bet1 = sin(bet1);
CT const sin_bet2 = sin(bet2);
CT const sin_bet3 = sin(bet3);
CT omg12 = 0;
if (bet1 < c0)
{
cos_bet1 *= cminus1;
omg12 += pi;
}
if (bet2 < c0)
{
cos_bet2 *= cminus1;
omg12 += pi;
}
CT const sin_alp1 = sin(alp1);
CT const cos_alp1 = math::sqrt(c1 - math::sqr(sin_alp1));
CT const norm = math::sqrt(math::sqr(cos_alp1) + math::sqr(sin_alp1 * sin_bet1));
CT const sin_alp0 = sin(atan2(sin_alp1 * cos_bet1, norm));
BOOST_ASSERT(cos_bet2 != c0);
CT const sin_alp2 = sin_alp1 * cos_bet1 / cos_bet2;
CT const cos_alp0 = math::sqrt(c1 - math::sqr(sin_alp0));
CT const cos_alp2 = math::sqrt(c1 - math::sqr(sin_alp2));
CT const sig1 = atan2(sin_bet1, cos_alp1 * cos_bet1);
CT const sig2 = atan2(sin_bet2, -cos_alp2 * cos_bet2); //lat3 is a vertex
CT const cos_sig1 = cos(sig1);
CT const sin_sig1 = math::sqrt(c1 - math::sqr(cos_sig1));
CT const cos_sig2 = cos(sig2);
CT const sin_sig2 = math::sqrt(c1 - math::sqr(cos_sig2));
CT const omg1 = atan2(sin_alp0 * sin_sig1, cos_sig1);
CT const omg2 = atan2(sin_alp0 * sin_sig2, cos_sig2);
omg12 += omg1 - omg2;
CT const sin_omg12 = sin(omg12);
CT const cos_omg12 = cos(omg12);
CT omg13 = geometry::formula::vertex_longitude_on_sphere<CT>
::apply(bet1, bet2, bet3, sin_omg12, cos_omg12);
if (lat1 * lat2 < c0)//different hemispheres
{
if ((lat2 - lat1) * lat3 > c0)// ascending segment
{
omg13 = pi - omg13;
}
}
// Second, compute the ellipsoidal longitude
CT const e2 = f * (c2 - f);
CT const ep = math::sqrt(e2 / (c1 - e2));
CT const k2 = math::sqr(ep * cos_alp0);
CT const sqrt_k2_plus_one = math::sqrt(c1 + k2);
CT const eps = (sqrt_k2_plus_one - c1) / (sqrt_k2_plus_one + c1);
CT const eps2 = eps * eps;
CT const n = f / (c2 - f);
// sig3 is the length from equator to the vertex
CT sig3;
if(sin_bet3 > c0)
{
sig3 = half_pi;
} else {
sig3 = -half_pi;
}
CT const cos_sig3 = 0;
CT const sin_sig3 = 1;
CT sig13 = sig3 - sig1;
if (sig13 > pi)
{
sig13 -= 2 * pi;
}
// Order 2 approximation
CT const c1over2 = 0.5;
CT const c1over4 = 0.25;
CT const c1over8 = 0.125;
CT const c1over16 = 0.0625;
CT const c4 = 4;
CT const c8 = 8;
CT const A3 = 1 - (c1over2 - c1over2 * n) * eps - c1over4 * eps2;
CT const C31 = (c1over4 - c1over4 * n) * eps + c1over8 * eps2;
CT const C32 = c1over16 * eps2;
CT const sin2_sig3 = c2 * cos_sig3 * sin_sig3;
CT const sin4_sig3 = sin_sig3 * (-c4 * cos_sig3
+ c8 * cos_sig3 * cos_sig3 * cos_sig3);
CT const sin2_sig1 = c2 * cos_sig1 * sin_sig1;
CT const sin4_sig1 = sin_sig1 * (-c4 * cos_sig1
+ c8 * cos_sig1 * cos_sig1 * cos_sig1);
CT const I3 = A3 * (sig13
+ C31 * (sin2_sig3 - sin2_sig1)
+ C32 * (sin4_sig3 - sin4_sig1));
CT const sign = bet3 >= c0
? c1
: cminus1;
CT const dlon_max = omg13 - sign * f * sin_alp0 * I3;
return dlon_max;
}
};
//CS_tag dispatching
template <typename CT, typename CS_Tag>
struct compute_vertex_lon
{
BOOST_GEOMETRY_STATIC_ASSERT_FALSE(
"Not implemented for this coordinate system.",
CT, CS_Tag);
};
template <typename CT>
struct compute_vertex_lon<CT, spherical_equatorial_tag>
{
template <typename Strategy>
static inline CT apply(CT const& lat1,
CT const& lat2,
CT const& vertex_lat,
CT const& sin_l12,
CT const& cos_l12,
CT,
Strategy)
{
return vertex_longitude_on_sphere<CT>
::apply(lat1,
lat2,
vertex_lat,
sin_l12,
cos_l12);
}
};
template <typename CT>
struct compute_vertex_lon<CT, geographic_tag>
{
template <typename Strategy>
static inline CT apply(CT const& lat1,
CT const& lat2,
CT const& vertex_lat,
CT,
CT,
CT& alp1,
Strategy const& azimuth_strategy)
{
return vertex_longitude_on_spheroid<CT>
::apply(lat1,
lat2,
vertex_lat,
alp1,
azimuth_strategy.model());
}
};
// Vertex longitude interface
// Assume that lon1 < lon2 and vertex_lat is the latitude of the vertex
template <typename CT, typename CS_Tag>
class vertex_longitude
{
public :
template <typename Strategy>
static inline CT apply(CT& lon1,
CT& lat1,
CT& lon2,
CT& lat2,
CT const& vertex_lat,
CT& alp1,
Strategy const& azimuth_strategy)
{
CT const c0 = 0;
CT pi = math::pi<CT>();
//Vertex is a segment's point
if (math::equals(vertex_lat, lat1))
{
return lon1;
}
if (math::equals(vertex_lat, lat2))
{
return lon2;
}
//Segment lay on meridian
if (math::equals(lon1, lon2))
{
return (std::max)(lat1, lat2);
}
BOOST_ASSERT(lon1 < lon2);
CT dlon = compute_vertex_lon<CT, CS_Tag>::apply(lat1, lat2,
vertex_lat,
sin(lon1 - lon2),
cos(lon1 - lon2),
alp1,
azimuth_strategy);
CT vertex_lon = std::fmod(lon1 + dlon, 2 * pi);
if (vertex_lat < c0)
{
vertex_lon -= pi;
}
if (std::abs(lon1 - lon2) > pi)
{
vertex_lon -= pi;
}
return vertex_lon;
}
};
template <typename CT>
class vertex_longitude<CT, cartesian_tag>
{
public :
template <typename Strategy>
static inline CT apply(CT& /*lon1*/,
CT& /*lat1*/,
CT& lon2,
CT& /*lat2*/,
CT const& /*vertex_lat*/,
CT& /*alp1*/,
Strategy const& /*azimuth_strategy*/)
{
return lon2;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_MAXIMUM_LONGITUDE_HPP
+186
View File
@@ -0,0 +1,186 @@
// Boost.Geometry
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// This file was modified by Oracle on 2014-2020.
// Modifications copyright (c) 2014-2020 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_VINCENTY_DIRECT_HPP
#define BOOST_GEOMETRY_FORMULAS_VINCENTY_DIRECT_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/util/normalize_spheroidal_coordinates.hpp>
#include <boost/geometry/formulas/differential_quantities.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_direct.hpp>
#ifndef BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS
#define BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS 1000
#endif
namespace boost { namespace geometry { namespace formula
{
/*!
\brief The solution of the direct 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
\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
- http://futureboy.homeip.net/fsp/colorize.fsp?fileName=navigation.frink
*/
template <
typename CT,
bool EnableCoordinates = true,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false
>
class vincenty_direct
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcCoordinates = EnableCoordinates || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
public:
typedef result_direct<CT> result_type;
template <typename T, typename Dist, typename Azi, typename Spheroid>
static inline result_type apply(T const& lo1,
T const& la1,
Dist const& distance,
Azi const& azimuth12,
Spheroid const& spheroid)
{
result_type result;
CT const lon1 = lo1;
CT const lat1 = la1;
CT const radius_a = CT(get_radius<0>(spheroid));
CT const radius_b = CT(get_radius<2>(spheroid));
CT const flattening = formula::flattening<CT>(spheroid);
CT const sin_azimuth12 = sin(azimuth12);
CT const cos_azimuth12 = cos(azimuth12);
// U: reduced latitude, defined by tan U = (1-f) tan phi
CT const one_min_f = CT(1) - flattening;
CT const tan_U1 = one_min_f * tan(lat1);
CT const sigma1 = atan2(tan_U1, cos_azimuth12); // (1)
// may be calculated from tan using 1 sqrt()
CT const U1 = atan(tan_U1);
CT const sin_U1 = sin(U1);
CT const cos_U1 = cos(U1);
CT const sin_alpha = cos_U1 * sin_azimuth12; // (2)
CT const sin_alpha_sqr = math::sqr(sin_alpha);
CT const cos_alpha_sqr = CT(1) - sin_alpha_sqr;
CT const b_sqr = radius_b * radius_b;
CT const u_sqr = cos_alpha_sqr * (radius_a * radius_a - b_sqr) / b_sqr;
CT const A = CT(1) + (u_sqr/CT(16384)) * (CT(4096) + u_sqr*(CT(-768) + u_sqr*(CT(320) - u_sqr*CT(175)))); // (3)
CT const B = (u_sqr/CT(1024))*(CT(256) + u_sqr*(CT(-128) + u_sqr*(CT(74) - u_sqr*CT(47)))); // (4)
CT s_div_bA = distance / (radius_b * A);
CT sigma = s_div_bA; // (7)
CT previous_sigma;
CT sin_sigma;
CT cos_sigma;
CT cos_2sigma_m;
CT cos_2sigma_m_sqr;
int counter = 0; // robustness
do
{
previous_sigma = sigma;
CT const two_sigma_m = CT(2) * sigma1 + sigma; // (5)
sin_sigma = sin(sigma);
cos_sigma = cos(sigma);
CT const sin_sigma_sqr = math::sqr(sin_sigma);
cos_2sigma_m = cos(two_sigma_m);
cos_2sigma_m_sqr = math::sqr(cos_2sigma_m);
CT const delta_sigma = B * sin_sigma * (cos_2sigma_m
+ (B/CT(4)) * ( cos_sigma * (CT(-1) + CT(2)*cos_2sigma_m_sqr)
- (B/CT(6) * cos_2sigma_m * (CT(-3)+CT(4)*sin_sigma_sqr) * (CT(-3)+CT(4)*cos_2sigma_m_sqr)) )); // (6)
sigma = s_div_bA + delta_sigma; // (7)
++counter; // robustness
} while ( geometry::math::abs(previous_sigma - sigma) > CT(1e-12)
//&& geometry::math::abs(sigma) < pi
&& counter < BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS ); // robustness
if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
result.lat2
= atan2( sin_U1 * cos_sigma + cos_U1 * sin_sigma * cos_azimuth12,
one_min_f * math::sqrt(sin_alpha_sqr + math::sqr(sin_U1 * sin_sigma - cos_U1 * cos_sigma * cos_azimuth12))); // (8)
CT const lambda = atan2( sin_sigma * sin_azimuth12,
cos_U1 * cos_sigma - sin_U1 * sin_sigma * cos_azimuth12); // (9)
CT const C = (flattening/CT(16)) * cos_alpha_sqr * ( CT(4) + flattening * ( CT(4) - CT(3) * cos_alpha_sqr ) ); // (10)
CT const L = lambda - (CT(1) - C) * flattening * sin_alpha
* ( sigma + C * sin_sigma * ( cos_2sigma_m + C * cos_sigma * ( CT(-1) + CT(2) * cos_2sigma_m_sqr ) ) ); // (11)
result.lon2 = lon1 + L;
}
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
result.reverse_azimuth
= atan2(sin_alpha, -sin_U1 * sin_sigma + cos_U1 * cos_sigma * cos_azimuth12); // (12)
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities;
quantities::apply(lon1, lat1, result.lon2, result.lat2,
azimuth12, result.reverse_azimuth,
radius_b, flattening,
result.reduced_length, result.geodesic_scale);
}
if (BOOST_GEOMETRY_CONDITION(CalcCoordinates))
{
// For longitudes close to the antimeridian the result can be out
// of range. Therefore normalize.
// It has to be done at the end because otherwise differential
// quantities are calculated incorrectly.
math::detail::normalize_angle_cond<radian>(result.lon2);
}
return result;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_VINCENTY_DIRECT_HPP
+220
View File
@@ -0,0 +1,220 @@
// Boost.Geometry
// Copyright (c) 2007-2012 Barend Gehrels, Amsterdam, the Netherlands.
// Copyright (c) 2018 Adam Wulkiewicz, Lodz, Poland.
// 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
// 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_VINCENTY_INVERSE_HPP
#define BOOST_GEOMETRY_FORMULAS_VINCENTY_INVERSE_HPP
#include <boost/math/constants/constants.hpp>
#include <boost/geometry/core/radius.hpp>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>
#include <boost/geometry/formulas/differential_quantities.hpp>
#include <boost/geometry/formulas/flattening.hpp>
#include <boost/geometry/formulas/result_inverse.hpp>
#ifndef BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS
#define BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS 1000
#endif
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/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
- http://futureboy.homeip.net/fsp/colorize.fsp?fileName=navigation.frink
*/
template <
typename CT,
bool EnableDistance,
bool EnableAzimuth,
bool EnableReverseAzimuth = false,
bool EnableReducedLength = false,
bool EnableGeodesicScale = false
>
struct vincenty_inverse
{
static const bool CalcQuantities = EnableReducedLength || EnableGeodesicScale;
static const bool CalcAzimuths = EnableAzimuth || EnableReverseAzimuth || CalcQuantities;
static const bool CalcFwdAzimuth = EnableAzimuth || CalcQuantities;
static const bool CalcRevAzimuth = EnableReverseAzimuth || CalcQuantities;
public:
typedef result_inverse<CT> result_type;
template <typename T1, typename T2, typename Spheroid>
static inline result_type apply(T1 const& lon1,
T1 const& lat1,
T2 const& lon2,
T2 const& lat2,
Spheroid const& spheroid)
{
result_type result;
if (math::equals(lat1, lat2) && math::equals(lon1, lon2))
{
return result;
}
CT const c0 = 0;
CT const c1 = 1;
CT const c2 = 2;
CT const c3 = 3;
CT const c4 = 4;
CT const c16 = 16;
CT const c_e_12 = CT(1e-12);
CT const pi = geometry::math::pi<CT>();
CT const two_pi = c2 * pi;
// lambda: difference in longitude on an auxiliary sphere
CT L = lon2 - lon1;
CT lambda = L;
if (L < -pi) L += two_pi;
if (L > pi) L -= two_pi;
CT const radius_a = CT(get_radius<0>(spheroid));
CT const radius_b = CT(get_radius<2>(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 - f;
CT const tan_U1 = one_min_f * tan(lat1); // above (1)
CT const tan_U2 = one_min_f * tan(lat2); // above (1)
// calculate sin U and cos U using trigonometric identities
CT const temp_den_U1 = math::sqrt(c1 + math::sqr(tan_U1));
CT const temp_den_U2 = math::sqrt(c1 + math::sqr(tan_U2));
// cos = 1 / sqrt(1 + tan^2)
CT const cos_U1 = c1 / temp_den_U1;
CT const cos_U2 = c1 / temp_den_U2;
// sin = tan / sqrt(1 + tan^2)
// 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);
//CT const U2 = atan(tan_U2);
//cos_U1 = cos(U1);
//cos_U2 = cos(U2);
//sin_U1 = tan_U1 * cos_U1; // sin(U1);
//sin_U2 = tan_U2 * cos_U2; // sin(U2);
CT previous_lambda;
CT sin_lambda;
CT cos_lambda;
CT sin_sigma;
CT sin_alpha;
CT cos2_alpha;
CT cos_2sigma_m;
CT cos2_2sigma_m;
CT sigma;
int counter = 0; // robustness
do
{
previous_lambda = lambda; // (13)
sin_lambda = sin(lambda);
cos_lambda = cos(lambda);
sin_sigma = math::sqrt(math::sqr(cos_U2 * sin_lambda) + math::sqr(cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda)); // (14)
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);
cos_2sigma_m = math::equals(cos2_alpha, c0) ? c0 : cos_sigma - c2 * sin_U1 * sin_U2 / cos2_alpha; // (18)
cos2_2sigma_m = math::sqr(cos_2sigma_m);
CT C = f/c16 * cos2_alpha * (c4 + f * (c4 - c3 * cos2_alpha)); // (10)
sigma = atan2(sin_sigma, cos_sigma); // (16)
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
} while ( geometry::math::abs(previous_lambda - lambda) > c_e_12
&& geometry::math::abs(lambda) < pi
&& counter < BOOST_GEOMETRY_DETAIL_VINCENTY_MAX_STEPS ); // robustness
if ( BOOST_GEOMETRY_CONDITION(EnableDistance) )
{
// Some types cannot divide by doubles
CT const c6 = 6;
CT const c47 = 47;
CT const c74 = 74;
CT const c128 = 128;
CT const c256 = 256;
CT const c175 = 175;
CT const c320 = 320;
CT const c768 = 768;
CT const c1024 = 1024;
CT const c4096 = 4096;
CT const c16384 = 16384;
//CT sqr_u = cos2_alpha * (math::sqr(radius_a) - math::sqr(radius_b)) / math::sqr(radius_b); // above (1)
CT sqr_u = cos2_alpha * ( math::sqr(radius_a / radius_b) - c1 ); // above (1)
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 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)
}
if ( BOOST_GEOMETRY_CONDITION(CalcAzimuths) )
{
if (BOOST_GEOMETRY_CONDITION(CalcFwdAzimuth))
{
result.azimuth = atan2(cos_U2 * sin_lambda, cos_U1 * sin_U2 - sin_U1 * cos_U2 * cos_lambda); // (20)
}
if (BOOST_GEOMETRY_CONDITION(CalcRevAzimuth))
{
result.reverse_azimuth = atan2(cos_U1 * sin_lambda, -sin_U1 * cos_U2 + cos_U1 * sin_U2 * cos_lambda); // (21)
}
}
if (BOOST_GEOMETRY_CONDITION(CalcQuantities))
{
typedef differential_quantities<CT, EnableReducedLength, EnableGeodesicScale, 2> quantities;
quantities::apply(lon1, lat1, lon2, lat2,
result.azimuth, result.reverse_azimuth,
radius_b, f,
result.reduced_length, result.geodesic_scale);
}
return result;
}
};
}}} // namespace boost::geometry::formula
#endif // BOOST_GEOMETRY_FORMULAS_VINCENTY_INVERSE_HPP