GeographicLib 2.5
SphericalEngine.cpp
Go to the documentation of this file.
1/**
2 * \file SphericalEngine.cpp
3 * \brief Implementation for GeographicLib::SphericalEngine class
4 *
5 * Copyright (c) Charles Karney (2011-2022) <karney@alum.mit.edu> and licensed
6 * under the MIT/X11 License. For more information, see
7 * https://geographiclib.sourceforge.io/
8 *
9 * The general sum is\verbatim
10 V(r, theta, lambda) = sum(n = 0..N) sum(m = 0..n)
11 q^(n+1) * (C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) * P[n,m](t)
12\endverbatim
13 * where <tt>t = cos(theta)</tt>, <tt>q = a/r</tt>. In addition write <tt>u =
14 * sin(theta)</tt>.
15 *
16 * <tt>P[n,m]</tt> is a normalized associated Legendre function of degree
17 * <tt>n</tt> and order <tt>m</tt>. Here the formulas are given for full
18 * normalized functions (usually denoted <tt>Pbar</tt>).
19 *
20 * Rewrite outer sum\verbatim
21 V(r, theta, lambda) = sum(m = 0..N) * P[m,m](t) * q^(m+1) *
22 [Sc[m] * cos(m*lambda) + Ss[m] * sin(m*lambda)]
23\endverbatim
24 * where the inner sums are\verbatim
25 Sc[m] = sum(n = m..N) q^(n-m) * C[n,m] * P[n,m](t)/P[m,m](t)
26 Ss[m] = sum(n = m..N) q^(n-m) * S[n,m] * P[n,m](t)/P[m,m](t)
27\endverbatim
28 * Evaluate sums via Clenshaw method. The overall framework is similar to
29 * Deakin with the following changes:
30 * - Clenshaw summation is used to roll the computation of
31 * <tt>cos(m*lambda)</tt> and <tt>sin(m*lambda)</tt> into the evaluation of
32 * the outer sum (rather than independently computing an array of these
33 * trigonometric terms).
34 * - Scale the coefficients to guard against overflow when <tt>N</tt> is large.
35 * .
36 * For the general framework of Clenshaw, see
37 * http://mathworld.wolfram.com/ClenshawRecurrenceFormula.html
38 *
39 * Let\verbatim
40 S = sum(k = 0..N) c[k] * F[k](x)
41 F[n+1](x) = alpha[n](x) * F[n](x) + beta[n](x) * F[n-1](x)
42\endverbatim
43 * Evaluate <tt>S</tt> with\verbatim
44 y[N+2] = y[N+1] = 0
45 y[k] = alpha[k] * y[k+1] + beta[k+1] * y[k+2] + c[k]
46 S = c[0] * F[0] + y[1] * F[1] + beta[1] * F[0] * y[2]
47\endverbatim
48 * \e IF <tt>F[0](x) = 1</tt> and <tt>beta(0,x) = 0</tt>, then <tt>F[1](x) =
49 * alpha(0,x)</tt> and we can continue the recursion for <tt>y[k]</tt> until
50 * <tt>y[0]</tt>, giving\verbatim
51 S = y[0]
52\endverbatim
53 *
54 * Evaluating the inner sum\verbatim
55 l = n-m; n = l+m
56 Sc[m] = sum(l = 0..N-m) C[l+m,m] * q^l * P[l+m,m](t)/P[m,m](t)
57 F[l] = q^l * P[l+m,m](t)/P[m,m](t)
58\endverbatim
59 * Holmes + Featherstone, Eq. (11), give\verbatim
60 P[n,m] = sqrt((2*n-1)*(2*n+1)/((n-m)*(n+m))) * t * P[n-1,m] -
61 sqrt((2*n+1)*(n+m-1)*(n-m-1)/((n-m)*(n+m)*(2*n-3))) * P[n-2,m]
62\endverbatim
63 * thus\verbatim
64 alpha[l] = t * q * sqrt(((2*n+1)*(2*n+3))/
65 ((n-m+1)*(n+m+1)))
66 beta[l+1] = - q^2 * sqrt(((n-m+1)*(n+m+1)*(2*n+5))/
67 ((n-m+2)*(n+m+2)*(2*n+1)))
68\endverbatim
69 * In this case, <tt>F[0] = 1</tt> and <tt>beta[0] = 0</tt>, so the <tt>Sc[m]
70 * = y[0]</tt>.
71 *
72 * Evaluating the outer sum\verbatim
73 V = sum(m = 0..N) Sc[m] * q^(m+1) * cos(m*lambda) * P[m,m](t)
74 + sum(m = 0..N) Ss[m] * q^(m+1) * cos(m*lambda) * P[m,m](t)
75 F[m] = q^(m+1) * cos(m*lambda) * P[m,m](t) [or sin(m*lambda)]
76\endverbatim
77 * Holmes + Featherstone, Eq. (13), give\verbatim
78 P[m,m] = u * sqrt((2*m+1)/((m>1?2:1)*m)) * P[m-1,m-1]
79\endverbatim
80 * also, we have\verbatim
81 cos((m+1)*lambda) = 2*cos(lambda)*cos(m*lambda) - cos((m-1)*lambda)
82\endverbatim
83 * thus\verbatim
84 alpha[m] = 2*cos(lambda) * sqrt((2*m+3)/(2*(m+1))) * u * q
85 = cos(lambda) * sqrt( 2*(2*m+3)/(m+1) ) * u * q
86 beta[m+1] = -sqrt((2*m+3)*(2*m+5)/(4*(m+1)*(m+2))) * u^2 * q^2
87 * (m == 0 ? sqrt(2) : 1)
88\endverbatim
89 * Thus\verbatim
90 F[0] = q [or 0]
91 F[1] = cos(lambda) * sqrt(3) * u * q^2 [or sin(lambda)]
92 beta[1] = - sqrt(15/4) * u^2 * q^2
93\endverbatim
94 *
95 * Here is how the various components of the gradient are computed
96 *
97 * Differentiate wrt <tt>r</tt>\verbatim
98 d q^(n+1) / dr = (-1/r) * (n+1) * q^(n+1)
99\endverbatim
100 * so multiply <tt>C[n,m]</tt> by <tt>n+1</tt> in inner sum and multiply the
101 * sum by <tt>-1/r</tt>.
102 *
103 * Differentiate wrt <tt>lambda</tt>\verbatim
104 d cos(m*lambda) = -m * sin(m*lambda)
105 d sin(m*lambda) = m * cos(m*lambda)
106\endverbatim
107 * so multiply terms by <tt>m</tt> in outer sum and swap sine and cosine
108 * variables.
109 *
110 * Differentiate wrt <tt>theta</tt>\verbatim
111 dV/dtheta = V' = -u * dV/dt = -u * V'
112\endverbatim
113 * here <tt>'</tt> denotes differentiation wrt <tt>theta</tt>.\verbatim
114 d/dtheta (Sc[m] * P[m,m](t)) = Sc'[m] * P[m,m](t) + Sc[m] * P'[m,m](t)
115\endverbatim
116 * Now <tt>P[m,m](t) = const * u^m</tt>, so <tt>P'[m,m](t) = m * t/u *
117 * P[m,m](t)</tt>, thus\verbatim
118 d/dtheta (Sc[m] * P[m,m](t)) = (Sc'[m] + m * t/u * Sc[m]) * P[m,m](t)
119\endverbatim
120 * Clenshaw recursion for <tt>Sc[m]</tt> reads\verbatim
121 y[k] = alpha[k] * y[k+1] + beta[k+1] * y[k+2] + c[k]
122\endverbatim
123 * Substituting <tt>alpha[k] = const * t</tt>, <tt>alpha'[k] = -u/t *
124 * alpha[k]</tt>, <tt>beta'[k] = c'[k] = 0</tt> gives\verbatim
125 y'[k] = alpha[k] * y'[k+1] + beta[k+1] * y'[k+2] - u/t * alpha[k] * y[k+1]
126\endverbatim
127 *
128 * Finally, given the derivatives of <tt>V</tt>, we can compute the components
129 * of the gradient in spherical coordinates and transform the result into
130 * cartesian coordinates.
131 **********************************************************************/
132
136
137#if defined(_MSC_VER)
138// Squelch warnings about potentially uninitialized local variables
139# pragma warning (disable: 4701)
140#endif
141
142namespace GeographicLib {
143
144 using namespace std;
145
146 vector<Math::real>& SphericalEngine::sqrttable() {
147 static vector<real> sqrttable(0);
148 return sqrttable;
149 }
150
151 template<bool gradp, SphericalEngine::normalization norm, int L>
152 Math::real SphericalEngine::Value(const coeff c[], const real f[],
153 real x, real y, real z, real a,
154 real& gradx, real& grady, real& gradz)
155 {
156 static_assert(L > 0, "L must be positive");
157 static_assert(norm == FULL || norm == SCHMIDT, "Unknown normalization");
158 int N = c[0].nmx(), M = c[0].mmx();
159
160 real
161 p = hypot(x, y),
162 cl = p != 0 ? x / p : 1, // cos(lambda); at pole, pick lambda = 0
163 sl = p != 0 ? y / p : 0, // sin(lambda)
164 r = hypot(z, p),
165 t = r != 0 ? z / r : 0, // cos(theta); at origin, pick theta = pi/2
166 u = r != 0 ? fmax(p / r, eps()) : 1, // sin(theta); but avoid the pole
167 q = a / r;
168 real
169 q2 = Math::sq(q),
170 uq = u * q,
171 uq2 = Math::sq(uq),
172 tu = t / u;
173 // Initialize outer sum
174 real vc = 0, vc2 = 0, vs = 0, vs2 = 0; // v [N + 1], v [N + 2]
175 // vr, vt, vl and similar w variable accumulate the sums for the
176 // derivatives wrt r, theta, and lambda, respectively.
177 real vrc = 0, vrc2 = 0, vrs = 0, vrs2 = 0; // vr[N + 1], vr[N + 2]
178 real vtc = 0, vtc2 = 0, vts = 0, vts2 = 0; // vt[N + 1], vt[N + 2]
179 real vlc = 0, vlc2 = 0, vls = 0, vls2 = 0; // vl[N + 1], vl[N + 2]
180 int k[L];
181 const vector<real>& root( sqrttable() );
182 for (int m = M; m >= 0; --m) { // m = M .. 0
183 // Initialize inner sum
184 real
185 wc = 0, wc2 = 0, ws = 0, ws2 = 0, // w [N - m + 1], w [N - m + 2]
186 wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0, // wr[N - m + 1], wr[N - m + 2]
187 wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; // wt[N - m + 1], wt[N - m + 2]
188 for (int l = 0; l < L; ++l)
189 k[l] = c[l].index(N, m) + 1;
190 for (int n = N; n >= m; --n) { // n = N .. m; l = N - m .. 0
191 real w, A, Ax, B, R; // alpha[l], beta[l + 1]
192 switch (norm) {
193 case FULL:
194 w = root[2 * n + 1] / (root[n - m + 1] * root[n + m + 1]);
195 Ax = q * w * root[2 * n + 3];
196 A = t * Ax;
197 B = - q2 * root[2 * n + 5] /
198 (w * root[n - m + 2] * root[n + m + 2]);
199 break;
200 case SCHMIDT:
201 w = root[n - m + 1] * root[n + m + 1];
202 Ax = q * (2 * n + 1) / w;
203 A = t * Ax;
204 B = - q2 * w / (root[n - m + 2] * root[n + m + 2]);
205 break;
206 default: break; // To suppress warning message from Visual Studio
207 }
208 R = c[0].Cv(--k[0]);
209 for (int l = 1; l < L; ++l)
210 R += c[l].Cv(--k[l], n, m, f[l]);
211 R *= scale();
212 w = A * wc + B * wc2 + R; wc2 = wc; wc = w;
213 if (gradp) {
214 w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w;
215 w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w;
216 }
217 if (m) {
218 R = c[0].Sv(k[0]);
219 for (int l = 1; l < L; ++l)
220 R += c[l].Sv(k[l], n, m, f[l]);
221 R *= scale();
222 w = A * ws + B * ws2 + R; ws2 = ws; ws = w;
223 if (gradp) {
224 w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w;
225 w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w;
226 }
227 }
228 }
229 // Now Sc[m] = wc, Ss[m] = ws
230 // Sc'[m] = wtc, Ss'[m] = wtc
231 if (m) {
232 real v, A, B; // alpha[m], beta[m + 1]
233 switch (norm) {
234 case FULL:
235 v = root[2] * root[2 * m + 3] / root[m + 1];
236 A = cl * v * uq;
237 B = - v * root[2 * m + 5] / (root[8] * root[m + 2]) * uq2;
238 break;
239 case SCHMIDT:
240 v = root[2] * root[2 * m + 1] / root[m + 1];
241 A = cl * v * uq;
242 B = - v * root[2 * m + 3] / (root[8] * root[m + 2]) * uq2;
243 break;
244 default: break; // To suppress warning message from Visual Studio
245 }
246 v = A * vc + B * vc2 + wc ; vc2 = vc ; vc = v;
247 v = A * vs + B * vs2 + ws ; vs2 = vs ; vs = v;
248 if (gradp) {
249 // Include the terms Sc[m] * P'[m,m](t) and Ss[m] * P'[m,m](t)
250 wtc += m * tu * wc; wts += m * tu * ws;
251 v = A * vrc + B * vrc2 + wrc; vrc2 = vrc; vrc = v;
252 v = A * vrs + B * vrs2 + wrs; vrs2 = vrs; vrs = v;
253 v = A * vtc + B * vtc2 + wtc; vtc2 = vtc; vtc = v;
254 v = A * vts + B * vts2 + wts; vts2 = vts; vts = v;
255 v = A * vlc + B * vlc2 + m*ws; vlc2 = vlc; vlc = v;
256 v = A * vls + B * vls2 - m*wc; vls2 = vls; vls = v;
257 }
258 } else {
259 real A, B, qs;
260 switch (norm) {
261 case FULL:
262 A = root[3] * uq; // F[1]/(q*cl) or F[1]/(q*sl)
263 B = - root[15]/2 * uq2; // beta[1]/q
264 break;
265 case SCHMIDT:
266 A = uq;
267 B = - root[3]/2 * uq2;
268 break;
269 default: break; // To suppress warning message from Visual Studio
270 }
271 qs = q / scale();
272 vc = qs * (wc + A * (cl * vc + sl * vs ) + B * vc2);
273 if (gradp) {
274 qs /= r;
275 // The components of the gradient in spherical coordinates are
276 // r: dV/dr
277 // theta: 1/r * dV/dtheta
278 // lambda: 1/(r*u) * dV/dlambda
279 vrc = - qs * (wrc + A * (cl * vrc + sl * vrs) + B * vrc2);
280 vtc = qs * (wtc + A * (cl * vtc + sl * vts) + B * vtc2);
281 vlc = qs / u * ( A * (cl * vlc + sl * vls) + B * vlc2);
282 }
283 }
284 }
285
286 if (gradp) {
287 // Rotate into cartesian (geocentric) coordinates
288 gradx = cl * (u * vrc + t * vtc) - sl * vlc;
289 grady = sl * (u * vrc + t * vtc) + cl * vlc;
290 gradz = t * vrc - u * vtc ;
291 }
292 return vc;
293 }
294
295 template<bool gradp, SphericalEngine::normalization norm, int L>
297 real p, real z, real a) {
298
299 static_assert(L > 0, "L must be positive");
300 static_assert(norm == FULL || norm == SCHMIDT, "Unknown normalization");
301 int N = c[0].nmx(), M = c[0].mmx();
302
303 real
304 r = hypot(z, p),
305 t = r != 0 ? z / r : 0, // cos(theta); at origin, pick theta = pi/2
306 u = r != 0 ? fmax(p / r, eps()) : 1, // sin(theta); but avoid the pole
307 q = a / r;
308 real
309 q2 = Math::sq(q),
310 tu = t / u;
311 CircularEngine circ(M, gradp, norm, a, r, u, t);
312 int k[L];
313 const vector<real>& root( sqrttable() );
314 for (int m = M; m >= 0; --m) { // m = M .. 0
315 // Initialize inner sum
316 real
317 wc = 0, wc2 = 0, ws = 0, ws2 = 0, // w [N - m + 1], w [N - m + 2]
318 wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0, // wr[N - m + 1], wr[N - m + 2]
319 wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; // wt[N - m + 1], wt[N - m + 2]
320 for (int l = 0; l < L; ++l)
321 k[l] = c[l].index(N, m) + 1;
322 for (int n = N; n >= m; --n) { // n = N .. m; l = N - m .. 0
323 real w, A, Ax, B, R; // alpha[l], beta[l + 1]
324 switch (norm) {
325 case FULL:
326 w = root[2 * n + 1] / (root[n - m + 1] * root[n + m + 1]);
327 Ax = q * w * root[2 * n + 3];
328 A = t * Ax;
329 B = - q2 * root[2 * n + 5] /
330 (w * root[n - m + 2] * root[n + m + 2]);
331 break;
332 case SCHMIDT:
333 w = root[n - m + 1] * root[n + m + 1];
334 Ax = q * (2 * n + 1) / w;
335 A = t * Ax;
336 B = - q2 * w / (root[n - m + 2] * root[n + m + 2]);
337 break;
338 default: break; // To suppress warning message from Visual Studio
339 }
340 R = c[0].Cv(--k[0]);
341 for (int l = 1; l < L; ++l)
342 R += c[l].Cv(--k[l], n, m, f[l]);
343 R *= scale();
344 w = A * wc + B * wc2 + R; wc2 = wc; wc = w;
345 if (gradp) {
346 w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w;
347 w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w;
348 }
349 if (m) {
350 R = c[0].Sv(k[0]);
351 for (int l = 1; l < L; ++l)
352 R += c[l].Sv(k[l], n, m, f[l]);
353 R *= scale();
354 w = A * ws + B * ws2 + R; ws2 = ws; ws = w;
355 if (gradp) {
356 w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w;
357 w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w;
358 }
359 }
360 }
361 if (!gradp)
362 circ.SetCoeff(m, wc, ws);
363 else {
364 // Include the terms Sc[m] * P'[m,m](t) and Ss[m] * P'[m,m](t)
365 wtc += m * tu * wc; wts += m * tu * ws;
366 circ.SetCoeff(m, wc, ws, wrc, wrs, wtc, wts);
367 }
368 }
369
370 return circ;
371 }
372
374 // Need square roots up to max(2 * N + 5, 15).
375 vector<real>& root( sqrttable() );
376 int L = max(2 * N + 5, 15) + 1, oldL = int(root.size());
377 if (oldL >= L)
378 return;
379 root.resize(L);
380 for (int l = oldL; l < L; ++l)
381 root[l] = sqrt(real(l));
382 }
383
384 void SphericalEngine::coeff::readcoeffs(istream& stream, int& N, int& M,
385 vector<real>& C,
386 vector<real>& S,
387 bool truncate) {
388 if (truncate) {
389 if (!((N >= M && M >= 0) || (N == -1 && M == -1)))
390 // The last condition is that M = -1 implies N = -1.
391 throw GeographicErr("Bad requested degree and order " +
392 Utility::str(N) + " " + Utility::str(M));
393 }
394 int nm[2];
395 Utility::readarray<int, int, false>(stream, nm, 2);
396 int N0 = nm[0], M0 = nm[1];
397 if (!((N0 >= M0 && M0 >= 0) || (N0 == -1 && M0 == -1)))
398 // The last condition is that M0 = -1 implies N0 = -1.
399 throw GeographicErr("Bad degree and order " +
400 Utility::str(N0) + " " + Utility::str(M0));
401 N = truncate ? min(N, N0) : N0;
402 M = truncate ? min(M, M0) : M0;
403 C.resize(SphericalEngine::coeff::Csize(N, M));
404 S.resize(SphericalEngine::coeff::Ssize(N, M));
405 int skip = (SphericalEngine::coeff::Csize(N0, M0) -
406 SphericalEngine::coeff::Csize(N0, M )) * sizeof(double);
407 if (N == N0) {
408 Utility::readarray<double, real, false>(stream, C);
409 if (skip) stream.seekg(streamoff(skip), ios::cur);
410 Utility::readarray<double, real, false>(stream, S);
411 if (skip) stream.seekg(streamoff(skip), ios::cur);
412 } else {
413 for (int m = 0, k = 0; m <= M; ++m) {
414 Utility::readarray<double, real, false>(stream, &C[k], N + 1 - m);
415 stream.seekg((N0 - N) * sizeof(double), ios::cur);
416 k += N + 1 - m;
417 }
418 if (skip) stream.seekg(streamoff(skip), ios::cur);
419 for (int m = 1, k = 0; m <= M; ++m) {
420 Utility::readarray<double, real, false>(stream, &S[k], N + 1 - m);
421 stream.seekg((N0 - N) * sizeof(double), ios::cur);
422 k += N + 1 - m;
423 }
424 if (skip) stream.seekg(streamoff(skip), ios::cur);
425 }
426 return;
427 }
428
429 /// \cond SKIP
431 SphericalEngine::Value<true, SphericalEngine::FULL, 1>
432 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
434 SphericalEngine::Value<false, SphericalEngine::FULL, 1>
435 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
437 SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 1>
438 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
440 SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 1>
441 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
442
444 SphericalEngine::Value<true, SphericalEngine::FULL, 2>
445 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
447 SphericalEngine::Value<false, SphericalEngine::FULL, 2>
448 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
450 SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 2>
451 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
453 SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 2>
454 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
455
457 SphericalEngine::Value<true, SphericalEngine::FULL, 3>
458 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
460 SphericalEngine::Value<false, SphericalEngine::FULL, 3>
461 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
463 SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 3>
464 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
466 SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 3>
467 (const coeff[], const real[], real, real, real, real, real&, real&, real&);
468
470 SphericalEngine::Circle<true, SphericalEngine::FULL, 1>
471 (const coeff[], const real[], real, real, real);
473 SphericalEngine::Circle<false, SphericalEngine::FULL, 1>
474 (const coeff[], const real[], real, real, real);
476 SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 1>
477 (const coeff[], const real[], real, real, real);
479 SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 1>
480 (const coeff[], const real[], real, real, real);
481
483 SphericalEngine::Circle<true, SphericalEngine::FULL, 2>
484 (const coeff[], const real[], real, real, real);
486 SphericalEngine::Circle<false, SphericalEngine::FULL, 2>
487 (const coeff[], const real[], real, real, real);
489 SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 2>
490 (const coeff[], const real[], real, real, real);
492 SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 2>
493 (const coeff[], const real[], real, real, real);
494
496 SphericalEngine::Circle<true, SphericalEngine::FULL, 3>
497 (const coeff[], const real[], real, real, real);
499 SphericalEngine::Circle<false, SphericalEngine::FULL, 3>
500 (const coeff[], const real[], real, real, real);
502 SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 3>
503 (const coeff[], const real[], real, real, real);
505 SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 3>
506 (const coeff[], const real[], real, real, real);
507 /// \endcond
508
509} // namespace GeographicLib
Header for GeographicLib::CircularEngine class.
#define GEOGRAPHICLIB_EXPORT
Definition Constants.hpp:67
Header for GeographicLib::SphericalEngine class.
Header for GeographicLib::Utility class.
Spherical harmonic sums for a circle.
Exception handling for GeographicLib.
static T sq(T x)
Definition Math.hpp:221
Package up coefficients for SphericalEngine.
static void readcoeffs(std::istream &stream, int &N, int &M, std::vector< real > &C, std::vector< real > &S, bool truncate=false)
static Math::real Value(const coeff c[], const real f[], real x, real y, real z, real a, real &gradx, real &grady, real &gradz)
static CircularEngine Circle(const coeff c[], const real f[], real p, real z, real a)
static std::string str(T x, int p=-1)
Definition Utility.hpp:161
Namespace for GeographicLib.