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.
- 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
|