mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-08 08:27:50 +00:00
Added thirdparty: boost library
This commit is contained in:
+293
@@ -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
@@ -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):
|
||||
61–66, 1989
|
||||
- Charles F.F Karney, Algorithms for geodesics, 2011
|
||||
https://arxiv.org/pdf/1109.4448.pdf
|
||||
*/
|
||||
|
||||
template
|
||||
<
|
||||
typename CT,
|
||||
std::size_t SeriesOrder = 2,
|
||||
bool ExpandEpsN = true
|
||||
>
|
||||
class area_formulas
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
//TODO: move the following to a more general space to be used by other
|
||||
// classes as well
|
||||
/*
|
||||
Evaluate the polynomial in x using Horner's method.
|
||||
*/
|
||||
template <typename NT, typename IteratorType>
|
||||
static inline NT horner_evaluate(NT 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
+111
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
+1288
File diff suppressed because it is too large
Load Diff
+285
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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),
|
||||
637–644, 1996
|
||||
*/
|
||||
|
||||
template <typename CT>
|
||||
class vertex_latitude_on_sphere
|
||||
{
|
||||
|
||||
public:
|
||||
template<typename T1, typename T2>
|
||||
static inline CT apply(T1 const& lat1,
|
||||
T2 const& alp1)
|
||||
{
|
||||
return std::acos( math::abs(cos(lat1) * sin(alp1)) );
|
||||
}
|
||||
};
|
||||
|
||||
template <typename CT>
|
||||
class vertex_latitude_on_spheroid
|
||||
{
|
||||
|
||||
public:
|
||||
/*
|
||||
* formula based on paper
|
||||
* [Wood96] Wood - Vertex Latitudes on Ellipsoid Geodesics, SIAM Rev., 38(4),
|
||||
* 637–644, 1996
|
||||
template <typename T1, typename T2, typename Spheroid>
|
||||
static inline CT apply(T1 const& lat1,
|
||||
T2 const& alp1,
|
||||
Spheroid const& spheroid)
|
||||
{
|
||||
CT const f = formula::flattening<CT>(spheroid);
|
||||
|
||||
CT const e2 = f * (CT(2) - f);
|
||||
CT const sin_alp1 = sin(alp1);
|
||||
CT const sin2_lat1 = math::sqr(sin(lat1));
|
||||
CT const cos2_lat1 = CT(1) - sin2_lat1;
|
||||
|
||||
CT const e2_sin2 = CT(1) - e2 * sin2_lat1;
|
||||
CT const cos2_sin2 = cos2_lat1 * math::sqr(sin_alp1);
|
||||
CT const vertex_lat = std::asin( math::sqrt((e2_sin2 - cos2_sin2)
|
||||
/ (e2_sin2 - e2 * cos2_sin2)));
|
||||
return vertex_lat;
|
||||
}
|
||||
*/
|
||||
|
||||
// simpler formula based on Clairaut relation for spheroids
|
||||
template <typename T1, typename T2, typename Spheroid>
|
||||
static inline CT apply(T1 const& lat1,
|
||||
T2 const& alp1,
|
||||
Spheroid const& spheroid)
|
||||
{
|
||||
CT const f = formula::flattening<CT>(spheroid);
|
||||
|
||||
CT const one_minus_f = (CT(1) - f);
|
||||
|
||||
//get the reduced latitude
|
||||
CT const bet1 = atan( one_minus_f * tan(lat1) );
|
||||
|
||||
//apply Clairaut relation
|
||||
CT const betv = vertex_latitude_on_sphere<CT>::apply(bet1, alp1);
|
||||
|
||||
//return the spheroid latitude
|
||||
return atan( tan(betv) / one_minus_f );
|
||||
}
|
||||
|
||||
/*
|
||||
template <typename T>
|
||||
inline static void sign_adjustment(CT lat1, CT lat2, CT vertex_lat, T& vrt_result)
|
||||
{
|
||||
// signbit returns a non-zero value (true) if the sign is negative;
|
||||
// and zero (false) otherwise.
|
||||
bool sign = std::signbit(std::abs(lat1) > std::abs(lat2) ? lat1 : lat2);
|
||||
|
||||
vrt_result.north = sign ? std::max(lat1, lat2) : vertex_lat;
|
||||
vrt_result.south = sign ? vertex_lat * CT(-1) : std::min(lat1, lat2);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline static bool vertex_on_segment(CT alp1, CT alp2, CT lat1, CT lat2, T& vrt_result)
|
||||
{
|
||||
CT const half_pi = math::pi<CT>() / CT(2);
|
||||
|
||||
// if the segment does not contain the vertex of the geodesic
|
||||
// then return the endpoint of max (min) latitude
|
||||
if ((alp1 < half_pi && alp2 < half_pi)
|
||||
|| (alp1 > half_pi && alp2 > half_pi))
|
||||
{
|
||||
vrt_result.north = std::max(lat1, lat2);
|
||||
vrt_result.south = std::min(lat1, lat2);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
template <typename CT, typename CS_Tag>
|
||||
struct vertex_latitude
|
||||
{
|
||||
BOOST_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
@@ -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
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user