#include <GeographicLib/SphericalEngine.hpp>
#include <GeographicLib/CircularEngine.hpp>
#include <GeographicLib/Utility.hpp>
#if defined(_MSC_VER)
# pragma warning (disable: 4701)
#endif
namespace GeographicLib {
using namespace std;
vector<Math::real>& SphericalEngine::sqrttable() {
static vector<real> sqrttable(0);
return sqrttable;
}
template<bool gradp, SphericalEngine::normalization norm, int L>
Math::real SphericalEngine::Value(const coeff c[], const real f[],
real x, real y, real z, real a,
real& gradx, real& grady, real& gradz)
{
static_assert(L > 0, "L must be positive");
static_assert(norm == FULL || norm == SCHMIDT, "Unknown normalization");
int N = c[0].nmx(), M = c[0].mmx();
real
p = hypot(x, y),
cl = p != 0 ? x / p : 1, sl = p != 0 ? y / p : 0, r = hypot(z, p),
t = r != 0 ? z / r : 0, u = r != 0 ? fmax(p / r, eps()) : 1, q = a / r;
real
q2 = Math::sq(q),
uq = u * q,
uq2 = Math::sq(uq),
tu = t / u;
real vc = 0, vc2 = 0, vs = 0, vs2 = 0; real vrc = 0, vrc2 = 0, vrs = 0, vrs2 = 0; real vtc = 0, vtc2 = 0, vts = 0, vts2 = 0; real vlc = 0, vlc2 = 0, vls = 0, vls2 = 0; int k[L];
const vector<real>& root( sqrttable() );
for (int m = M; m >= 0; --m) { real
wc = 0, wc2 = 0, ws = 0, ws2 = 0, wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0, wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; for (int l = 0; l < L; ++l)
k[l] = c[l].index(N, m) + 1;
for (int n = N; n >= m; --n) { real w, A, Ax, B, R; switch (norm) {
case FULL:
w = root[2 * n + 1] / (root[n - m + 1] * root[n + m + 1]);
Ax = q * w * root[2 * n + 3];
A = t * Ax;
B = - q2 * root[2 * n + 5] /
(w * root[n - m + 2] * root[n + m + 2]);
break;
case SCHMIDT:
w = root[n - m + 1] * root[n + m + 1];
Ax = q * (2 * n + 1) / w;
A = t * Ax;
B = - q2 * w / (root[n - m + 2] * root[n + m + 2]);
break;
default: break; }
R = c[0].Cv(--k[0]);
for (int l = 1; l < L; ++l)
R += c[l].Cv(--k[l], n, m, f[l]);
R *= scale();
w = A * wc + B * wc2 + R; wc2 = wc; wc = w;
if (gradp) {
w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w;
w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w;
}
if (m) {
R = c[0].Sv(k[0]);
for (int l = 1; l < L; ++l)
R += c[l].Sv(k[l], n, m, f[l]);
R *= scale();
w = A * ws + B * ws2 + R; ws2 = ws; ws = w;
if (gradp) {
w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w;
w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w;
}
}
}
if (m) {
real v, A, B; switch (norm) {
case FULL:
v = root[2] * root[2 * m + 3] / root[m + 1];
A = cl * v * uq;
B = - v * root[2 * m + 5] / (root[8] * root[m + 2]) * uq2;
break;
case SCHMIDT:
v = root[2] * root[2 * m + 1] / root[m + 1];
A = cl * v * uq;
B = - v * root[2 * m + 3] / (root[8] * root[m + 2]) * uq2;
break;
default: break; }
v = A * vc + B * vc2 + wc ; vc2 = vc ; vc = v;
v = A * vs + B * vs2 + ws ; vs2 = vs ; vs = v;
if (gradp) {
wtc += m * tu * wc; wts += m * tu * ws;
v = A * vrc + B * vrc2 + wrc; vrc2 = vrc; vrc = v;
v = A * vrs + B * vrs2 + wrs; vrs2 = vrs; vrs = v;
v = A * vtc + B * vtc2 + wtc; vtc2 = vtc; vtc = v;
v = A * vts + B * vts2 + wts; vts2 = vts; vts = v;
v = A * vlc + B * vlc2 + m*ws; vlc2 = vlc; vlc = v;
v = A * vls + B * vls2 - m*wc; vls2 = vls; vls = v;
}
} else {
real A, B, qs;
switch (norm) {
case FULL:
A = root[3] * uq; B = - root[15]/2 * uq2; break;
case SCHMIDT:
A = uq;
B = - root[3]/2 * uq2;
break;
default: break; }
qs = q / scale();
vc = qs * (wc + A * (cl * vc + sl * vs ) + B * vc2);
if (gradp) {
qs /= r;
vrc = - qs * (wrc + A * (cl * vrc + sl * vrs) + B * vrc2);
vtc = qs * (wtc + A * (cl * vtc + sl * vts) + B * vtc2);
vlc = qs / u * ( A * (cl * vlc + sl * vls) + B * vlc2);
}
}
}
if (gradp) {
gradx = cl * (u * vrc + t * vtc) - sl * vlc;
grady = sl * (u * vrc + t * vtc) + cl * vlc;
gradz = t * vrc - u * vtc ;
}
return vc;
}
template<bool gradp, SphericalEngine::normalization norm, int L>
CircularEngine SphericalEngine::Circle(const coeff c[], const real f[],
real p, real z, real a) {
static_assert(L > 0, "L must be positive");
static_assert(norm == FULL || norm == SCHMIDT, "Unknown normalization");
int N = c[0].nmx(), M = c[0].mmx();
real
r = hypot(z, p),
t = r != 0 ? z / r : 0, u = r != 0 ? fmax(p / r, eps()) : 1, q = a / r;
real
q2 = Math::sq(q),
tu = t / u;
CircularEngine circ(M, gradp, norm, a, r, u, t);
int k[L];
const vector<real>& root( sqrttable() );
for (int m = M; m >= 0; --m) { real
wc = 0, wc2 = 0, ws = 0, ws2 = 0, wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0, wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; for (int l = 0; l < L; ++l)
k[l] = c[l].index(N, m) + 1;
for (int n = N; n >= m; --n) { real w, A, Ax, B, R; switch (norm) {
case FULL:
w = root[2 * n + 1] / (root[n - m + 1] * root[n + m + 1]);
Ax = q * w * root[2 * n + 3];
A = t * Ax;
B = - q2 * root[2 * n + 5] /
(w * root[n - m + 2] * root[n + m + 2]);
break;
case SCHMIDT:
w = root[n - m + 1] * root[n + m + 1];
Ax = q * (2 * n + 1) / w;
A = t * Ax;
B = - q2 * w / (root[n - m + 2] * root[n + m + 2]);
break;
default: break; }
R = c[0].Cv(--k[0]);
for (int l = 1; l < L; ++l)
R += c[l].Cv(--k[l], n, m, f[l]);
R *= scale();
w = A * wc + B * wc2 + R; wc2 = wc; wc = w;
if (gradp) {
w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w;
w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w;
}
if (m) {
R = c[0].Sv(k[0]);
for (int l = 1; l < L; ++l)
R += c[l].Sv(k[l], n, m, f[l]);
R *= scale();
w = A * ws + B * ws2 + R; ws2 = ws; ws = w;
if (gradp) {
w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w;
w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w;
}
}
}
if (!gradp)
circ.SetCoeff(m, wc, ws);
else {
wtc += m * tu * wc; wts += m * tu * ws;
circ.SetCoeff(m, wc, ws, wrc, wrs, wtc, wts);
}
}
return circ;
}
void SphericalEngine::RootTable(int N) {
vector<real>& root( sqrttable() );
int L = max(2 * N + 5, 15) + 1, oldL = int(root.size());
if (oldL >= L)
return;
root.resize(L);
for (int l = oldL; l < L; ++l)
root[l] = sqrt(real(l));
}
void SphericalEngine::coeff::readcoeffs(istream& stream, int& N, int& M,
vector<real>& C,
vector<real>& S,
bool truncate) {
if (truncate) {
if (!((N >= M && M >= 0) || (N == -1 && M == -1)))
throw GeographicErr("Bad requested degree and order " +
Utility::str(N) + " " + Utility::str(M));
}
int nm[2];
Utility::readarray<int, int, false>(stream, nm, 2);
int N0 = nm[0], M0 = nm[1];
if (!((N0 >= M0 && M0 >= 0) || (N0 == -1 && M0 == -1)))
throw GeographicErr("Bad degree and order " +
Utility::str(N0) + " " + Utility::str(M0));
N = truncate ? min(N, N0) : N0;
M = truncate ? min(M, M0) : M0;
C.resize(SphericalEngine::coeff::Csize(N, M));
S.resize(SphericalEngine::coeff::Ssize(N, M));
int skip = (SphericalEngine::coeff::Csize(N0, M0) -
SphericalEngine::coeff::Csize(N0, M )) * sizeof(double);
if (N == N0) {
Utility::readarray<double, real, false>(stream, C);
if (skip) stream.seekg(streamoff(skip), ios::cur);
Utility::readarray<double, real, false>(stream, S);
if (skip) stream.seekg(streamoff(skip), ios::cur);
} else {
for (int m = 0, k = 0; m <= M; ++m) {
Utility::readarray<double, real, false>(stream, &C[k], N + 1 - m);
stream.seekg((N0 - N) * sizeof(double), ios::cur);
k += N + 1 - m;
}
if (skip) stream.seekg(streamoff(skip), ios::cur);
for (int m = 1, k = 0; m <= M; ++m) {
Utility::readarray<double, real, false>(stream, &S[k], N + 1 - m);
stream.seekg((N0 - N) * sizeof(double), ios::cur);
k += N + 1 - m;
}
if (skip) stream.seekg(streamoff(skip), ios::cur);
}
return;
}
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<true, SphericalEngine::FULL, 1>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<false, SphericalEngine::FULL, 1>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 1>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 1>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<true, SphericalEngine::FULL, 2>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<false, SphericalEngine::FULL, 2>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 2>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 2>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<true, SphericalEngine::FULL, 3>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<false, SphericalEngine::FULL, 3>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 3>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template Math::real GEOGRAPHICLIB_EXPORT
SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 3>
(const coeff[], const real[], real, real, real, real, real&, real&, real&);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<true, SphericalEngine::FULL, 1>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<false, SphericalEngine::FULL, 1>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 1>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 1>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<true, SphericalEngine::FULL, 2>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<false, SphericalEngine::FULL, 2>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 2>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 2>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<true, SphericalEngine::FULL, 3>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<false, SphericalEngine::FULL, 3>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 3>
(const coeff[], const real[], real, real, real);
template CircularEngine GEOGRAPHICLIB_EXPORT
SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 3>
(const coeff[], const real[], real, real, real);
}