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,383 @@
1
+ /**
2
+ * \file Rhumb.cpp
3
+ * \brief Implementation for GeographicLib::Rhumb and GeographicLib::RhumbLine
4
+ * classes
5
+ *
6
+ * Copyright (c) Charles Karney (2014-2015) <charles@karney.com> and licensed
7
+ * under the MIT/X11 License. For more information, see
8
+ * http://geographiclib.sourceforge.net/
9
+ **********************************************************************/
10
+
11
+ #include <algorithm>
12
+ #include <GeographicLib/Rhumb.hpp>
13
+
14
+ namespace GeographicLib {
15
+
16
+ using namespace std;
17
+
18
+ Rhumb::Rhumb(real a, real f, bool exact)
19
+ : _ell(a, f)
20
+ , _exact(exact)
21
+ , _c2(_ell.Area() / 720)
22
+ {
23
+ // Generated by Maxima on 2015-05-15 08:24:04-04:00
24
+ #if GEOGRAPHICLIB_RHUMBAREA_ORDER == 4
25
+ static const real coeff[] = {
26
+ // R[0]/n^0, polynomial in n of order 4
27
+ 691, 7860, -20160, 18900, 0, 56700,
28
+ // R[1]/n^1, polynomial in n of order 3
29
+ 1772, -5340, 6930, -4725, 14175,
30
+ // R[2]/n^2, polynomial in n of order 2
31
+ -1747, 1590, -630, 4725,
32
+ // R[3]/n^3, polynomial in n of order 1
33
+ 104, -31, 315,
34
+ // R[4]/n^4, polynomial in n of order 0
35
+ -41, 420,
36
+ }; // count = 20
37
+ #elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 5
38
+ static const real coeff[] = {
39
+ // R[0]/n^0, polynomial in n of order 5
40
+ -79036, 22803, 259380, -665280, 623700, 0, 1871100,
41
+ // R[1]/n^1, polynomial in n of order 4
42
+ 41662, 58476, -176220, 228690, -155925, 467775,
43
+ // R[2]/n^2, polynomial in n of order 3
44
+ 18118, -57651, 52470, -20790, 155925,
45
+ // R[3]/n^3, polynomial in n of order 2
46
+ -23011, 17160, -5115, 51975,
47
+ // R[4]/n^4, polynomial in n of order 1
48
+ 5480, -1353, 13860,
49
+ // R[5]/n^5, polynomial in n of order 0
50
+ -668, 5775,
51
+ }; // count = 27
52
+ #elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 6
53
+ static const real coeff[] = {
54
+ // R[0]/n^0, polynomial in n of order 6
55
+ 128346268, -107884140, 31126095, 354053700, -908107200, 851350500, 0,
56
+ 2554051500LL,
57
+ // R[1]/n^1, polynomial in n of order 5
58
+ -114456994, 56868630, 79819740, -240540300, 312161850, -212837625,
59
+ 638512875,
60
+ // R[2]/n^2, polynomial in n of order 4
61
+ 51304574, 24731070, -78693615, 71621550, -28378350, 212837625,
62
+ // R[3]/n^3, polynomial in n of order 3
63
+ 1554472, -6282003, 4684680, -1396395, 14189175,
64
+ // R[4]/n^4, polynomial in n of order 2
65
+ -4913956, 3205800, -791505, 8108100,
66
+ // R[5]/n^5, polynomial in n of order 1
67
+ 1092376, -234468, 2027025,
68
+ // R[6]/n^6, polynomial in n of order 0
69
+ -313076, 2027025,
70
+ }; // count = 35
71
+ #elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 7
72
+ static const real coeff[] = {
73
+ // R[0]/n^0, polynomial in n of order 7
74
+ -317195588, 385038804, -323652420, 93378285, 1062161100, -2724321600LL,
75
+ 2554051500LL, 0, 7662154500LL,
76
+ // R[1]/n^1, polynomial in n of order 6
77
+ 258618446, -343370982, 170605890, 239459220, -721620900, 936485550,
78
+ -638512875, 1915538625,
79
+ // R[2]/n^2, polynomial in n of order 5
80
+ -248174686, 153913722, 74193210, -236080845, 214864650, -85135050,
81
+ 638512875,
82
+ // R[3]/n^3, polynomial in n of order 4
83
+ 114450437, 23317080, -94230045, 70270200, -20945925, 212837625,
84
+ // R[4]/n^4, polynomial in n of order 3
85
+ 15445736, -103193076, 67321800, -16621605, 170270100,
86
+ // R[5]/n^5, polynomial in n of order 2
87
+ -27766753, 16385640, -3517020, 30405375,
88
+ // R[6]/n^6, polynomial in n of order 1
89
+ 4892722, -939228, 6081075,
90
+ // R[7]/n^7, polynomial in n of order 0
91
+ -3189007, 14189175,
92
+ }; // count = 44
93
+ #elif GEOGRAPHICLIB_RHUMBAREA_ORDER == 8
94
+ static const real coeff[] = {
95
+ // R[0]/n^0, polynomial in n of order 8
96
+ 71374704821LL, -161769749880LL, 196369790040LL, -165062734200LL,
97
+ 47622925350LL, 541702161000LL, -1389404016000LL, 1302566265000LL, 0,
98
+ 3907698795000LL,
99
+ // R[1]/n^1, polynomial in n of order 7
100
+ -13691187484LL, 65947703730LL, -87559600410LL, 43504501950LL,
101
+ 61062101100LL, -184013329500LL, 238803815250LL, -162820783125LL,
102
+ 488462349375LL,
103
+ // R[2]/n^2, polynomial in n of order 6
104
+ 30802104839LL, -63284544930LL, 39247999110LL, 18919268550LL,
105
+ -60200615475LL, 54790485750LL, -21709437750LL, 162820783125LL,
106
+ // R[3]/n^3, polynomial in n of order 5
107
+ -8934064508LL, 5836972287LL, 1189171080, -4805732295LL, 3583780200LL,
108
+ -1068242175, 10854718875LL,
109
+ // R[4]/n^4, polynomial in n of order 4
110
+ 50072287748LL, 3938662680LL, -26314234380LL, 17167059000LL,
111
+ -4238509275LL, 43418875500LL,
112
+ // R[5]/n^5, polynomial in n of order 3
113
+ 359094172, -9912730821LL, 5849673480LL, -1255576140, 10854718875LL,
114
+ // R[6]/n^6, polynomial in n of order 2
115
+ -16053944387LL, 8733508770LL, -1676521980, 10854718875LL,
116
+ // R[7]/n^7, polynomial in n of order 1
117
+ 930092876, -162639357, 723647925,
118
+ // R[8]/n^8, polynomial in n of order 0
119
+ -673429061, 1929727800,
120
+ }; // count = 54
121
+ #else
122
+ #error "Bad value for GEOGRAPHICLIB_RHUMBAREA_ORDER"
123
+ #endif
124
+ GEOGRAPHICLIB_STATIC_ASSERT(sizeof(coeff) / sizeof(real) ==
125
+ ((maxpow_ + 1) * (maxpow_ + 4))/2,
126
+ "Coefficient array size mismatch for Rhumb");
127
+ real d = 1;
128
+ int o = 0;
129
+ for (int l = 0; l <= maxpow_; ++l) {
130
+ int m = maxpow_ - l;
131
+ // R[0] is just an integration constant so it cancels when evaluating a
132
+ // definite integral. So don't bother computing it. It won't be used
133
+ // when invoking SinCosSeries.
134
+ if (l)
135
+ _R[l] = d * Math::polyval(m, coeff + o, _ell._n) / coeff[o + m + 1];
136
+ o += m + 2;
137
+ d *= _ell._n;
138
+ }
139
+ // Post condition: o == sizeof(alpcoeff) / sizeof(real)
140
+ }
141
+
142
+ const Rhumb& Rhumb::WGS84() {
143
+ static const Rhumb wgs84(Constants::WGS84_a(), Constants::WGS84_f(), false);
144
+ return wgs84;
145
+ }
146
+
147
+ void Rhumb::GenInverse(real lat1, real lon1, real lat2, real lon2,
148
+ unsigned outmask,
149
+ real& s12, real& azi12, real& S12) const {
150
+ real
151
+ lon12 = Math::AngDiff(lon1, lon2),
152
+ psi1 = _ell.IsometricLatitude(lat1),
153
+ psi2 = _ell.IsometricLatitude(lat2),
154
+ psi12 = psi2 - psi1,
155
+ h = Math::hypot(lon12, psi12);
156
+ if (outmask & AZIMUTH)
157
+ azi12 = Math::atan2d(lon12, psi12);
158
+ if (outmask & DISTANCE) {
159
+ real dmudpsi = DIsometricToRectifying(psi2, psi1);
160
+ s12 = h * dmudpsi * _ell.QuarterMeridian() / 90;
161
+ }
162
+ if (outmask & AREA)
163
+ S12 = _c2 * lon12 *
164
+ MeanSinXi(psi2 * Math::degree(), psi1 * Math::degree());
165
+ }
166
+
167
+ RhumbLine Rhumb::Line(real lat1, real lon1, real azi12) const
168
+ { return RhumbLine(*this, lat1, lon1, azi12, _exact); }
169
+
170
+ void Rhumb::GenDirect(real lat1, real lon1, real azi12, real s12,
171
+ unsigned outmask,
172
+ real& lat2, real& lon2, real& S12) const
173
+ { Line(lat1, lon1, azi12).GenPosition(s12, outmask, lat2, lon2, S12); }
174
+
175
+ Math::real Rhumb::DE(real x, real y) const {
176
+ const EllipticFunction& ei = _ell._ell;
177
+ real d = x - y;
178
+ if (x * y <= 0)
179
+ return d ? (ei.E(x) - ei.E(y)) / d : 1;
180
+ // See DLMF: Eqs (19.11.2) and (19.11.4) letting
181
+ // theta -> x, phi -> -y, psi -> z
182
+ //
183
+ // (E(x) - E(y)) / d = E(z)/d - k2 * sin(x) * sin(y) * sin(z)/d
184
+ //
185
+ // tan(z/2) = (sin(x)*Delta(y) - sin(y)*Delta(x)) / (cos(x) + cos(y))
186
+ // = d * Dsin(x,y) * (sin(x) + sin(y))/(cos(x) + cos(y)) /
187
+ // (sin(x)*Delta(y) + sin(y)*Delta(x))
188
+ // = t = d * Dt
189
+ // sin(z) = 2*t/(1+t^2); cos(z) = (1-t^2)/(1+t^2)
190
+ // Alt (this only works for |z| <= pi/2 -- however, this conditions holds
191
+ // if x*y > 0):
192
+ // sin(z) = d * Dsin(x,y) * (sin(x) + sin(y))/
193
+ // (sin(x)*cos(y)*Delta(y) + sin(y)*cos(x)*Delta(x))
194
+ // cos(z) = sqrt((1-sin(z))*(1+sin(z)))
195
+ real sx = sin(x), sy = sin(y), cx = cos(x), cy = cos(y);
196
+ real Dt = Dsin(x, y) * (sx + sy) /
197
+ ((cx + cy) * (sx * ei.Delta(sy, cy) + sy * ei.Delta(sx, cx))),
198
+ t = d * Dt, Dsz = 2 * Dt / (1 + t*t),
199
+ sz = d * Dsz, cz = (1 - t) * (1 + t) / (1 + t*t);
200
+ return ((sz ? ei.E(sz, cz, ei.Delta(sz, cz)) / sz : 1)
201
+ - ei.k2() * sx * sy) * Dsz;
202
+ }
203
+
204
+ Math::real Rhumb::DRectifying(real latx, real laty) const {
205
+ real
206
+ tbetx = _ell._f1 * Math::tand(latx),
207
+ tbety = _ell._f1 * Math::tand(laty);
208
+ return (Math::pi()/2) * _ell._b * _ell._f1 * DE(atan(tbetx), atan(tbety))
209
+ * Dtan(latx, laty) * Datan(tbetx, tbety) / _ell.QuarterMeridian();
210
+ }
211
+
212
+ Math::real Rhumb::DIsometric(real latx, real laty) const {
213
+ real
214
+ phix = latx * Math::degree(), tx = Math::tand(latx),
215
+ phiy = laty * Math::degree(), ty = Math::tand(laty);
216
+ return Dasinh(tx, ty) * Dtan(latx, laty)
217
+ - Deatanhe(sin(phix), sin(phiy)) * Dsin(phix, phiy);
218
+ }
219
+
220
+ Math::real Rhumb::SinCosSeries(bool sinp,
221
+ real x, real y, const real c[], int n) {
222
+ // N.B. n >= 0 and c[] has n+1 elements 0..n, of which c[0] is ignored.
223
+ //
224
+ // Use Clenshaw summation to evaluate
225
+ // m = (g(x) + g(y)) / 2 -- mean value
226
+ // s = (g(x) - g(y)) / (x - y) -- average slope
227
+ // where
228
+ // g(x) = sum(c[j]*SC(2*j*x), j = 1..n)
229
+ // SC = sinp ? sin : cos
230
+ // CS = sinp ? cos : sin
231
+ //
232
+ // This function returns only s; m is discarded.
233
+ //
234
+ // Write
235
+ // t = [m; s]
236
+ // t = sum(c[j] * f[j](x,y), j = 1..n)
237
+ // where
238
+ // f[j](x,y) = [ (SC(2*j*x)+SC(2*j*y))/2 ]
239
+ // [ (SC(2*j*x)-SC(2*j*y))/d ]
240
+ //
241
+ // = [ cos(j*d)*SC(j*p) ]
242
+ // [ +/-(2/d)*sin(j*d)*CS(j*p) ]
243
+ // (+/- = sinp ? + : -) and
244
+ // p = x+y, d = x-y
245
+ //
246
+ // f[j+1](x,y) = A * f[j](x,y) - f[j-1](x,y)
247
+ //
248
+ // A = [ 2*cos(p)*cos(d) -sin(p)*sin(d)*d]
249
+ // [ -4*sin(p)*sin(d)/d 2*cos(p)*cos(d) ]
250
+ //
251
+ // Let b[n+1] = b[n+2] = [0 0; 0 0]
252
+ // b[j] = A * b[j+1] - b[j+2] + c[j] * I for j = n..1
253
+ // t = (c[0] * I - b[2]) * f[0](x,y) + b[1] * f[1](x,y)
254
+ // c[0] is not accessed for s = t[2]
255
+ real p = x + y, d = x - y,
256
+ cp = cos(p), cd = cos(d),
257
+ sp = sin(p), sd = d ? sin(d)/d : 1,
258
+ m = 2 * cp * cd, s = sp * sd;
259
+ // 2x2 matrices stored in row-major order
260
+ const real a[4] = {m, -s * d * d, -4 * s, m};
261
+ real ba[4] = {0, 0, 0, 0};
262
+ real bb[4] = {0, 0, 0, 0};
263
+ real* b1 = ba;
264
+ real* b2 = bb;
265
+ if (n > 0) b1[0] = b1[3] = c[n];
266
+ for (int j = n - 1; j > 0; --j) { // j = n-1 .. 1
267
+ std::swap(b1, b2);
268
+ // b1 = A * b2 - b1 + c[j] * I
269
+ b1[0] = a[0] * b2[0] + a[1] * b2[2] - b1[0] + c[j];
270
+ b1[1] = a[0] * b2[1] + a[1] * b2[3] - b1[1];
271
+ b1[2] = a[2] * b2[0] + a[3] * b2[2] - b1[2];
272
+ b1[3] = a[2] * b2[1] + a[3] * b2[3] - b1[3] + c[j];
273
+ }
274
+ // Here are the full expressions for m and s
275
+ // m = (c[0] - b2[0]) * f01 - b2[1] * f02 + b1[0] * f11 + b1[1] * f12;
276
+ // s = - b2[2] * f01 + (c[0] - b2[3]) * f02 + b1[2] * f11 + b1[3] * f12;
277
+ if (sinp) {
278
+ // real f01 = 0, f02 = 0;
279
+ real f11 = cd * sp, f12 = 2 * sd * cp;
280
+ // m = b1[0] * f11 + b1[1] * f12;
281
+ s = b1[2] * f11 + b1[3] * f12;
282
+ } else {
283
+ // real f01 = 1, f02 = 0;
284
+ real f11 = cd * cp, f12 = - 2 * sd * sp;
285
+ // m = c[0] - b2[0] + b1[0] * f11 + b1[1] * f12;
286
+ s = - b2[2] + b1[2] * f11 + b1[3] * f12;
287
+ }
288
+ return s;
289
+ }
290
+
291
+ Math::real Rhumb::DConformalToRectifying(real chix, real chiy) const {
292
+ return 1 + SinCosSeries(true, chix, chiy,
293
+ _ell.ConformalToRectifyingCoeffs(), tm_maxord);
294
+ }
295
+
296
+ Math::real Rhumb::DRectifyingToConformal(real mux, real muy) const {
297
+ return 1 - SinCosSeries(true, mux, muy,
298
+ _ell.RectifyingToConformalCoeffs(), tm_maxord);
299
+ }
300
+
301
+ Math::real Rhumb::DIsometricToRectifying(real psix, real psiy) const {
302
+ if (_exact) {
303
+ real
304
+ latx = _ell.InverseIsometricLatitude(psix),
305
+ laty = _ell.InverseIsometricLatitude(psiy);
306
+ return DRectifying(latx, laty) / DIsometric(latx, laty);
307
+ } else {
308
+ psix *= Math::degree();
309
+ psiy *= Math::degree();
310
+ return DConformalToRectifying(gd(psix), gd(psiy)) * Dgd(psix, psiy);
311
+ }
312
+ }
313
+
314
+ Math::real Rhumb::DRectifyingToIsometric(real mux, real muy) const {
315
+ real
316
+ latx = _ell.InverseRectifyingLatitude(mux/Math::degree()),
317
+ laty = _ell.InverseRectifyingLatitude(muy/Math::degree());
318
+ return _exact ?
319
+ DIsometric(latx, laty) / DRectifying(latx, laty) :
320
+ Dgdinv(Math::taupf(Math::tand(latx), _ell._es),
321
+ Math::taupf(Math::tand(laty), _ell._es)) *
322
+ DRectifyingToConformal(mux, muy);
323
+ }
324
+
325
+ Math::real Rhumb::MeanSinXi(real psix, real psiy) const {
326
+ return Dlog(cosh(psix), cosh(psiy)) * Dcosh(psix, psiy)
327
+ + SinCosSeries(false, gd(psix), gd(psiy), _R, maxpow_) * Dgd(psix, psiy);
328
+ }
329
+
330
+ RhumbLine::RhumbLine(const Rhumb& rh, real lat1, real lon1, real azi12,
331
+ bool exact)
332
+ : _rh(rh)
333
+ , _exact(exact)
334
+ , _lat1(Math::LatFix(lat1))
335
+ , _lon1(lon1)
336
+ , _azi12(Math::AngNormalize(azi12))
337
+ {
338
+ real alp12 = _azi12 * Math::degree();
339
+ _salp = _azi12 == -180 ? 0 : sin(alp12);
340
+ _calp = abs(_azi12) == 90 ? 0 : cos(alp12);
341
+ _mu1 = _rh._ell.RectifyingLatitude(lat1);
342
+ _psi1 = _rh._ell.IsometricLatitude(lat1);
343
+ _r1 = _rh._ell.CircleRadius(lat1);
344
+ }
345
+
346
+ void RhumbLine::GenPosition(real s12, unsigned outmask,
347
+ real& lat2, real& lon2, real& S12) const {
348
+ real
349
+ mu12 = s12 * _calp * 90 / _rh._ell.QuarterMeridian(),
350
+ mu2 = _mu1 + mu12;
351
+ real psi2, lat2x, lon2x;
352
+ if (abs(mu2) <= 90) {
353
+ if (_calp) {
354
+ lat2x = _rh._ell.InverseRectifyingLatitude(mu2);
355
+ real psi12 = _rh.DRectifyingToIsometric( mu2 * Math::degree(),
356
+ _mu1 * Math::degree()) * mu12;
357
+ lon2x = _salp * psi12 / _calp;
358
+ psi2 = _psi1 + psi12;
359
+ } else {
360
+ lat2x = _lat1;
361
+ lon2x = _salp * s12 / (_r1 * Math::degree());
362
+ psi2 = _psi1;
363
+ }
364
+ if (outmask & AREA)
365
+ S12 = _rh._c2 * lon2x *
366
+ _rh.MeanSinXi(_psi1 * Math::degree(), psi2 * Math::degree());
367
+ lon2x = outmask & LONG_UNROLL ? _lon1 + lon2x :
368
+ Math::AngNormalize(Math::AngNormalize(_lon1) + lon2x);
369
+ } else {
370
+ // Reduce to the interval [-180, 180)
371
+ mu2 = Math::AngNormalize(mu2);
372
+ // Deal with points on the anti-meridian
373
+ if (abs(mu2) > 90) mu2 = Math::AngNormalize(180 - mu2);
374
+ lat2x = _rh._ell.InverseRectifyingLatitude(mu2);
375
+ lon2x = Math::NaN();
376
+ if (outmask & AREA)
377
+ S12 = Math::NaN();
378
+ }
379
+ if (outmask & LATITUDE) lat2 = lat2x;
380
+ if (outmask & LONGITUDE) lon2 = lon2x;
381
+ }
382
+
383
+ } // namespace GeographicLib
@@ -0,0 +1,477 @@
1
+ /**
2
+ * \file SphericalEngine.cpp
3
+ * \brief Implementation for GeographicLib::SphericalEngine 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
+ * 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
+
133
+ #include <GeographicLib/SphericalEngine.hpp>
134
+ #include <GeographicLib/CircularEngine.hpp>
135
+ #include <GeographicLib/Utility.hpp>
136
+
137
+ #if defined(_MSC_VER)
138
+ // Squelch warnings about constant conditional expressions and potentially
139
+ // uninitialized local variables
140
+ # pragma warning (disable: 4127 4701)
141
+ #endif
142
+
143
+ namespace GeographicLib {
144
+
145
+ using namespace std;
146
+
147
+ const vector<Math::real> SphericalEngine::Z_(0);
148
+ vector<Math::real> SphericalEngine::root_(0);
149
+
150
+ template<bool gradp, SphericalEngine::normalization norm, int L>
151
+ Math::real SphericalEngine::Value(const coeff c[], const real f[],
152
+ real x, real y, real z, real a,
153
+ real& gradx, real& grady, real& gradz)
154
+ {
155
+ GEOGRAPHICLIB_STATIC_ASSERT(L > 0, "L must be positive");
156
+ GEOGRAPHICLIB_STATIC_ASSERT(norm == FULL || norm == SCHMIDT,
157
+ "Unknown normalization");
158
+ int N = c[0].nmx(), M = c[0].mmx();
159
+
160
+ real
161
+ p = Math::hypot(x, y),
162
+ cl = p ? x / p : 1, // cos(lambda); at pole, pick lambda = 0
163
+ sl = p ? y / p : 0, // sin(lambda)
164
+ r = Math::hypot(z, p),
165
+ t = r ? z / r : 0, // cos(theta); at origin, pick theta = pi/2
166
+ u = r ? max(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
+ for (int m = M; m >= 0; --m) { // m = M .. 0
182
+ // Initialize inner sum
183
+ real wc = 0, wc2 = 0, ws = 0, ws2 = 0; // w [N - m + 1], w [N - m + 2]
184
+ real wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0; // wr[N - m + 1], wr[N - m + 2]
185
+ real wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; // wt[N - m + 1], wt[N - m + 2]
186
+ for (int l = 0; l < L; ++l)
187
+ k[l] = c[l].index(N, m) + 1;
188
+ for (int n = N; n >= m; --n) { // n = N .. m; l = N - m .. 0
189
+ real w, A, Ax, B, R; // alpha[l], beta[l + 1]
190
+ switch (norm) {
191
+ case FULL:
192
+ w = root_[2 * n + 1] / (root_[n - m + 1] * root_[n + m + 1]);
193
+ Ax = q * w * root_[2 * n + 3];
194
+ A = t * Ax;
195
+ B = - q2 * root_[2 * n + 5] /
196
+ (w * root_[n - m + 2] * root_[n + m + 2]);
197
+ break;
198
+ case SCHMIDT:
199
+ w = root_[n - m + 1] * root_[n + m + 1];
200
+ Ax = q * (2 * n + 1) / w;
201
+ A = t * Ax;
202
+ B = - q2 * w / (root_[n - m + 2] * root_[n + m + 2]);
203
+ break;
204
+ default: break; // To suppress warning message from Visual Studio
205
+ }
206
+ R = c[0].Cv(--k[0]);
207
+ for (int l = 1; l < L; ++l)
208
+ R += c[l].Cv(--k[l], n, m, f[l]);
209
+ R *= scale();
210
+ w = A * wc + B * wc2 + R; wc2 = wc; wc = w;
211
+ if (gradp) {
212
+ w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w;
213
+ w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w;
214
+ }
215
+ if (m) {
216
+ R = c[0].Sv(k[0]);
217
+ for (int l = 1; l < L; ++l)
218
+ R += c[l].Sv(k[l], n, m, f[l]);
219
+ R *= scale();
220
+ w = A * ws + B * ws2 + R; ws2 = ws; ws = w;
221
+ if (gradp) {
222
+ w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w;
223
+ w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w;
224
+ }
225
+ }
226
+ }
227
+ // Now Sc[m] = wc, Ss[m] = ws
228
+ // Sc'[m] = wtc, Ss'[m] = wtc
229
+ if (m) {
230
+ real v, A, B; // alpha[m], beta[m + 1]
231
+ switch (norm) {
232
+ case FULL:
233
+ v = root_[2] * root_[2 * m + 3] / root_[m + 1];
234
+ A = cl * v * uq;
235
+ B = - v * root_[2 * m + 5] / (root_[8] * root_[m + 2]) * uq2;
236
+ break;
237
+ case SCHMIDT:
238
+ v = root_[2] * root_[2 * m + 1] / root_[m + 1];
239
+ A = cl * v * uq;
240
+ B = - v * root_[2 * m + 3] / (root_[8] * root_[m + 2]) * uq2;
241
+ break;
242
+ default: break; // To suppress warning message from Visual Studio
243
+ }
244
+ v = A * vc + B * vc2 + wc ; vc2 = vc ; vc = v;
245
+ v = A * vs + B * vs2 + ws ; vs2 = vs ; vs = v;
246
+ if (gradp) {
247
+ // Include the terms Sc[m] * P'[m,m](t) and Ss[m] * P'[m,m](t)
248
+ wtc += m * tu * wc; wts += m * tu * ws;
249
+ v = A * vrc + B * vrc2 + wrc; vrc2 = vrc; vrc = v;
250
+ v = A * vrs + B * vrs2 + wrs; vrs2 = vrs; vrs = v;
251
+ v = A * vtc + B * vtc2 + wtc; vtc2 = vtc; vtc = v;
252
+ v = A * vts + B * vts2 + wts; vts2 = vts; vts = v;
253
+ v = A * vlc + B * vlc2 + m*ws; vlc2 = vlc; vlc = v;
254
+ v = A * vls + B * vls2 - m*wc; vls2 = vls; vls = v;
255
+ }
256
+ } else {
257
+ real A, B, qs;
258
+ switch (norm) {
259
+ case FULL:
260
+ A = root_[3] * uq; // F[1]/(q*cl) or F[1]/(q*sl)
261
+ B = - root_[15]/2 * uq2; // beta[1]/q
262
+ break;
263
+ case SCHMIDT:
264
+ A = uq;
265
+ B = - root_[3]/2 * uq2;
266
+ break;
267
+ default: break; // To suppress warning message from Visual Studio
268
+ }
269
+ qs = q / scale();
270
+ vc = qs * (wc + A * (cl * vc + sl * vs ) + B * vc2);
271
+ if (gradp) {
272
+ qs /= r;
273
+ // The components of the gradient in spherical coordinates are
274
+ // r: dV/dr
275
+ // theta: 1/r * dV/dtheta
276
+ // lambda: 1/(r*u) * dV/dlambda
277
+ vrc = - qs * (wrc + A * (cl * vrc + sl * vrs) + B * vrc2);
278
+ vtc = qs * (wtc + A * (cl * vtc + sl * vts) + B * vtc2);
279
+ vlc = qs / u * ( A * (cl * vlc + sl * vls) + B * vlc2);
280
+ }
281
+ }
282
+ }
283
+
284
+ if (gradp) {
285
+ // Rotate into cartesian (geocentric) coordinates
286
+ gradx = cl * (u * vrc + t * vtc) - sl * vlc;
287
+ grady = sl * (u * vrc + t * vtc) + cl * vlc;
288
+ gradz = t * vrc - u * vtc ;
289
+ }
290
+ return vc;
291
+ }
292
+
293
+ template<bool gradp, SphericalEngine::normalization norm, int L>
294
+ CircularEngine SphericalEngine::Circle(const coeff c[], const real f[],
295
+ real p, real z, real a) {
296
+
297
+ GEOGRAPHICLIB_STATIC_ASSERT(L > 0, "L must be positive");
298
+ GEOGRAPHICLIB_STATIC_ASSERT(norm == FULL || norm == SCHMIDT,
299
+ "Unknown normalization");
300
+ int N = c[0].nmx(), M = c[0].mmx();
301
+
302
+ real
303
+ r = Math::hypot(z, p),
304
+ t = r ? z / r : 0, // cos(theta); at origin, pick theta = pi/2
305
+ u = r ? max(p / r, eps()) : 1, // sin(theta); but avoid the pole
306
+ q = a / r;
307
+ real
308
+ q2 = Math::sq(q),
309
+ tu = t / u;
310
+ CircularEngine circ(M, gradp, norm, a, r, u, t);
311
+ int k[L];
312
+ for (int m = M; m >= 0; --m) { // m = M .. 0
313
+ // Initialize inner sum
314
+ real wc = 0, wc2 = 0, ws = 0, ws2 = 0; // w [N - m + 1], w [N - m + 2]
315
+ real wrc = 0, wrc2 = 0, wrs = 0, wrs2 = 0; // wr[N - m + 1], wr[N - m + 2]
316
+ real wtc = 0, wtc2 = 0, wts = 0, wts2 = 0; // wt[N - m + 1], wt[N - m + 2]
317
+ for (int l = 0; l < L; ++l)
318
+ k[l] = c[l].index(N, m) + 1;
319
+ for (int n = N; n >= m; --n) { // n = N .. m; l = N - m .. 0
320
+ real w, A, Ax, B, R; // alpha[l], beta[l + 1]
321
+ switch (norm) {
322
+ case FULL:
323
+ w = root_[2 * n + 1] / (root_[n - m + 1] * root_[n + m + 1]);
324
+ Ax = q * w * root_[2 * n + 3];
325
+ A = t * Ax;
326
+ B = - q2 * root_[2 * n + 5] /
327
+ (w * root_[n - m + 2] * root_[n + m + 2]);
328
+ break;
329
+ case SCHMIDT:
330
+ w = root_[n - m + 1] * root_[n + m + 1];
331
+ Ax = q * (2 * n + 1) / w;
332
+ A = t * Ax;
333
+ B = - q2 * w / (root_[n - m + 2] * root_[n + m + 2]);
334
+ break;
335
+ default: break; // To suppress warning message from Visual Studio
336
+ }
337
+ R = c[0].Cv(--k[0]);
338
+ for (int l = 1; l < L; ++l)
339
+ R += c[l].Cv(--k[l], n, m, f[l]);
340
+ R *= scale();
341
+ w = A * wc + B * wc2 + R; wc2 = wc; wc = w;
342
+ if (gradp) {
343
+ w = A * wrc + B * wrc2 + (n + 1) * R; wrc2 = wrc; wrc = w;
344
+ w = A * wtc + B * wtc2 - u*Ax * wc2; wtc2 = wtc; wtc = w;
345
+ }
346
+ if (m) {
347
+ R = c[0].Sv(k[0]);
348
+ for (int l = 1; l < L; ++l)
349
+ R += c[l].Sv(k[l], n, m, f[l]);
350
+ R *= scale();
351
+ w = A * ws + B * ws2 + R; ws2 = ws; ws = w;
352
+ if (gradp) {
353
+ w = A * wrs + B * wrs2 + (n + 1) * R; wrs2 = wrs; wrs = w;
354
+ w = A * wts + B * wts2 - u*Ax * ws2; wts2 = wts; wts = w;
355
+ }
356
+ }
357
+ }
358
+ if (!gradp)
359
+ circ.SetCoeff(m, wc, ws);
360
+ else {
361
+ // Include the terms Sc[m] * P'[m,m](t) and Ss[m] * P'[m,m](t)
362
+ wtc += m * tu * wc; wts += m * tu * ws;
363
+ circ.SetCoeff(m, wc, ws, wrc, wrs, wtc, wts);
364
+ }
365
+ }
366
+
367
+ return circ;
368
+ }
369
+
370
+ void SphericalEngine::RootTable(int N) {
371
+ // Need square roots up to max(2 * N + 5, 15).
372
+ int L = max(2 * N + 5, 15) + 1, oldL = int(root_.size());
373
+ if (oldL >= L)
374
+ return;
375
+ root_.resize(L);
376
+ for (int l = oldL; l < L; ++l)
377
+ root_[l] = sqrt(real(l));
378
+ }
379
+
380
+ void SphericalEngine::coeff::readcoeffs(std::istream& stream, int& N, int& M,
381
+ std::vector<real>& C,
382
+ std::vector<real>& S) {
383
+ int nm[2];
384
+ Utility::readarray<int, int, false>(stream, nm, 2);
385
+ N = nm[0]; M = nm[1];
386
+ if (!(N >= M && M >= -1 && N * M >= 0))
387
+ // The last condition is that M = -1 implies N = -1 and vice versa.
388
+ throw GeographicErr("Bad degree and order " +
389
+ Utility::str(N) + " " + Utility::str(M));
390
+ C.resize(SphericalEngine::coeff::Csize(N, M));
391
+ Utility::readarray<double, real, false>(stream, C);
392
+ S.resize(SphericalEngine::coeff::Ssize(N, M));
393
+ Utility::readarray<double, real, false>(stream, S);
394
+ return;
395
+ }
396
+
397
+ /// \cond SKIP
398
+ template Math::real GEOGRAPHICLIB_EXPORT
399
+ SphericalEngine::Value<true, SphericalEngine::FULL, 1>
400
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
401
+ template Math::real GEOGRAPHICLIB_EXPORT
402
+ SphericalEngine::Value<false, SphericalEngine::FULL, 1>
403
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
404
+ template Math::real GEOGRAPHICLIB_EXPORT
405
+ SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 1>
406
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
407
+ template Math::real GEOGRAPHICLIB_EXPORT
408
+ SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 1>
409
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
410
+
411
+ template Math::real GEOGRAPHICLIB_EXPORT
412
+ SphericalEngine::Value<true, SphericalEngine::FULL, 2>
413
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
414
+ template Math::real GEOGRAPHICLIB_EXPORT
415
+ SphericalEngine::Value<false, SphericalEngine::FULL, 2>
416
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
417
+ template Math::real GEOGRAPHICLIB_EXPORT
418
+ SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 2>
419
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
420
+ template Math::real GEOGRAPHICLIB_EXPORT
421
+ SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 2>
422
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
423
+
424
+ template Math::real GEOGRAPHICLIB_EXPORT
425
+ SphericalEngine::Value<true, SphericalEngine::FULL, 3>
426
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
427
+ template Math::real GEOGRAPHICLIB_EXPORT
428
+ SphericalEngine::Value<false, SphericalEngine::FULL, 3>
429
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
430
+ template Math::real GEOGRAPHICLIB_EXPORT
431
+ SphericalEngine::Value<true, SphericalEngine::SCHMIDT, 3>
432
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
433
+ template Math::real GEOGRAPHICLIB_EXPORT
434
+ SphericalEngine::Value<false, SphericalEngine::SCHMIDT, 3>
435
+ (const coeff[], const real[], real, real, real, real, real&, real&, real&);
436
+
437
+ template CircularEngine GEOGRAPHICLIB_EXPORT
438
+ SphericalEngine::Circle<true, SphericalEngine::FULL, 1>
439
+ (const coeff[], const real[], real, real, real);
440
+ template CircularEngine GEOGRAPHICLIB_EXPORT
441
+ SphericalEngine::Circle<false, SphericalEngine::FULL, 1>
442
+ (const coeff[], const real[], real, real, real);
443
+ template CircularEngine GEOGRAPHICLIB_EXPORT
444
+ SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 1>
445
+ (const coeff[], const real[], real, real, real);
446
+ template CircularEngine GEOGRAPHICLIB_EXPORT
447
+ SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 1>
448
+ (const coeff[], const real[], real, real, real);
449
+
450
+ template CircularEngine GEOGRAPHICLIB_EXPORT
451
+ SphericalEngine::Circle<true, SphericalEngine::FULL, 2>
452
+ (const coeff[], const real[], real, real, real);
453
+ template CircularEngine GEOGRAPHICLIB_EXPORT
454
+ SphericalEngine::Circle<false, SphericalEngine::FULL, 2>
455
+ (const coeff[], const real[], real, real, real);
456
+ template CircularEngine GEOGRAPHICLIB_EXPORT
457
+ SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 2>
458
+ (const coeff[], const real[], real, real, real);
459
+ template CircularEngine GEOGRAPHICLIB_EXPORT
460
+ SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 2>
461
+ (const coeff[], const real[], real, real, real);
462
+
463
+ template CircularEngine GEOGRAPHICLIB_EXPORT
464
+ SphericalEngine::Circle<true, SphericalEngine::FULL, 3>
465
+ (const coeff[], const real[], real, real, real);
466
+ template CircularEngine GEOGRAPHICLIB_EXPORT
467
+ SphericalEngine::Circle<false, SphericalEngine::FULL, 3>
468
+ (const coeff[], const real[], real, real, real);
469
+ template CircularEngine GEOGRAPHICLIB_EXPORT
470
+ SphericalEngine::Circle<true, SphericalEngine::SCHMIDT, 3>
471
+ (const coeff[], const real[], real, real, real);
472
+ template CircularEngine GEOGRAPHICLIB_EXPORT
473
+ SphericalEngine::Circle<false, SphericalEngine::SCHMIDT, 3>
474
+ (const coeff[], const real[], real, real, real);
475
+ /// \endcond
476
+
477
+ } // namespace GeographicLib