geographiclib 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/AUTHORS +12 -0
- data/LICENSE +24 -0
- data/ext/geographiclib/Accumulator.cpp +23 -0
- data/ext/geographiclib/AlbersEqualArea.cpp +445 -0
- data/ext/geographiclib/AzimuthalEquidistant.cpp +41 -0
- data/ext/geographiclib/CassiniSoldner.cpp +89 -0
- data/ext/geographiclib/CircularEngine.cpp +96 -0
- data/ext/geographiclib/DMS.cpp +381 -0
- data/ext/geographiclib/Ellipsoid.cpp +125 -0
- data/ext/geographiclib/EllipticFunction.cpp +512 -0
- data/ext/geographiclib/GARS.cpp +122 -0
- data/ext/geographiclib/GeoCoords.cpp +175 -0
- data/ext/geographiclib/Geocentric.cpp +172 -0
- data/ext/geographiclib/Geodesic.cpp +1908 -0
- data/ext/geographiclib/GeodesicExact.cpp +927 -0
- data/ext/geographiclib/GeodesicExactC4.cpp +7879 -0
- data/ext/geographiclib/GeodesicLine.cpp +321 -0
- data/ext/geographiclib/GeodesicLineExact.cpp +289 -0
- data/ext/geographiclib/GeographicLib/Accumulator.hpp +184 -0
- data/ext/geographiclib/GeographicLib/AlbersEqualArea.hpp +312 -0
- data/ext/geographiclib/GeographicLib/AzimuthalEquidistant.hpp +139 -0
- data/ext/geographiclib/GeographicLib/CassiniSoldner.hpp +204 -0
- data/ext/geographiclib/GeographicLib/CircularEngine.hpp +195 -0
- data/ext/geographiclib/GeographicLib/Config.h +12 -0
- data/ext/geographiclib/GeographicLib/Constants.hpp +387 -0
- data/ext/geographiclib/GeographicLib/DMS.hpp +370 -0
- data/ext/geographiclib/GeographicLib/Ellipsoid.hpp +534 -0
- data/ext/geographiclib/GeographicLib/EllipticFunction.hpp +692 -0
- data/ext/geographiclib/GeographicLib/GARS.hpp +143 -0
- data/ext/geographiclib/GeographicLib/GeoCoords.hpp +544 -0
- data/ext/geographiclib/GeographicLib/Geocentric.hpp +267 -0
- data/ext/geographiclib/GeographicLib/Geodesic.hpp +970 -0
- data/ext/geographiclib/GeographicLib/GeodesicExact.hpp +862 -0
- data/ext/geographiclib/GeographicLib/GeodesicLine.hpp +701 -0
- data/ext/geographiclib/GeographicLib/GeodesicLineExact.hpp +667 -0
- data/ext/geographiclib/GeographicLib/Geohash.hpp +180 -0
- data/ext/geographiclib/GeographicLib/Geoid.hpp +472 -0
- data/ext/geographiclib/GeographicLib/Georef.hpp +160 -0
- data/ext/geographiclib/GeographicLib/Gnomonic.hpp +206 -0
- data/ext/geographiclib/GeographicLib/GravityCircle.hpp +301 -0
- data/ext/geographiclib/GeographicLib/GravityModel.hpp +520 -0
- data/ext/geographiclib/GeographicLib/LambertConformalConic.hpp +313 -0
- data/ext/geographiclib/GeographicLib/LocalCartesian.hpp +236 -0
- data/ext/geographiclib/GeographicLib/MGRS.hpp +355 -0
- data/ext/geographiclib/GeographicLib/MagneticCircle.hpp +178 -0
- data/ext/geographiclib/GeographicLib/MagneticModel.hpp +347 -0
- data/ext/geographiclib/GeographicLib/Math.hpp +920 -0
- data/ext/geographiclib/GeographicLib/NormalGravity.hpp +350 -0
- data/ext/geographiclib/GeographicLib/OSGB.hpp +249 -0
- data/ext/geographiclib/GeographicLib/PolarStereographic.hpp +150 -0
- data/ext/geographiclib/GeographicLib/PolygonArea.hpp +288 -0
- data/ext/geographiclib/GeographicLib/Rhumb.hpp +589 -0
- data/ext/geographiclib/GeographicLib/SphericalEngine.hpp +376 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic.hpp +354 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic1.hpp +281 -0
- data/ext/geographiclib/GeographicLib/SphericalHarmonic2.hpp +315 -0
- data/ext/geographiclib/GeographicLib/TransverseMercator.hpp +196 -0
- data/ext/geographiclib/GeographicLib/TransverseMercatorExact.hpp +254 -0
- data/ext/geographiclib/GeographicLib/UTMUPS.hpp +421 -0
- data/ext/geographiclib/GeographicLib/Utility.hpp +612 -0
- data/ext/geographiclib/Geohash.cpp +102 -0
- data/ext/geographiclib/Geoid.cpp +509 -0
- data/ext/geographiclib/Georef.cpp +135 -0
- data/ext/geographiclib/Gnomonic.cpp +85 -0
- data/ext/geographiclib/GravityCircle.cpp +129 -0
- data/ext/geographiclib/GravityModel.cpp +360 -0
- data/ext/geographiclib/LambertConformalConic.cpp +456 -0
- data/ext/geographiclib/LocalCartesian.cpp +62 -0
- data/ext/geographiclib/MGRS.cpp +461 -0
- data/ext/geographiclib/MagneticCircle.cpp +52 -0
- data/ext/geographiclib/MagneticModel.cpp +269 -0
- data/ext/geographiclib/Math.cpp +63 -0
- data/ext/geographiclib/NormalGravity.cpp +262 -0
- data/ext/geographiclib/OSGB.cpp +167 -0
- data/ext/geographiclib/PolarStereographic.cpp +108 -0
- data/ext/geographiclib/PolygonArea.cpp +204 -0
- data/ext/geographiclib/Rhumb.cpp +383 -0
- data/ext/geographiclib/SphericalEngine.cpp +477 -0
- data/ext/geographiclib/TransverseMercator.cpp +603 -0
- data/ext/geographiclib/TransverseMercatorExact.cpp +464 -0
- data/ext/geographiclib/UTMUPS.cpp +296 -0
- data/ext/geographiclib/Utility.cpp +61 -0
- data/ext/geographiclib/extconf.rb +3 -0
- data/ext/geographiclib/geographiclib.cpp +62 -0
- data/lib/geographiclib.rb +20 -0
- 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
|