geographiclib 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/AUTHORS +12 -0
  3. data/LICENSE +24 -0
  4. data/ext/geographiclib/Accumulator.cpp +23 -0
  5. data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
  6. data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
  7. data/ext/geographiclib/CassiniSoldner.cpp +89 -0
  8. data/ext/geographiclib/CircularEngine.cpp +96 -0
  9. data/ext/geographiclib/DMS.cpp +381 -0
  10. data/ext/geographiclib/Ellipsoid.cpp +125 -0
  11. data/ext/geographiclib/EllipticFunction.cpp +512 -0
  12. data/ext/geographiclib/GARS.cpp +122 -0
  13. data/ext/geographiclib/GeoCoords.cpp +175 -0
  14. data/ext/geographiclib/Geocentric.cpp +172 -0
  15. data/ext/geographiclib/Geodesic.cpp +1908 -0
  16. data/ext/geographiclib/GeodesicExact.cpp +927 -0
  17. data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
  18. data/ext/geographiclib/GeodesicLine.cpp +321 -0
  19. data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
  20. data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
  21. data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
  22. data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
  23. data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
  24. data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
  25. data/ext/geographiclib/GeographicLib/Config.h +12 -0
  26. data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
  27. data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
  28. data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
  29. data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
  30. data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
  31. data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
  32. data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
  33. data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
  34. data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
  35. data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
  36. data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
  37. data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
  38. data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
  39. data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
  40. data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
  41. data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
  42. data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
  43. data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
  44. data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
  45. data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
  46. data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
  47. data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
  48. data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
  49. data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
  50. data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
  51. data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
  52. data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
  53. data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
  54. data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
  55. data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
  56. data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
  57. data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
  58. data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
  59. data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
  60. data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
  61. data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
  62. data/ext/geographiclib/Geohash.cpp +102 -0
  63. data/ext/geographiclib/Geoid.cpp +509 -0
  64. data/ext/geographiclib/Georef.cpp +135 -0
  65. data/ext/geographiclib/Gnomonic.cpp +85 -0
  66. data/ext/geographiclib/GravityCircle.cpp +129 -0
  67. data/ext/geographiclib/GravityModel.cpp +360 -0
  68. data/ext/geographiclib/LambertConformalConic.cpp +456 -0
  69. data/ext/geographiclib/LocalCartesian.cpp +62 -0
  70. data/ext/geographiclib/MGRS.cpp +461 -0
  71. data/ext/geographiclib/MagneticCircle.cpp +52 -0
  72. data/ext/geographiclib/MagneticModel.cpp +269 -0
  73. data/ext/geographiclib/Math.cpp +63 -0
  74. data/ext/geographiclib/NormalGravity.cpp +262 -0
  75. data/ext/geographiclib/OSGB.cpp +167 -0
  76. data/ext/geographiclib/PolarStereographic.cpp +108 -0
  77. data/ext/geographiclib/PolygonArea.cpp +204 -0
  78. data/ext/geographiclib/Rhumb.cpp +383 -0
  79. data/ext/geographiclib/SphericalEngine.cpp +477 -0
  80. data/ext/geographiclib/TransverseMercator.cpp +603 -0
  81. data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
  82. data/ext/geographiclib/UTMUPS.cpp +296 -0
  83. data/ext/geographiclib/Utility.cpp +61 -0
  84. data/ext/geographiclib/extconf.rb +3 -0
  85. data/ext/geographiclib/geographiclib.cpp +62 -0
  86. data/lib/geographiclib.rb +20 -0
  87. metadata +140 -0
