Loading...
Searching...
No Matches
Rhumb.cpp
Go to the documentation of this file.
/**
* \file Rhumb.cpp
* \brief Implementation for GeographicLib::Rhumb and GeographicLib::RhumbLine
* classes
*
* Copyright (c) Charles Karney (2014-2023) <karney@alum.mit.edu> and licensed
* under the MIT/X11 License. For more information, see
* https://geographiclib.sourceforge.io/
**********************************************************************/
#include <GeographicLib/Rhumb.hpp>
#include <GeographicLib/DST.hpp>
namespace GeographicLib {
using namespace std;
Rhumb::Rhumb(real a, real f, bool exact)
: _aux(a, f)
, _exact(exact)
, _a(a)
, _f(f)
, _n(_f / (2 - _f))
, _rm(_aux.RectifyingRadius(_exact))
, _c2(_aux.AuthalicRadiusSquared(_exact) * Math::degree())
, _lL(_exact ? 8 : Lmax_) // 8 is starting size for DFT fit
, _pP(_lL)
{
AreaCoeffs();
}
const Rhumb& Rhumb::WGS84() {
static const Rhumb
wgs84(Constants::WGS84_a(), Constants::WGS84_f());
return wgs84;
}
void Rhumb::AreaCoeffs() {
// Set up coefficients for area calculation
if (_exact) {
// Compute coefficients by Fourier transform of integrand
static const real eps = numeric_limits<real>::epsilon()/2;
qIntegrand f(_aux);
int L = 4;
vector<real> c(L);
DST fft(L); fft.transform(f, c.data()); L *= 2;
// For |n| <= 0.99, actual max for doubles is 2163. This scales as
// Math::digits() and for long doubles (GEOGRAPHICLIB_PRECISION = 3,
// digits = 64), this becomes 2163 * 64 / 53 = 2612. Round this up to
// 2^12 = 4096 and scale this by Math::digits()/64 if digits() > 64.
//
// 64 = digits for long double, 6 = 12 - log2(64)
int Lmax = 1<<(int(ceil(log2(max(Math::digits(), 64)))) + 6);
for (_lL = 0; L <= Lmax && _lL == 0; L *=2) {
fft.reset(L/2); c.resize(L); fft.refine(f, c.data());
_pP.resize(L);
for (int l = 0, k = -1; l < L; ++l) {
// Compute Fourier coefficients of integral
_pP[l] = (c[l] + (l+1 < L ? c[l+1] : 0)) / (-4 * (l+1));
if (fabs(_pP[l]) <= eps) {
if (k < 0) k = l; // mark as first small value
} else
k = -1; // run interrupted
if (k >= 0 && l - k + 1 >= (l + 1 + 7) / 8) {
// run of small values of at least l/8?
_lL = l + 1; _pP.resize(_lL); break;
}
}
// loop exits if _lL > 0
}
if (_lL == 0) // Hasn't converged -- just use the values we have
_lL = int(_pP.size());
} else {
// Use series expansions in n for Fourier coefficients of the integral
// See "Series expansions for computing rhumb areas"
// https://doi.org/10.5281/zenodo.7685484
#if GEOGRAPHICLIB_RHUMBAREA_ORDER == 4
static const real coeffs[] = {
// Coefficients in matrix Q
596/real(2025), -398/real(945), 22/real(45), -1/real(3),
1543/real(4725), -118/real(315), 1/real(5),
152/real(945), -17/real(315),
5/real(252),
};
#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 5
static const real coeffs[] = {
// Coefficients in matrix Q
-102614/real(467775), 596/real(2025), -398/real(945), 22/real(45),
-1/real(3),
-24562/real(155925), 1543/real(4725), -118/real(315), 1/real(5),
-38068/real(155925), 152/real(945), -17/real(315),
-752/real(10395), 5/real(252),
-101/real(17325),
};
#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 6
static const real coeffs[] = {
// Coefficients in matrix Q
138734126/real(638512875), -102614/real(467775), 596/real(2025),
-398/real(945), 22/real(45), -1/real(3),
17749373/real(425675250), -24562/real(155925), 1543/real(4725),
-118/real(315), 1/real(5),
1882432/real(8513505), -38068/real(155925), 152/real(945),
-17/real(315),
268864/real(2027025), -752/real(10395), 5/real(252),
62464/real(2027025), -101/real(17325),
11537/real(4054050),
};
#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 7
static const real coeffs[] = {
// Coefficients in matrix Q
-565017322/real(1915538625), 138734126/real(638512875),
-102614/real(467775), 596/real(2025), -398/real(945), 22/real(45),
-1/real(3),
-1969276/real(58046625), 17749373/real(425675250), -24562/real(155925),
1543/real(4725), -118/real(315), 1/real(5),
-58573784/real(638512875), 1882432/real(8513505), -38068/real(155925),
152/real(945), -17/real(315),
-6975184/real(42567525), 268864/real(2027025), -752/real(10395),
5/real(252),
-112832/real(1447875), 62464/real(2027025), -101/real(17325),
-4096/real(289575), 11537/real(4054050),
-311/real(525525),
};
#elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 8
static const real coeffs[] = {
// Coefficients in matrix Q
188270561816LL/real(488462349375LL), -565017322/real(1915538625),
138734126/real(638512875), -102614/real(467775), 596/real(2025),
-398/real(945), 22/real(45), -1/real(3),
2332829602LL/real(23260111875LL), -1969276/real(58046625),
17749373/real(425675250), -24562/real(155925), 1543/real(4725),
-118/real(315), 1/real(5),
-41570288/real(930404475), -58573784/real(638512875),
1882432/real(8513505), -38068/real(155925), 152/real(945),
-17/real(315),
1538774036/real(10854718875LL), -6975184/real(42567525),
268864/real(2027025), -752/real(10395), 5/real(252),
436821248/real(3618239625LL), -112832/real(1447875),
62464/real(2027025), -101/real(17325),
3059776/real(80405325), -4096/real(289575), 11537/real(4054050),
4193792/real(723647925), -311/real(525525),
1097653/real(1929727800),
};
#else
#error "Bad value for GEOGRAPHICLIB_RHUMBAREA_ORDER"
#endif
static_assert(sizeof(coeffs) / sizeof(real) ==
(Lmax_ * (Lmax_ + 1))/2,
"Coefficient array size mismatch for Rhumb");
real d = 1;
int o = 0;
for (int l = 0; l < Lmax_; ++l) {
int m = Lmax_ - l - 1;
d *= _n;
_pP[l] = d * Math::polyval(m, coeffs + o, _n);
o += m + 1;
}
}
// Post condition: o == sizeof(coeffs) / sizeof(real)
}
Rhumb::qIntegrand::qIntegrand(const AuxLatitude& aux)
: _aux(aux) {}
Math::real Rhumb::qIntegrand::operator()(real beta) const {
// pbeta(beta) = integrate(q(beta), beta)
// q(beta) = (1-f) * (sin(xi) - sin(chi)) / cos(phi)
// = (1-f) * (cos(chi) - cos(xi)) / cos(phi) *
// (cos(xi) + cos(chi)) / (sin(xi) + sin(chi))
// Fit q(beta)/cos(beta) with Fourier transform
// q(beta)/cos(beta) = sum(c[k] * sin((2*k+1)*beta), k, 0, K-1)
// then the integral is
// pbeta = sum(d[k] * cos((2*k+2)*beta), k, 0, K-1)
// where
// d[k] = -1/(4*(k+1)) * (c[k] + c[k+1]) for k in 0..K-2
// d[K-1] = -1/(4*K) * c[K-1]
AuxAngle betaa(AuxAngle::radians(beta)),
phia(_aux.Convert(AuxLatitude::BETA, AuxLatitude::PHI,
betaa, true).normalized()),
chia(_aux.Convert(AuxLatitude::PHI , AuxLatitude::CHI,
phia , true).normalized()),
xia (_aux.Convert(AuxLatitude::PHI , AuxLatitude::XI ,
phia , true).normalized());
real schi = chia.y(), cchi = chia.x(), sxi = xia.y(), cxi = xia.x(),
cphi = phia.x(), cbeta = betaa.x();
return (1 - _aux.Flattening()) *
( fabs(schi) < fabs(cchi) ? sxi - schi :
(cchi - cxi) * (cxi + cchi) / (sxi + schi) ) / (cphi * cbeta);
// Value for beta = pi/2. This isn't needed because beta is given in
// radians and cos(pi/2) is never exactly 0. See formulas in the auxlat
// paper for tan(chi)/tan(phi) and tan(xi)/tan(phi) in the limit phi ->
// pi/2.
//
// n = 1/2;
// e = 2*sqrt(n) / (1 + n);
// e1 = 2*sqrt(n) / (1 - n);
// at = f == 0 ? 1 : (f < 0 ? atan(|e|)/|e| : asinh(e1)/e);
// q1 = at + 1 / (1 - e^2);
// s1 = sinh(e^2 * at);
// h1 = f < 0 ? hypot(1, s1) - s1 : 1/(hypot(1, s1) + s1);
// v = (1 - e^2) / (2 * h1^2) - 1 / ((1 - e^2) * q1);
}
void Rhumb::GenInverse(real lat1, real lon1, real lat2, real lon2,
unsigned outmask,
real& s12, real& azi12, real& S12) const {
AuxAngle phi1(AuxAngle::degrees(lat1)), phi2(AuxAngle::degrees(lat2)),
chi1(_aux.Convert(_aux.PHI, _aux.CHI, phi1, _exact)),
chi2(_aux.Convert(_aux.PHI, _aux.CHI, phi2, _exact));
real
lon12 = Math::AngDiff(lon1, lon2),
lam12 = lon12 * Math::degree<real>(),
psi1 = chi1.lam(),
psi2 = chi2.lam(),
psi12 = psi2 - psi1;
if (outmask & AZIMUTH)
azi12 = Math::atan2d(lam12, psi12);
if (outmask & DISTANCE) {
if (isinf(psi1) || isinf(psi2)) {
s12 = fabs(_aux.Convert(AuxLatitude::PHI, AuxLatitude::MU,
phi2, _exact).radians() -
_aux.Convert(AuxLatitude::PHI, AuxLatitude::MU,
phi1, _exact).radians()) * _rm;
} else {
real h = hypot(lam12, psi12);
// dmu/dpsi = dmu/dchi / dpsi/dchi
real dmudpsi = _exact ?
_aux.DRectifying(phi1, phi2) / _aux.DIsometric(phi1, phi2) :
_aux.DConvert(AuxLatitude::CHI, AuxLatitude::MU, chi1, chi2)
/ DAuxLatitude::Dlam(chi1.tan(), chi2.tan());
s12 = h * dmudpsi * _rm;
}
}
if (outmask & AREA)
S12 = _c2 * lon12 * MeanSinXi(chi1, chi2);
}
RhumbLine Rhumb::Line(real lat1, real lon1, real azi12) const
{ return RhumbLine(*this, lat1, lon1, azi12); }
void Rhumb::GenDirect(real lat1, real lon1, real azi12, real s12,
unsigned outmask,
real& lat2, real& lon2, real& S12) const
{ Line(lat1, lon1, azi12).GenPosition(s12, outmask, lat2, lon2, S12); }
Math::real Rhumb::MeanSinXi(const AuxAngle& chix, const AuxAngle& chiy)
const {
AuxAngle
phix (_aux.Convert(_aux.CHI, _aux.PHI , chix, _exact)),
phiy (_aux.Convert(_aux.CHI, _aux.PHI , chiy, _exact)),
betax(_aux.Convert(_aux.PHI, _aux.BETA, phix, _exact).normalized()),
betay(_aux.Convert(_aux.PHI, _aux.BETA, phiy, _exact).normalized());
real DpbetaDbeta =
DAuxLatitude::DClenshaw(false,
betay.radians() - betax.radians(),
betax.y(), betax.x(), betay.y(), betay.x(),
_pP.data(), _lL),
tx = chix.tan(), ty = chiy.tan(),
DbetaDpsi = _exact ?
_aux.DParametric(phix, phiy) / _aux.DIsometric(phix, phiy) :
_aux.DConvert(AuxLatitude::CHI, AuxLatitude::BETA, chix, chiy) /
DAuxLatitude::Dlam(tx, ty);
return DAuxLatitude::Dp0Dpsi(tx, ty) + DpbetaDbeta * DbetaDpsi;
}
RhumbLine::RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12)
: _rh(rh)
, _lat1(Math::LatFix(lat1))
, _lon1(lon1)
, _azi12(Math::AngNormalize(azi12))
{
Math::sincosd(_azi12, _salp, _calp);
_phi1 = AuxAngle::degrees(lat1);
_mu1 = _rh._aux.Convert(AuxLatitude::PHI, AuxLatitude::MU,
_phi1, _rh._exact).degrees();
_chi1 = _rh._aux.Convert(AuxLatitude::PHI, AuxLatitude::CHI,
_phi1, _rh._exact);
_psi1 = _chi1.lam();
}
void RhumbLine::GenPosition(real s12, unsigned outmask,
real& lat2, real& lon2, real& S12) const {
real
r12 = s12 / (_rh._rm * Math::degree()), // scaled distance in degrees
mu12 = r12 * _calp,
mu2 = _mu1 + mu12;
real lat2x, lon2x;
if (fabs(mu2) <= Math::qd) {
AuxAngle mu2a(AuxAngle::degrees(mu2)),
phi2(_rh._aux.Convert(AuxLatitude::MU, AuxLatitude::PHI,
mu2a, _rh._exact)),
chi2(_rh._aux.Convert(AuxLatitude::PHI, AuxLatitude::CHI,
phi2, _rh._exact));
lat2x = phi2.degrees();
real dmudpsi = _rh._exact ?
_rh._aux.DRectifying(_phi1, phi2) / _rh._aux.DIsometric(_phi1, phi2) :
_rh._aux.DConvert(AuxLatitude::CHI, AuxLatitude::MU, _chi1, chi2)
/ DAuxLatitude::Dlam(_chi1.tan(), chi2.tan());
lon2x = r12 * _salp / dmudpsi;
if (outmask & AREA)
S12 = _rh._c2 * lon2x * _rh.MeanSinXi(_chi1, chi2);
lon2x = outmask & LONG_UNROLL ? _lon1 + lon2x :
Math::AngNormalize(Math::AngNormalize(_lon1) + lon2x);
} else {
// Reduce to the interval [-180, 180)
mu2 = Math::AngNormalize(mu2);
// Deal with points on the anti-meridian
if (fabs(mu2) > Math::qd) mu2 = Math::AngNormalize(Math::hd - mu2);
lat2x = _rh._aux.Convert(AuxLatitude::MU, AuxLatitude::PHI,
AuxAngle::degrees(mu2), _rh._exact).degrees();
lon2x = Math::NaN();
if (outmask & AREA)
S12 = Math::NaN();
}
if (outmask & LATITUDE) lat2 = lat2x;
if (outmask & LONGITUDE) lon2 = lon2x;
}
} // namespace GeographicLib
DST.hpp
Header for GeographicLib::DST class.
real
GeographicLib::Math::real real
Definition Geod3Solve.cpp:25
Rhumb.hpp
Header for GeographicLib::Rhumb and GeographicLib::RhumbLine classes.
GeographicLib::AuxAngle
An accurate representation of angles.
Definition AuxAngle.hpp:51
GeographicLib::AuxAngle::radians
Math::real radians() const
Definition AuxAngle.hpp:232
GeographicLib::AuxAngle::normalized
AuxAngle normalized() const
Definition AuxAngle.cpp:28
GeographicLib::AuxAngle::degrees
Math::real degrees() const
Definition AuxAngle.hpp:228
GeographicLib::AuxAngle::lam
Math::real lam() const
Definition AuxAngle.hpp:236
GeographicLib::AuxAngle::tan
Math::real tan() const
Definition AuxAngle.hpp:117
GeographicLib::AuxLatitude
Conversions between auxiliary latitudes.
Definition AuxLatitude.hpp:75
GeographicLib::AuxLatitude::Convert
AuxAngle Convert(int auxin, int auxout, const AuxAngle &zeta, bool exact=false) const
Definition AuxLatitude.cpp:294
GeographicLib::AuxLatitude::XI
@ XI
Definition AuxLatitude.hpp:151
GeographicLib::AuxLatitude::BETA
@ BETA
Definition AuxLatitude.hpp:131
GeographicLib::AuxLatitude::MU
@ MU
Definition AuxLatitude.hpp:141
GeographicLib::AuxLatitude::CHI
@ CHI
Definition AuxLatitude.hpp:146
GeographicLib::AuxLatitude::PHI
@ PHI
Definition AuxLatitude.hpp:126
GeographicLib::Constants::WGS84_f
static T WGS84_f()
Definition Constants.hpp:118
GeographicLib::Constants::WGS84_a
static T WGS84_a()
Definition Constants.hpp:112
GeographicLib::DAuxLatitude::Dlam
static Math::real Dlam(real x, real y)
Definition DAuxLatitude.hpp:144
GeographicLib::DAuxLatitude::DParametric
Math::real DParametric(const AuxAngle &phi1, const AuxAngle &phi2) const
Definition DAuxLatitude.cpp:46
GeographicLib::DAuxLatitude::DConvert
Math::real DConvert(int auxin, int auxout, const AuxAngle &zeta1, const AuxAngle &zeta2) const
Definition DAuxLatitude.cpp:191
GeographicLib::DAuxLatitude::DIsometric
Math::real DIsometric(const AuxAngle &phi1, const AuxAngle &phi2) const
Definition DAuxLatitude.cpp:180
GeographicLib::DAuxLatitude::Dp0Dpsi
static Math::real Dp0Dpsi(real x, real y)
Definition DAuxLatitude.hpp:165
GeographicLib::DAuxLatitude::DClenshaw
static Math::real DClenshaw(bool sinp, real Delta, real szeta1, real czeta1, real szeta2, real czeta2, const real c[], int K)
Definition DAuxLatitude.cpp:206
GeographicLib::DST
Discrete sine transforms.
Definition DST.hpp:67
GeographicLib::Math
Mathematical functions needed by GeographicLib.
Definition Math.hpp:82
GeographicLib::Math::degree
static T degree()
Definition Math.hpp:197
GeographicLib::Math::sincosd
static void sincosd(T x, T &sinx, T &cosx)
Definition Math.cpp:104
GeographicLib::Math::atan2d
static T atan2d(T y, T x)
Definition Math.cpp:212
GeographicLib::Math::qd
static constexpr int qd
degrees per quarter turn
Definition Math.hpp:142
GeographicLib::Math::AngNormalize
static T AngNormalize(T x)
Definition Math.cpp:69
GeographicLib::Math::digits
static int digits()
Definition Math.cpp:20
GeographicLib::Math::NaN
static T NaN()
Definition Math.cpp:301
GeographicLib::Math::polyval
static T polyval(int N, const T p[], T x)
Definition Math.hpp:270
GeographicLib::Math::AngDiff
static T AngDiff(T x, T y, T &e)
Definition Math.cpp:80
GeographicLib::Math::hd
static constexpr int hd
degrees per half turn
Definition Math.hpp:145
GeographicLib::Math::real
double real
Definition Math.hpp:115
GeographicLib::RhumbLine::LONG_UNROLL
@ LONG_UNROLL
Definition Rhumb.hpp:437
GeographicLib::RhumbLine::LONGITUDE
@ LONGITUDE
Definition Rhumb.hpp:417
GeographicLib::RhumbLine::AREA
@ AREA
Definition Rhumb.hpp:432
GeographicLib::RhumbLine::LATITUDE
@ LATITUDE
Definition Rhumb.hpp:412
GeographicLib::RhumbLine::GenPosition
void GenPosition(real s12, unsigned outmask, real &lat2, real &lon2, real &S12) const
Definition Rhumb.cpp:282
GeographicLib::Rhumb::DISTANCE
@ DISTANCE
Definition Rhumb.hpp:147
GeographicLib::Rhumb::AZIMUTH
@ AZIMUTH
Definition Rhumb.hpp:142
GeographicLib::Rhumb::AREA
@ AREA
Definition Rhumb.hpp:152
GeographicLib::Rhumb::Line
RhumbLine Line(real lat1, real lon1, real azi12) const
Definition Rhumb.cpp:239
GeographicLib::Rhumb::RhumbLine
friend class RhumbLine
Definition Rhumb.hpp:83
GeographicLib::Rhumb::Rhumb
Rhumb(real a, real f, bool exact=false)
Definition Rhumb.cpp:18
GeographicLib::Rhumb::WGS84
static const Rhumb & WGS84()
Definition Rhumb.cpp:32
GeographicLib
Namespace for GeographicLib.
Definition Accumulator.cpp:12