@@ -0,0 +1,376 @@
1
+ /**
2
+ * \file SphericalEngine.hpp
3
+ * \brief Header for GeographicLib::SphericalEngine class
4
+ *
5
+ * Copyright (c) Charles Karney (2011-2012) <charles@karney.com> and licensed
6
+ * under the MIT/X11 License. For more information, see
7
+ * http://geographiclib.sourceforge.net/
8
+ **********************************************************************/
9
+
10
+ #if !defined(GEOGRAPHICLIB_SPHERICALENGINE_HPP)
11
+ #define GEOGRAPHICLIB_SPHERICALENGINE_HPP 1
12
+
13
+ #include <vector>
14
+ #include <istream>
15
+ #include <GeographicLib/Constants.hpp>
16
+
17
+ #if defined(_MSC_VER)
18
+ // Squelch warnings about dll vs vector
19
+ # pragma warning (push)
20
+ # pragma warning (disable: 4251)
21
+ #endif
22
+
23
+ namespace GeographicLib {
24
+
25
+ class CircularEngine;
26
+
27
+ /**
28
+ * \brief The evaluation engine for SphericalHarmonic
29
+ *
30
+ * This serves as the backend to SphericalHarmonic, SphericalHarmonic1, and
31
+ * SphericalHarmonic2. Typically end-users will not have to access this
32
+ * class directly.
33
+ *
34
+ * See SphericalEngine.cpp for more information on the implementation.
35
+ *
36
+ * Example of use:
37
+ * \include example-SphericalEngine.cpp
38
+ **********************************************************************/
39
+
40
+ class GEOGRAPHICLIB_EXPORT SphericalEngine {
41
+ private:
42
+ typedef Math::real real;
43
+ // A table of the square roots of integers
44
+ static std::vector<real> root_;
45
+ friend class CircularEngine; // CircularEngine needs access to root_, scale_
46
+ // An internal scaling of the coefficients to avoid overflow in
47
+ // intermediate calculations.
48
+ static real scale() {
49
+ using std::pow;
50
+ return pow(real(std::numeric_limits<real>::radix),
51
+ -3 * (std::numeric_limits<real>::max_exponent < (1<<14) ?
52
+ std::numeric_limits<real>::max_exponent : (1<<14)) / 5);
53
+ }
54
+ // Move latitudes near the pole off the axis by this amount.
55
+ static real eps() {
56
+ using std::sqrt;
57
+ return std::numeric_limits<real>::epsilon() *
58
+ sqrt(std::numeric_limits<real>::epsilon());
59
+ }
60
+ static const std::vector<real> Z_;
61
+ SphericalEngine(); // Disable constructor
62
+ public:
63
+ /**
64
+ * Supported normalizations for associated Legendre polynomials.
65
+ **********************************************************************/
66
+ enum normalization {
67
+ /**
68
+ * Fully normalized associated Legendre polynomials. See
69
+ * SphericalHarmonic::FULL for documentation.
70
+ *
71
+ * @hideinitializer
72
+ **********************************************************************/
73
+ FULL = 0,
74
+ /**
75
+ * Schmidt semi-normalized associated Legendre polynomials. See
76
+ * SphericalHarmonic::SCHMIDT for documentation.
77
+ *
78
+ * @hideinitializer
79
+ **********************************************************************/
80
+ SCHMIDT = 1,
81
+ };
82
+
83
+ /**
84
+ * \brief Package up coefficients for SphericalEngine
85
+ *
86
+ * This packages up the \e C, \e S coefficients and information about how
87
+ * the coefficients are stored into a single structure. This allows a
88
+ * vector of type SphericalEngine::coeff to be passed to
89
+ * SphericalEngine::Value. This class also includes functions to aid
90
+ * indexing into \e C and \e S.
91
+ *
92
+ * The storage layout of the coefficients is documented in
93
+ * SphericalHarmonic and SphericalHarmonic::SphericalHarmonic.
94
+ **********************************************************************/
95
+ class GEOGRAPHICLIB_EXPORT coeff {
96
+ private:
97
+ int _Nx, _nmx, _mmx;
98
+ std::vector<real>::const_iterator _Cnm;
99
+ std::vector<real>::const_iterator _Snm;
100
+ public:
101
+ /**
102
+ * A default constructor
103
+ **********************************************************************/
104
+ coeff()
105
+ : _Nx(-1)
106
+ , _nmx(-1)
107
+ , _mmx(-1)
108
+ , _Cnm(Z_.begin())
109
+ , _Snm(Z_.begin()) {}
110
+ /**
111
+ * The general constructor.
112
+ *
113
+ * @param[in] C a vector of coefficients for the cosine terms.
114
+ * @param[in] S a vector of coefficients for the sine terms.
115
+ * @param[in] N the degree giving storage layout for \e C and \e S.
116
+ * @param[in] nmx the maximum degree to be used.
117
+ * @param[in] mmx the maximum order to be used.
118
+ * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
119
+ * \e N &ge; \e nmx &ge; \e mmx &ge; &minus;1.
120
+ * @exception GeographicErr if \e C or \e S is not big enough to hold the
121
+ * coefficients.
122
+ * @exception std::bad_alloc if the memory for the square root table
123
+ * can't be allocated.
124
+ **********************************************************************/
125
+ coeff(const std::vector<real>& C,
126
+ const std::vector<real>& S,
127
+ int N, int nmx, int mmx)
128
+ : _Nx(N)
129
+ , _nmx(nmx)
130
+ , _mmx(mmx)
131
+ , _Cnm(C.begin())
132
+ , _Snm(S.begin())
133
+ {
134
+ if (!(_Nx >= _nmx && _nmx >= _mmx && _mmx >= -1))
135
+ throw GeographicErr("Bad indices for coeff");
136
+ if (!(index(_nmx, _mmx) < int(C.size()) &&
137
+ index(_nmx, _mmx) < int(S.size()) + (_Nx + 1)))
138
+ throw GeographicErr("Arrays too small in coeff");
139
+ SphericalEngine::RootTable(_nmx);
140
+ }
141
+ /**
142
+ * The constructor for full coefficient vectors.
143
+ *
144
+ * @param[in] C a vector of coefficients for the cosine terms.
145
+ * @param[in] S a vector of coefficients for the sine terms.
146
+ * @param[in] N the maximum degree and order.
147
+ * @exception GeographicErr if \e N does not satisfy \e N &ge; &minus;1.
148
+ * @exception GeographicErr if \e C or \e S is not big enough to hold the
149
+ * coefficients.
150
+ * @exception std::bad_alloc if the memory for the square root table
151
+ * can't be allocated.
152
+ **********************************************************************/
153
+ coeff(const std::vector<real>& C,
154
+ const std::vector<real>& S,
155
+ int N)
156
+ : _Nx(N)
157
+ , _nmx(N)
158
+ , _mmx(N)
159
+ , _Cnm(C.begin())
160
+ , _Snm(S.begin())
161
+ {
162
+ if (!(_Nx >= -1))
163
+ throw GeographicErr("Bad indices for coeff");
164
+ if (!(index(_nmx, _mmx) < int(C.size()) &&
165
+ index(_nmx, _mmx) < int(S.size()) + (_Nx + 1)))
166
+ throw GeographicErr("Arrays too small in coeff");
167
+ SphericalEngine::RootTable(_nmx);
168
+ }
169
+ /**
170
+ * @return \e N the degree giving storage layout for \e C and \e S.
171
+ **********************************************************************/
172
+ inline int N() const { return _Nx; }
173
+ /**
174
+ * @return \e nmx the maximum degree to be used.
175
+ **********************************************************************/
176
+ inline int nmx() const { return _nmx; }
177
+ /**
178
+ * @return \e mmx the maximum order to be used.
179
+ **********************************************************************/
180
+ inline int mmx() const { return _mmx; }
181
+ /**
182
+ * The one-dimensional index into \e C and \e S.
183
+ *
184
+ * @param[in] n the degree.
185
+ * @param[in] m the order.
186
+ * @return the one-dimensional index.
187
+ **********************************************************************/
188
+ inline int index(int n, int m) const
189
+ { return m * _Nx - m * (m - 1) / 2 + n; }
190
+ /**
191
+ * An element of \e C.
192
+ *
193
+ * @param[in] k the one-dimensional index.
194
+ * @return the value of the \e C coefficient.
195
+ **********************************************************************/
196
+ inline Math::real Cv(int k) const { return *(_Cnm + k); }
197
+ /**
198
+ * An element of \e S.
199
+ *
200
+ * @param[in] k the one-dimensional index.
201
+ * @return the value of the \e S coefficient.
202
+ **********************************************************************/
203
+ inline Math::real Sv(int k) const { return *(_Snm + (k - (_Nx + 1))); }
204
+ /**
205
+ * An element of \e C with checking.
206
+ *
207
+ * @param[in] k the one-dimensional index.
208
+ * @param[in] n the requested degree.
209
+ * @param[in] m the requested order.
210
+ * @param[in] f a multiplier.
211
+ * @return the value of the \e C coefficient multiplied by \e f in \e n
212
+ * and \e m are in range else 0.
213
+ **********************************************************************/
214
+ inline Math::real Cv(int k, int n, int m, real f) const
215
+ { return m > _mmx || n > _nmx ? 0 : *(_Cnm + k) * f; }
216
+ /**
217
+ * An element of \e S with checking.
218
+ *
219
+ * @param[in] k the one-dimensional index.
220
+ * @param[in] n the requested degree.
221
+ * @param[in] m the requested order.
222
+ * @param[in] f a multiplier.
223
+ * @return the value of the \e S coefficient multiplied by \e f in \e n
224
+ * and \e m are in range else 0.
225
+ **********************************************************************/
226
+ inline Math::real Sv(int k, int n, int m, real f) const
227
+ { return m > _mmx || n > _nmx ? 0 : *(_Snm + (k - (_Nx + 1))) * f; }
228
+
229
+ /**
230
+ * The size of the coefficient vector for the cosine terms.
231
+ *
232
+ * @param[in] N the maximum degree.
233
+ * @param[in] M the maximum order.
234
+ * @return the size of the vector of cosine terms as stored in column
235
+ * major order.
236
+ **********************************************************************/
237
+ static inline int Csize(int N, int M)
238
+ { return (M + 1) * (2 * N - M + 2) / 2; }
239
+
240
+ /**
241
+ * The size of the coefficient vector for the sine terms.
242
+ *
243
+ * @param[in] N the maximum degree.
244
+ * @param[in] M the maximum order.
245
+ * @return the size of the vector of cosine terms as stored in column
246
+ * major order.
247
+ **********************************************************************/
248
+ static inline int Ssize(int N, int M)
249
+ { return Csize(N, M) - (N + 1); }
250
+
251
+ /**
252
+ * Load coefficients from a binary stream.
253
+ *
254
+ * @param[in] stream the input stream.
255
+ * @param[out] N The maximum degree of the coefficients.
256
+ * @param[out] M The maximum order of the coefficients.
257
+ * @param[out] C The vector of cosine coefficients.
258
+ * @param[out] S The vector of sine coefficients.
259
+ * @exception GeographicErr if \e N and \e M do not satisfy \e N &ge;
260
+ * \e M &ge; &minus;1.
261
+ * @exception GeographicErr if there's an error reading the data.
262
+ * @exception std::bad_alloc if the memory for \e C or \e S can't be
263
+ * allocated.
264
+ *
265
+ * \e N and \e M are read as 4-byte ints. \e C and \e S are resized to
266
+ * accommodate all the coefficients (with the \e m = 0 coefficients for
267
+ * \e S excluded) and the data for these coefficients read as 8-byte
268
+ * doubles. The coefficients are stored in column major order. The
269
+ * bytes in the stream should use little-endian ordering. IEEE floating
270
+ * point is assumed for the coefficients.
271
+ **********************************************************************/
272
+ static void readcoeffs(std::istream& stream, int& N, int& M,
273
+ std::vector<real>& C, std::vector<real>& S);
274
+ };
275
+
276
+ /**
277
+ * Evaluate a spherical harmonic sum and its gradient.
278
+ *
279
+ * @tparam gradp should the gradient be calculated.
280
+ * @tparam norm the normalization for the associated Legendre polynomials.
281
+ * @tparam L the number of terms in the coefficients.
282
+ * @param[in] c an array of coeff objects.
283
+ * @param[in] f array of coefficient multipliers. f[0] should be 1.
284
+ * @param[in] x the \e x component of the cartesian position.
285
+ * @param[in] y the \e y component of the cartesian position.
286
+ * @param[in] z the \e z component of the cartesian position.
287
+ * @param[in] a the normalizing radius.
288
+ * @param[out] gradx the \e x component of the gradient.
289
+ * @param[out] grady the \e y component of the gradient.
290
+ * @param[out] gradz the \e z component of the gradient.
291
+ * @result the spherical harmonic sum.
292
+ *
293
+ * See the SphericalHarmonic class for the definition of the sum. The
294
+ * coefficients used by this function are, for example, c[0].Cv + f[1] *
295
+ * c[1].Cv + ... + f[L&minus;1] * c[L&minus;1].Cv. (Note that f[0] is \e
296
+ * not used.) The upper limits on the sum are determined by c[0].nmx() and
297
+ * c[0].mmx(); these limits apply to \e all the components of the
298
+ * coefficients. The parameters \e gradp, \e norm, and \e L are template
299
+ * parameters, to allow more optimization to be done at compile time.
300
+ *
301
+ * Clenshaw summation is used which permits the evaluation of the sum
302
+ * without the need to allocate temporary arrays. Thus this function never
303
+ * throws an exception.
304
+ **********************************************************************/
305
+ template<bool gradp, normalization norm, int L>
306
+ static Math::real Value(const coeff c[], const real f[],
307
+ real x, real y, real z, real a,
308
+ real& gradx, real& grady, real& gradz);
309
+
310
+ /**
311
+ * Create a CircularEngine object
312
+ *
313
+ * @tparam gradp should the gradient be calculated.
314
+ * @tparam norm the normalization for the associated Legendre polynomials.
315
+ * @tparam L the number of terms in the coefficients.
316
+ * @param[in] c an array of coeff objects.
317
+ * @param[in] f array of coefficient multipliers. f[0] should be 1.
318
+ * @param[in] p the radius of the circle = sqrt(<i>x</i><sup>2</sup> +
319
+ * <i>y</i><sup>2</sup>).
320
+ * @param[in] z the height of the circle.
321
+ * @param[in] a the normalizing radius.
322
+ * @exception std::bad_alloc if the memory for the CircularEngine can't be
323
+ * allocated.
324
+ * @result the CircularEngine object.
325
+ *
326
+ * If you need to evaluate the spherical harmonic sum for several points
327
+ * with constant \e f, \e p = sqrt(<i>x</i><sup>2</sup> +
328
+ * <i>y</i><sup>2</sup>), \e z, and \e a, it is more efficient to construct
329
+ * call SphericalEngine::Circle to give a CircularEngine object and then
330
+ * call CircularEngine::operator()() with arguments <i>x</i>/\e p and
331
+ * <i>y</i>/\e p.
332
+ **********************************************************************/
333
+ template<bool gradp, normalization norm, int L>
334
+ static CircularEngine Circle(const coeff c[], const real f[],
335
+ real p, real z, real a);
336
+ /**
337
+ * Check that the static table of square roots is big enough and enlarge it
338
+ * if necessary.
339
+ *
340
+ * @param[in] N the maximum degree to be used in SphericalEngine.
341
+ * @exception std::bad_alloc if the memory for the square root table can't
342
+ * be allocated.
343
+ *
344
+ * Typically, there's no need for an end-user to call this routine, because
345
+ * the constructors for SphericalEngine::coeff do so. However, since this
346
+ * updates a static table, there's a possible race condition in a
347
+ * multi-threaded environment. Because this routine does nothing if the
348
+ * table is already large enough, one way to avoid race conditions is to
349
+ * call this routine at program start up (when it's still single threaded),
350
+ * supplying the largest degree that your program will use. E.g., \code
351
+ GeographicLib::SphericalEngine::RootTable(2190);
352
+ \endcode
353
+ * suffices to accommodate extant magnetic and gravity models.
354
+ **********************************************************************/
355
+ static void RootTable(int N);
356
+
357
+ /**
358
+ * Clear the static table of square roots and release the memory. Call
359
+ * this only when you are sure you no longer will be using SphericalEngine.
360
+ * Your program will crash if you call SphericalEngine after calling this
361
+ * routine. <b>It's safest not to call this routine at all.</b> (The space
362
+ * used by the table is modest.)
363
+ **********************************************************************/
364
+ static void ClearRootTable() {
365
+ std::vector<real> temp(0);
366
+ root_.swap(temp);
367
+ }
368
+ };
369
+
370
+ } // namespace GeographicLib
371
+
372
+ #if defined(_MSC_VER)
373
+ # pragma warning (pop)
374
+ #endif
375
+
376
+ #endif // GEOGRAPHICLIB_SPHERICALENGINE_HPP
@@ -0,0 +1,354 @@
1
+ /**
2
+ * \file SphericalHarmonic.hpp
3
+ * \brief Header for GeographicLib::SphericalHarmonic class
4
+ *
5
+ * Copyright (c) Charles Karney (2011) <charles@karney.com> and licensed under
6
+ * the MIT/X11 License. For more information, see
7
+ * http://geographiclib.sourceforge.net/
8
+ **********************************************************************/
9
+
10
+ #if !defined(GEOGRAPHICLIB_SPHERICALHARMONIC_HPP)
11
+ #define GEOGRAPHICLIB_SPHERICALHARMONIC_HPP 1
12
+
13
+ #include <vector>
14
+ #include <GeographicLib/Constants.hpp>
15
+ #include <GeographicLib/SphericalEngine.hpp>
16
+ #include <GeographicLib/CircularEngine.hpp>
17
+
18
+ namespace GeographicLib {
19
+
20
+ /**
21
+ * \brief Spherical harmonic series
22
+ *
23
+ * This class evaluates the spherical harmonic sum \verbatim
24
+ V(x, y, z) = sum(n = 0..N)[ q^(n+1) * sum(m = 0..n)[
25
+ (C[n,m] * cos(m*lambda) + S[n,m] * sin(m*lambda)) *
26
+ P[n,m](cos(theta)) ] ]
27
+ \endverbatim
28
+ * where
29
+ * - <i>p</i><sup>2</sup> = <i>x</i><sup>2</sup> + <i>y</i><sup>2</sup>,
30
+ * - <i>r</i><sup>2</sup> = <i>p</i><sup>2</sup> + <i>z</i><sup>2</sup>,
31
+ * - \e q = <i>a</i>/<i>r</i>,
32
+ * - &theta; = atan2(\e p, \e z) = the spherical \e colatitude,
33
+ * - &lambda; = atan2(\e y, \e x) = the longitude.
34
+ * - P<sub><i>nm</i></sub>(\e t) is the associated Legendre polynomial of
35
+ * degree \e n and order \e m.
36
+ *
37
+ * Two normalizations are supported for P<sub><i>nm</i></sub>
38
+ * - fully normalized denoted by SphericalHarmonic::FULL.
39
+ * - Schmidt semi-normalized denoted by SphericalHarmonic::SCHMIDT.
40
+ *
41
+ * Clenshaw summation is used for the sums over both \e n and \e m. This
42
+ * allows the computation to be carried out without the need for any
43
+ * temporary arrays. See SphericalEngine.cpp for more information on the
44
+ * implementation.
45
+ *
46
+ * References:
47
+ * - C. W. Clenshaw,
48
+ * <a href="https://dx.doi.org/10.1090/S0025-5718-1955-0071856-0">
49
+ * A note on the summation of Chebyshev series</a>,
50
+ * %Math. Tables Aids Comput. 9(51), 118--120 (1955).
51
+ * - R. E. Deakin, Derivatives of the earth's potentials, Geomatics
52
+ * Research Australasia 68, 31--60, (June 1998).
53
+ * - W. A. Heiskanen and H. Moritz, Physical Geodesy, (Freeman, San
54
+ * Francisco, 1967). (See Sec. 1-14, for a definition of Pbar.)
55
+ * - S. A. Holmes and W. E. Featherstone,
56
+ * <a href="https://dx.doi.org/10.1007/s00190-002-0216-2">
57
+ * A unified approach to the Clenshaw summation and the recursive
58
+ * computation of very high degree and order normalised associated Legendre
59
+ * functions</a>, J. Geodesy 76(5), 279--299 (2002).
60
+ * - C. C. Tscherning and K. Poder,
61
+ * <a href="http://cct.gfy.ku.dk/publ_cct/cct80.pdf">
62
+ * Some geodetic applications of Clenshaw summation</a>,
63
+ * Boll. Geod. Sci. Aff. 41(4), 349--375 (1982).
64
+ *
65
+ * Example of use:
66
+ * \include example-SphericalHarmonic.cpp
67
+ **********************************************************************/
68
+
69
+ class GEOGRAPHICLIB_EXPORT SphericalHarmonic {
70
+ public:
71
+ /**
72
+ * Supported normalizations for the associated Legendre polynomials.
73
+ **********************************************************************/
74
+ enum normalization {
75
+ /**
76
+ * Fully normalized associated Legendre polynomials.
77
+ *
78
+ * These are defined by
79
+ * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(\e z)
80
+ * = (&minus;1)<sup><i>m</i></sup>
81
+ * sqrt(\e k (2\e n + 1) (\e n &minus; \e m)! / (\e n + \e m)!)
82
+ * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
83
+ * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
84
+ * function (also known as the Legendre function on the cut or the
85
+ * associated Legendre polynomial) http://dlmf.nist.gov/14.7.E10 and \e k
86
+ * = 1 for \e m = 0 and \e k = 2 otherwise.
87
+ *
88
+ * The mean squared value of
89
+ * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
90
+ * cos(<i>m</i>&lambda;) and
91
+ * <i>P</i><sub><i>nm</i></sub><sup>full</sup>(cos&theta;)
92
+ * sin(<i>m</i>&lambda;) over the sphere is 1.
93
+ *
94
+ * @hideinitializer
95
+ **********************************************************************/
96
+ FULL = SphericalEngine::FULL,
97
+ /**
98
+ * Schmidt semi-normalized associated Legendre polynomials.
99
+ *
100
+ * These are defined by
101
+ * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(\e z)
102
+ * = (&minus;1)<sup><i>m</i></sup>
103
+ * sqrt(\e k (\e n &minus; \e m)! / (\e n + \e m)!)
104
+ * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z), where
105
+ * <b>P</b><sub><i>n</i></sub><sup><i>m</i></sup>(\e z) is Ferrers
106
+ * function (also known as the Legendre function on the cut or the
107
+ * associated Legendre polynomial) http://dlmf.nist.gov/14.7.E10 and \e k
108
+ * = 1 for \e m = 0 and \e k = 2 otherwise.
109
+ *
110
+ * The mean squared value of
111
+ * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
112
+ * cos(<i>m</i>&lambda;) and
113
+ * <i>P</i><sub><i>nm</i></sub><sup>schmidt</sup>(cos&theta;)
114
+ * sin(<i>m</i>&lambda;) over the sphere is 1/(2\e n + 1).
115
+ *
116
+ * @hideinitializer
117
+ **********************************************************************/
118
+ SCHMIDT = SphericalEngine::SCHMIDT,
119
+ };
120
+
121
+ private:
122
+ typedef Math::real real;
123
+ SphericalEngine::coeff _c[1];
124
+ real _a;
125
+ unsigned _norm;
126
+
127
+ public:
128
+ /**
129
+ * Constructor with a full set of coefficients specified.
130
+ *
131
+ * @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
132
+ * @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
133
+ * @param[in] N the maximum degree and order of the sum
134
+ * @param[in] a the reference radius appearing in the definition of the
135
+ * sum.
136
+ * @param[in] norm the normalization for the associated Legendre
137
+ * polynomials, either SphericalHarmonic::FULL (the default) or
138
+ * SphericalHarmonic::SCHMIDT.
139
+ * @exception GeographicErr if \e N does not satisfy \e N &ge; &minus;1.
140
+ * @exception GeographicErr if \e C or \e S is not big enough to hold the
141
+ * coefficients.
142
+ *
143
+ * The coefficients <i>C</i><sub><i>nm</i></sub> and
144
+ * <i>S</i><sub><i>nm</i></sub> are stored in the one-dimensional vectors
145
+ * \e C and \e S which must contain (\e N + 1)(\e N + 2)/2 and \e N (\e N +
146
+ * 1)/2 elements, respectively, stored in "column-major" order. Thus for
147
+ * \e N = 3, the order would be:
148
+ * <i>C</i><sub>00</sub>,
149
+ * <i>C</i><sub>10</sub>,
150
+ * <i>C</i><sub>20</sub>,
151
+ * <i>C</i><sub>30</sub>,
152
+ * <i>C</i><sub>11</sub>,
153
+ * <i>C</i><sub>21</sub>,
154
+ * <i>C</i><sub>31</sub>,
155
+ * <i>C</i><sub>22</sub>,
156
+ * <i>C</i><sub>32</sub>,
157
+ * <i>C</i><sub>33</sub>.
158
+ * In general the (\e n,\e m) element is at index \e m \e N &minus; \e m
159
+ * (\e m &minus; 1)/2 + \e n. The layout of \e S is the same except that
160
+ * the first column is omitted (since the \e m = 0 terms never contribute
161
+ * to the sum) and the 0th element is <i>S</i><sub>11</sub>
162
+ *
163
+ * The class stores <i>pointers</i> to the first elements of \e C and \e S.
164
+ * These arrays should not be altered or destroyed during the lifetime of a
165
+ * SphericalHarmonic object.
166
+ **********************************************************************/
167
+ SphericalHarmonic(const std::vector<real>& C,
168
+ const std::vector<real>& S,
169
+ int N, real a, unsigned norm = FULL)
170
+ : _a(a)
171
+ , _norm(norm)
172
+ { _c[0] = SphericalEngine::coeff(C, S, N); }
173
+
174
+ /**
175
+ * Constructor with a subset of coefficients specified.
176
+ *
177
+ * @param[in] C the coefficients <i>C</i><sub><i>nm</i></sub>.
178
+ * @param[in] S the coefficients <i>S</i><sub><i>nm</i></sub>.
179
+ * @param[in] N the degree used to determine the layout of \e C and \e S.
180
+ * @param[in] nmx the maximum degree used in the sum. The sum over \e n is
181
+ * from 0 thru \e nmx.
182
+ * @param[in] mmx the maximum order used in the sum. The sum over \e m is
183
+ * from 0 thru min(\e n, \e mmx).
184
+ * @param[in] a the reference radius appearing in the definition of the
185
+ * sum.
186
+ * @param[in] norm the normalization for the associated Legendre
187
+ * polynomials, either SphericalHarmonic::FULL (the default) or
188
+ * SphericalHarmonic::SCHMIDT.
189
+ * @exception GeographicErr if \e N, \e nmx, and \e mmx do not satisfy
190
+ * \e N &ge; \e nmx &ge; \e mmx &ge; &minus;1.
191
+ * @exception GeographicErr if \e C or \e S is not big enough to hold the
192
+ * coefficients.
193
+ *
194
+ * The class stores <i>pointers</i> to the first elements of \e C and \e S.
195
+ * These arrays should not be altered or destroyed during the lifetime of a
196
+ * SphericalHarmonic object.
197
+ **********************************************************************/
198
+ SphericalHarmonic(const std::vector<real>& C,
199
+ const std::vector<real>& S,
200
+ int N, int nmx, int mmx,
201
+ real a, unsigned norm = FULL)
202
+ : _a(a)
203
+ , _norm(norm)
204
+ { _c[0] = SphericalEngine::coeff(C, S, N, nmx, mmx); }
205
+
206
+ /**
207
+ * A default constructor so that the object can be created when the
208
+ * constructor for another object is initialized. This default object can
209
+ * then be reset with the default copy assignment operator.
210
+ **********************************************************************/
211
+ SphericalHarmonic() {}
212
+
213
+ /**
214
+ * Compute the spherical harmonic sum.
215
+ *
216
+ * @param[in] x cartesian coordinate.
217
+ * @param[in] y cartesian coordinate.
218
+ * @param[in] z cartesian coordinate.
219
+ * @return \e V the spherical harmonic sum.
220
+ *
221
+ * This routine requires constant memory and thus never throws an
222
+ * exception.
223
+ **********************************************************************/
224
+ Math::real operator()(real x, real y, real z) const {
225
+ real f[] = {1};
226
+ real v = 0;
227
+ real dummy;
228
+ switch (_norm) {
229
+ case FULL:
230
+ v = SphericalEngine::Value<false, SphericalEngine::FULL, 1>
231
+ (_c, f, x, y, z, _a, dummy, dummy, dummy);
232
+ break;
233
+ case SCHMIDT:
234
+ v = SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 1>
235
+ (_c, f, x, y, z, _a, dummy, dummy, dummy);
236
+ break;
237
+ }
238
+ return v;
239
+ }
240
+
241
+ /**
242
+ * Compute a spherical harmonic sum and its gradient.
243
+ *
244
+ * @param[in] x cartesian coordinate.
245
+ * @param[in] y cartesian coordinate.
246
+ * @param[in] z cartesian coordinate.
247
+ * @param[out] gradx \e x component of the gradient
248
+ * @param[out] grady \e y component of the gradient
249
+ * @param[out] gradz \e z component of the gradient
250
+ * @return \e V the spherical harmonic sum.
251
+ *
252
+ * This is the same as the previous function, except that the components of
253
+ * the gradients of the sum in the \e x, \e y, and \e z directions are
254
+ * computed. This routine requires constant memory and thus never throws
255
+ * an exception.
256
+ **********************************************************************/
257
+ Math::real operator()(real x, real y, real z,
258
+ real& gradx, real& grady, real& gradz) const {
259
+ real f[] = {1};
260
+ real v = 0;
261
+ switch (_norm) {
262
+ case FULL:
263
+ v = SphericalEngine::Value<true, SphericalEngine::FULL, 1>
264
+ (_c, f, x, y, z, _a, gradx, grady, gradz);
265
+ break;
266
+ case SCHMIDT:
267
+ v = SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 1>
268
+ (_c, f, x, y, z, _a, gradx, grady, gradz);
269
+ break;
270
+ }
271
+ return v;
272
+ }
273
+
274
+ /**
275
+ * Create a CircularEngine to allow the efficient evaluation of several
276
+ * points on a circle of latitude.
277
+ *
278
+ * @param[in] p the radius of the circle.
279
+ * @param[in] z the height of the circle above the equatorial plane.
280
+ * @param[in] gradp if true the returned object will be able to compute the
281
+ * gradient of the sum.
282
+ * @exception std::bad_alloc if the memory for the CircularEngine can't be
283
+ * allocated.
284
+ * @return the CircularEngine object.
285
+ *
286
+ * SphericalHarmonic::operator()() exchanges the order of the sums in the
287
+ * definition, i.e., &sum;<sub><i>n</i> = 0..<i>N</i></sub>
288
+ * &sum;<sub><i>m</i> = 0..<i>n</i></sub> becomes &sum;<sub><i>m</i> =
289
+ * 0..<i>N</i></sub> &sum;<sub><i>n</i> = <i>m</i>..<i>N</i></sub>.
290
+ * SphericalHarmonic::Circle performs the inner sum over degree \e n (which
291
+ * entails about <i>N</i><sup>2</sup> operations). Calling
292
+ * CircularEngine::operator()() on the returned object performs the outer
293
+ * sum over the order \e m (about \e N operations).
294
+ *
295
+ * Here's an example of computing the spherical sum at a sequence of
296
+ * longitudes without using a CircularEngine object \code
297
+ SphericalHarmonic h(...); // Create the SphericalHarmonic object
298
+ double r = 2, lat = 33, lon0 = 44, dlon = 0.01;
299
+ double
300
+ phi = lat * Math::degree<double>(),
301
+ z = r * sin(phi), p = r * cos(phi);
302
+ for (int i = 0; i <= 100; ++i) {
303
+ real
304
+ lon = lon0 + i * dlon,
305
+ lam = lon * Math::degree<double>();
306
+ std::cout << lon << " " << h(p * cos(lam), p * sin(lam), z) << "\n";
307
+ }
308
+ \endcode
309
+ * Here is the same calculation done using a CircularEngine object. This
310
+ * will be about <i>N</i>/2 times faster. \code
311
+ SphericalHarmonic h(...); // Create the SphericalHarmonic object
312
+ double r = 2, lat = 33, lon0 = 44, dlon = 0.01;
313
+ double
314
+ phi = lat * Math::degree<double>(),
315
+ z = r * sin(phi), p = r * cos(phi);
316
+ CircularEngine c(h(p, z, false)); // Create the CircularEngine object
317
+ for (int i = 0; i <= 100; ++i) {
318
+ real
319
+ lon = lon0 + i * dlon;
320
+ std::cout << lon << " " << c(lon) << "\n";
321
+ }
322
+ \endcode
323
+ **********************************************************************/
324
+ CircularEngine Circle(real p, real z, bool gradp) const {
325
+ real f[] = {1};
326
+ switch (_norm) {
327
+ case FULL:
328
+ return gradp ?
329
+ SphericalEngine::Circle<true, SphericalEngine::FULL, 1>
330
+ (_c, f, p, z, _a) :
331
+ SphericalEngine::Circle<false, SphericalEngine::FULL, 1>
332
+ (_c, f, p, z, _a);
333
+ break;
334
+ case SCHMIDT:
335
+ default: // To avoid compiler warnings
336
+ return gradp ?
337
+ SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 1>
338
+ (_c, f, p, z, _a) :
339
+ SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 1>
340
+ (_c, f, p, z, _a);
341
+ break;
342
+ }
343
+ }
344
+
345
+ /**
346
+ * @return the zeroth SphericalEngine::coeff object.
347
+ **********************************************************************/
348
+ const SphericalEngine::coeff& Coefficients() const
349
+ { return _c[0]; }
350
+ };
351
+
352
+ } // namespace GeographicLib
353
+
354
+ #endif // GEOGRAPHICLIB_SPHERICALHARMONIC_HPP