geodesic_wgs84 1.32.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +34 -0
- data/Makefile +13 -0
- data/README.md +58 -0
- data/Rakefile +17 -0
- data/ext/geodesic_wgs84/extconf.rb +13 -0
- data/ext/geodesic_wgs84/geodesic.c +1770 -0
- data/ext/geodesic_wgs84/geodesic.h +745 -0
- data/ext/geodesic_wgs84/geodesic_wgs84.c +192 -0
- data/geodesic_wgs84.gemspec +26 -0
- data/lib/geodesic_wgs84/version.rb +3 -0
- data/lib/geodesic_wgs84.rb +9 -0
- data/spec/geodesic_wgs84_spec.rb +25 -0
- metadata +88 -0
@@ -0,0 +1,1770 @@
|
|
1
|
+
/**
|
2
|
+
* \file geodesic.c
|
3
|
+
* \brief Implementation of the geodesic routines in C
|
4
|
+
*
|
5
|
+
* For the full documentation see geodesic.h.
|
6
|
+
**********************************************************************/
|
7
|
+
|
8
|
+
/** @cond SKIP */
|
9
|
+
|
10
|
+
/*
|
11
|
+
* This is a C implementation of the geodesic algorithms described in
|
12
|
+
*
|
13
|
+
* C. F. F. Karney,
|
14
|
+
* Algorithms for geodesics,
|
15
|
+
* J. Geodesy <b>87</b>, 43--55 (2013);
|
16
|
+
* http://dx.doi.org/10.1007/s00190-012-0578-z
|
17
|
+
* Addenda: http://geographiclib.sf.net/geod-addenda.html
|
18
|
+
*
|
19
|
+
* See the comments in geodesic.h for documentation.
|
20
|
+
*
|
21
|
+
* Copyright (c) Charles Karney (2012-2013) <charles@karney.com> and licensed
|
22
|
+
* under the MIT/X11 License. For more information, see
|
23
|
+
* http://geographiclib.sourceforge.net/
|
24
|
+
*/
|
25
|
+
|
26
|
+
#include "geodesic.h"
|
27
|
+
#include <math.h>
|
28
|
+
|
29
|
+
#define GEOGRAPHICLIB_GEODESIC_ORDER 6
|
30
|
+
#define nC1 GEOGRAPHICLIB_GEODESIC_ORDER
|
31
|
+
#define nC1p GEOGRAPHICLIB_GEODESIC_ORDER
|
32
|
+
#define nC2 GEOGRAPHICLIB_GEODESIC_ORDER
|
33
|
+
#define nA3 GEOGRAPHICLIB_GEODESIC_ORDER
|
34
|
+
#define nA3x nA3
|
35
|
+
#define nC3 GEOGRAPHICLIB_GEODESIC_ORDER
|
36
|
+
#define nC3x ((nC3 * (nC3 - 1)) / 2)
|
37
|
+
#define nC4 GEOGRAPHICLIB_GEODESIC_ORDER
|
38
|
+
#define nC4x ((nC4 * (nC4 + 1)) / 2)
|
39
|
+
|
40
|
+
typedef double real;
|
41
|
+
typedef int boolx;
|
42
|
+
|
43
|
+
static unsigned init = 0;
|
44
|
+
static const int FALSE = 0;
|
45
|
+
static const int TRUE = 1;
|
46
|
+
static unsigned digits, maxit1, maxit2;
|
47
|
+
static real epsilon, realmin, pi, degree, NaN,
|
48
|
+
tiny, tol0, tol1, tol2, tolb, xthresh;
|
49
|
+
|
50
|
+
static void Init() {
|
51
|
+
if (!init) {
|
52
|
+
#if defined(__DBL_MANT_DIG__)
|
53
|
+
digits = __DBL_MANT_DIG__;
|
54
|
+
#else
|
55
|
+
digits = 53;
|
56
|
+
#endif
|
57
|
+
#if defined(__DBL_EPSILON__)
|
58
|
+
epsilon = __DBL_EPSILON__;
|
59
|
+
#else
|
60
|
+
epsilon = pow(0.5, digits - 1);
|
61
|
+
#endif
|
62
|
+
#if defined(__DBL_MIN__)
|
63
|
+
realmin = __DBL_MIN__;
|
64
|
+
#else
|
65
|
+
realmin = pow(0.5, 1022);
|
66
|
+
#endif
|
67
|
+
#if defined(M_PI)
|
68
|
+
pi = M_PI;
|
69
|
+
#else
|
70
|
+
pi = atan2(0.0, -1.0);
|
71
|
+
#endif
|
72
|
+
maxit1 = 20;
|
73
|
+
maxit2 = maxit1 + digits + 10;
|
74
|
+
tiny = sqrt(realmin);
|
75
|
+
tol0 = epsilon;
|
76
|
+
/* Increase multiplier in defn of tol1 from 100 to 200 to fix inverse case
|
77
|
+
* 52.784459512564 0 -52.784459512563990912 179.634407464943777557
|
78
|
+
* which otherwise failed for Visual Studio 10 (Release and Debug) */
|
79
|
+
tol1 = 200 * tol0;
|
80
|
+
tol2 = sqrt(tol0);
|
81
|
+
/* Check on bisection interval */
|
82
|
+
tolb = tol0 * tol2;
|
83
|
+
xthresh = 1000 * tol2;
|
84
|
+
degree = pi/180;
|
85
|
+
NaN = sqrt(-1.0);
|
86
|
+
init = 1;
|
87
|
+
}
|
88
|
+
}
|
89
|
+
|
90
|
+
enum captype {
|
91
|
+
CAP_NONE = 0U,
|
92
|
+
CAP_C1 = 1U<<0,
|
93
|
+
CAP_C1p = 1U<<1,
|
94
|
+
CAP_C2 = 1U<<2,
|
95
|
+
CAP_C3 = 1U<<3,
|
96
|
+
CAP_C4 = 1U<<4,
|
97
|
+
CAP_ALL = 0x1FU,
|
98
|
+
OUT_ALL = 0x7F80U
|
99
|
+
};
|
100
|
+
|
101
|
+
static real sq(real x) { return x * x; }
|
102
|
+
static real log1px(real x) {
|
103
|
+
volatile real
|
104
|
+
y = 1 + x,
|
105
|
+
z = y - 1;
|
106
|
+
/* Here's the explanation for this magic: y = 1 + z, exactly, and z
|
107
|
+
* approx x, thus log(y)/z (which is nearly constant near z = 0) returns
|
108
|
+
* a good approximation to the true log(1 + x)/x. The multiplication x *
|
109
|
+
* (log(y)/z) introduces little additional error. */
|
110
|
+
return z == 0 ? x : x * log(y) / z;
|
111
|
+
}
|
112
|
+
|
113
|
+
static real atanhx(real x) {
|
114
|
+
real y = fabs(x); /* Enforce odd parity */
|
115
|
+
y = log1px(2 * y/(1 - y))/2;
|
116
|
+
return x < 0 ? -y : y;
|
117
|
+
}
|
118
|
+
|
119
|
+
static real hypotx(real x, real y)
|
120
|
+
{ return sqrt(x * x + y * y); }
|
121
|
+
|
122
|
+
static real cbrtx(real x) {
|
123
|
+
real y = pow(fabs(x), 1/(real)(3)); /* Return the real cube root */
|
124
|
+
return x < 0 ? -y : y;
|
125
|
+
}
|
126
|
+
|
127
|
+
static real sumx(real u, real v, real* t) {
|
128
|
+
volatile real s = u + v;
|
129
|
+
volatile real up = s - v;
|
130
|
+
volatile real vpp = s - up;
|
131
|
+
up -= u;
|
132
|
+
vpp -= v;
|
133
|
+
*t = -(up + vpp);
|
134
|
+
/* error-free sum:
|
135
|
+
* u + v = s + t
|
136
|
+
* = round(u + v) + t */
|
137
|
+
return s;
|
138
|
+
}
|
139
|
+
|
140
|
+
static real minx(real x, real y)
|
141
|
+
{ return x < y ? x : y; }
|
142
|
+
|
143
|
+
static real maxx(real x, real y)
|
144
|
+
{ return x > y ? x : y; }
|
145
|
+
|
146
|
+
static void swapx(real* x, real* y)
|
147
|
+
{ real t = *x; *x = *y; *y = t; }
|
148
|
+
|
149
|
+
static void SinCosNorm(real* sinx, real* cosx) {
|
150
|
+
real r = hypotx(*sinx, *cosx);
|
151
|
+
*sinx /= r;
|
152
|
+
*cosx /= r;
|
153
|
+
}
|
154
|
+
|
155
|
+
static real AngNormalize(real x)
|
156
|
+
{ return x >= 180 ? x - 360 : (x < -180 ? x + 360 : x); }
|
157
|
+
static real AngNormalize2(real x)
|
158
|
+
{ return AngNormalize(fmod(x, (real)(360))); }
|
159
|
+
|
160
|
+
static real AngDiff(real x, real y) {
|
161
|
+
real t, d = sumx(-x, y, &t);
|
162
|
+
if ((d - (real)(180)) + t > (real)(0)) /* y - x > 180 */
|
163
|
+
d -= (real)(360); /* exact */
|
164
|
+
else if ((d + (real)(180)) + t <= (real)(0)) /* y - x <= -180 */
|
165
|
+
d += (real)(360); /* exact */
|
166
|
+
return d + t;
|
167
|
+
}
|
168
|
+
|
169
|
+
static real AngRound(real x) {
|
170
|
+
const real z = 1/(real)(16);
|
171
|
+
volatile real y = fabs(x);
|
172
|
+
/* The compiler mustn't "simplify" z - (z - y) to y */
|
173
|
+
y = y < z ? z - (z - y) : y;
|
174
|
+
return x < 0 ? -y : y;
|
175
|
+
}
|
176
|
+
|
177
|
+
static void A3coeff(struct geod_geodesic* g);
|
178
|
+
static void C3coeff(struct geod_geodesic* g);
|
179
|
+
static void C4coeff(struct geod_geodesic* g);
|
180
|
+
static real SinCosSeries(boolx sinp,
|
181
|
+
real sinx, real cosx,
|
182
|
+
const real c[], int n);
|
183
|
+
static void Lengths(const struct geod_geodesic* g,
|
184
|
+
real eps, real sig12,
|
185
|
+
real ssig1, real csig1, real dn1,
|
186
|
+
real ssig2, real csig2, real dn2,
|
187
|
+
real cbet1, real cbet2,
|
188
|
+
real* ps12b, real* pm12b, real* pm0,
|
189
|
+
boolx scalep, real* pM12, real* pM21,
|
190
|
+
/* Scratch areas of the right size */
|
191
|
+
real C1a[], real C2a[]);
|
192
|
+
static real Astroid(real x, real y);
|
193
|
+
static real InverseStart(const struct geod_geodesic* g,
|
194
|
+
real sbet1, real cbet1, real dn1,
|
195
|
+
real sbet2, real cbet2, real dn2,
|
196
|
+
real lam12,
|
197
|
+
real* psalp1, real* pcalp1,
|
198
|
+
/* Only updated if return val >= 0 */
|
199
|
+
real* psalp2, real* pcalp2,
|
200
|
+
/* Only updated for short lines */
|
201
|
+
real* pdnm,
|
202
|
+
/* Scratch areas of the right size */
|
203
|
+
real C1a[], real C2a[]);
|
204
|
+
static real Lambda12(const struct geod_geodesic* g,
|
205
|
+
real sbet1, real cbet1, real dn1,
|
206
|
+
real sbet2, real cbet2, real dn2,
|
207
|
+
real salp1, real calp1,
|
208
|
+
real* psalp2, real* pcalp2,
|
209
|
+
real* psig12,
|
210
|
+
real* pssig1, real* pcsig1,
|
211
|
+
real* pssig2, real* pcsig2,
|
212
|
+
real* peps, real* pdomg12,
|
213
|
+
boolx diffp, real* pdlam12,
|
214
|
+
/* Scratch areas of the right size */
|
215
|
+
real C1a[], real C2a[], real C3a[]);
|
216
|
+
static real A3f(const struct geod_geodesic* g, real eps);
|
217
|
+
static void C3f(const struct geod_geodesic* g, real eps, real c[]);
|
218
|
+
static void C4f(const struct geod_geodesic* g, real eps, real c[]);
|
219
|
+
static real A1m1f(real eps);
|
220
|
+
static void C1f(real eps, real c[]);
|
221
|
+
static void C1pf(real eps, real c[]);
|
222
|
+
static real A2m1f(real eps);
|
223
|
+
static void C2f(real eps, real c[]);
|
224
|
+
static int transit(real lon1, real lon2);
|
225
|
+
static void accini(real s[]);
|
226
|
+
static void acccopy(const real s[], real t[]);
|
227
|
+
static void accadd(real s[], real y);
|
228
|
+
static real accsum(const real s[], real y);
|
229
|
+
static void accneg(real s[]);
|
230
|
+
|
231
|
+
void geod_init(struct geod_geodesic* g, real a, real f) {
|
232
|
+
if (!init) Init();
|
233
|
+
g->a = a;
|
234
|
+
g->f = f <= 1 ? f : 1/f;
|
235
|
+
g->f1 = 1 - g->f;
|
236
|
+
g->e2 = g->f * (2 - g->f);
|
237
|
+
g->ep2 = g->e2 / sq(g->f1); /* e2 / (1 - e2) */
|
238
|
+
g->n = g->f / ( 2 - g->f);
|
239
|
+
g->b = g->a * g->f1;
|
240
|
+
g->c2 = (sq(g->a) + sq(g->b) *
|
241
|
+
(g->e2 == 0 ? 1 :
|
242
|
+
(g->e2 > 0 ? atanhx(sqrt(g->e2)) : atan(sqrt(-g->e2))) /
|
243
|
+
sqrt(fabs(g->e2))))/2; /* authalic radius squared */
|
244
|
+
/* The sig12 threshold for "really short". Using the auxiliary sphere
|
245
|
+
* solution with dnm computed at (bet1 + bet2) / 2, the relative error in the
|
246
|
+
* azimuth consistency check is sig12^2 * abs(f) * min(1, 1-f/2) / 2. (Error
|
247
|
+
* measured for 1/100 < b/a < 100 and abs(f) >= 1/1000. For a given f and
|
248
|
+
* sig12, the max error occurs for lines near the pole. If the old rule for
|
249
|
+
* computing dnm = (dn1 + dn2)/2 is used, then the error increases by a
|
250
|
+
* factor of 2.) Setting this equal to epsilon gives sig12 = etol2. Here
|
251
|
+
* 0.1 is a safety factor (error decreased by 100) and max(0.001, abs(f))
|
252
|
+
* stops etol2 getting too large in the nearly spherical case. */
|
253
|
+
g->etol2 = 0.1 * tol2 /
|
254
|
+
sqrt( maxx((real)(0.001), fabs(g->f)) * minx((real)(1), 1 - g->f/2) / 2 );
|
255
|
+
|
256
|
+
A3coeff(g);
|
257
|
+
C3coeff(g);
|
258
|
+
C4coeff(g);
|
259
|
+
}
|
260
|
+
|
261
|
+
void geod_lineinit(struct geod_geodesicline* l,
|
262
|
+
const struct geod_geodesic* g,
|
263
|
+
real lat1, real lon1, real azi1, unsigned caps) {
|
264
|
+
real alp1, cbet1, sbet1, phi, eps;
|
265
|
+
l->a = g->a;
|
266
|
+
l->f = g->f;
|
267
|
+
l->b = g->b;
|
268
|
+
l->c2 = g->c2;
|
269
|
+
l->f1 = g->f1;
|
270
|
+
/* If caps is 0 assume the standard direct calculation */
|
271
|
+
l->caps = (caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE) |
|
272
|
+
GEOD_LATITUDE | GEOD_AZIMUTH; /* Always allow latitude and azimuth */
|
273
|
+
|
274
|
+
/* Guard against underflow in salp0 */
|
275
|
+
azi1 = AngRound(AngNormalize(azi1));
|
276
|
+
lon1 = AngNormalize(lon1);
|
277
|
+
l->lat1 = lat1;
|
278
|
+
l->lon1 = lon1;
|
279
|
+
l->azi1 = azi1;
|
280
|
+
/* alp1 is in [0, pi] */
|
281
|
+
alp1 = azi1 * degree;
|
282
|
+
/* Enforce sin(pi) == 0 and cos(pi/2) == 0. Better to face the ensuing
|
283
|
+
* problems directly than to skirt them. */
|
284
|
+
l->salp1 = azi1 == -180 ? 0 : sin(alp1);
|
285
|
+
l->calp1 = fabs(azi1) == 90 ? 0 : cos(alp1);
|
286
|
+
phi = lat1 * degree;
|
287
|
+
/* Ensure cbet1 = +epsilon at poles */
|
288
|
+
sbet1 = l->f1 * sin(phi);
|
289
|
+
cbet1 = fabs(lat1) == 90 ? tiny : cos(phi);
|
290
|
+
SinCosNorm(&sbet1, &cbet1);
|
291
|
+
l->dn1 = sqrt(1 + g->ep2 * sq(sbet1));
|
292
|
+
|
293
|
+
/* Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), */
|
294
|
+
l->salp0 = l->salp1 * cbet1; /* alp0 in [0, pi/2 - |bet1|] */
|
295
|
+
/* Alt: calp0 = hypot(sbet1, calp1 * cbet1). The following
|
296
|
+
* is slightly better (consider the case salp1 = 0). */
|
297
|
+
l->calp0 = hypotx(l->calp1, l->salp1 * sbet1);
|
298
|
+
/* Evaluate sig with tan(bet1) = tan(sig1) * cos(alp1).
|
299
|
+
* sig = 0 is nearest northward crossing of equator.
|
300
|
+
* With bet1 = 0, alp1 = pi/2, we have sig1 = 0 (equatorial line).
|
301
|
+
* With bet1 = pi/2, alp1 = -pi, sig1 = pi/2
|
302
|
+
* With bet1 = -pi/2, alp1 = 0 , sig1 = -pi/2
|
303
|
+
* Evaluate omg1 with tan(omg1) = sin(alp0) * tan(sig1).
|
304
|
+
* With alp0 in (0, pi/2], quadrants for sig and omg coincide.
|
305
|
+
* No atan2(0,0) ambiguity at poles since cbet1 = +epsilon.
|
306
|
+
* With alp0 = 0, omg1 = 0 for alp1 = 0, omg1 = pi for alp1 = pi. */
|
307
|
+
l->ssig1 = sbet1; l->somg1 = l->salp0 * sbet1;
|
308
|
+
l->csig1 = l->comg1 = sbet1 != 0 || l->calp1 != 0 ? cbet1 * l->calp1 : 1;
|
309
|
+
SinCosNorm(&l->ssig1, &l->csig1); /* sig1 in (-pi, pi] */
|
310
|
+
/* SinCosNorm(somg1, comg1); -- don't need to normalize! */
|
311
|
+
|
312
|
+
l->k2 = sq(l->calp0) * g->ep2;
|
313
|
+
eps = l->k2 / (2 * (1 + sqrt(1 + l->k2)) + l->k2);
|
314
|
+
|
315
|
+
if (l->caps & CAP_C1) {
|
316
|
+
real s, c;
|
317
|
+
l->A1m1 = A1m1f(eps);
|
318
|
+
C1f(eps, l->C1a);
|
319
|
+
l->B11 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C1a, nC1);
|
320
|
+
s = sin(l->B11); c = cos(l->B11);
|
321
|
+
/* tau1 = sig1 + B11 */
|
322
|
+
l->stau1 = l->ssig1 * c + l->csig1 * s;
|
323
|
+
l->ctau1 = l->csig1 * c - l->ssig1 * s;
|
324
|
+
/* Not necessary because C1pa reverts C1a
|
325
|
+
* B11 = -SinCosSeries(TRUE, stau1, ctau1, C1pa, nC1p); */
|
326
|
+
}
|
327
|
+
|
328
|
+
if (l->caps & CAP_C1p)
|
329
|
+
C1pf(eps, l->C1pa);
|
330
|
+
|
331
|
+
if (l->caps & CAP_C2) {
|
332
|
+
l->A2m1 = A2m1f(eps);
|
333
|
+
C2f(eps, l->C2a);
|
334
|
+
l->B21 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C2a, nC2);
|
335
|
+
}
|
336
|
+
|
337
|
+
if (l->caps & CAP_C3) {
|
338
|
+
C3f(g, eps, l->C3a);
|
339
|
+
l->A3c = -l->f * l->salp0 * A3f(g, eps);
|
340
|
+
l->B31 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C3a, nC3-1);
|
341
|
+
}
|
342
|
+
|
343
|
+
if (l->caps & CAP_C4) {
|
344
|
+
C4f(g, eps, l->C4a);
|
345
|
+
/* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0) */
|
346
|
+
l->A4 = sq(l->a) * l->calp0 * l->salp0 * g->e2;
|
347
|
+
l->B41 = SinCosSeries(FALSE, l->ssig1, l->csig1, l->C4a, nC4);
|
348
|
+
}
|
349
|
+
}
|
350
|
+
|
351
|
+
real geod_genposition(const struct geod_geodesicline* l,
|
352
|
+
boolx arcmode, real s12_a12,
|
353
|
+
real* plat2, real* plon2, real* pazi2,
|
354
|
+
real* ps12, real* pm12,
|
355
|
+
real* pM12, real* pM21,
|
356
|
+
real* pS12) {
|
357
|
+
real lat2 = 0, lon2 = 0, azi2 = 0, s12 = 0,
|
358
|
+
m12 = 0, M12 = 0, M21 = 0, S12 = 0;
|
359
|
+
/* Avoid warning about uninitialized B12. */
|
360
|
+
real sig12, ssig12, csig12, B12 = 0, AB1 = 0;
|
361
|
+
real omg12, lam12, lon12;
|
362
|
+
real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2, dn2;
|
363
|
+
unsigned outmask =
|
364
|
+
(plat2 ? GEOD_LATITUDE : 0U) |
|
365
|
+
(plon2 ? GEOD_LONGITUDE : 0U) |
|
366
|
+
(pazi2 ? GEOD_AZIMUTH : 0U) |
|
367
|
+
(ps12 ? GEOD_DISTANCE : 0U) |
|
368
|
+
(pm12 ? GEOD_REDUCEDLENGTH : 0U) |
|
369
|
+
(pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) |
|
370
|
+
(pS12 ? GEOD_AREA : 0U);
|
371
|
+
|
372
|
+
outmask &= l->caps & OUT_ALL;
|
373
|
+
if (!( TRUE /*Init()*/ &&
|
374
|
+
(arcmode || (l->caps & GEOD_DISTANCE_IN & OUT_ALL)) ))
|
375
|
+
/* Uninitialized or impossible distance calculation requested */
|
376
|
+
return NaN;
|
377
|
+
|
378
|
+
if (arcmode) {
|
379
|
+
real s12a;
|
380
|
+
/* Interpret s12_a12 as spherical arc length */
|
381
|
+
sig12 = s12_a12 * degree;
|
382
|
+
s12a = fabs(s12_a12);
|
383
|
+
s12a -= 180 * floor(s12a / 180);
|
384
|
+
ssig12 = s12a == 0 ? 0 : sin(sig12);
|
385
|
+
csig12 = s12a == 90 ? 0 : cos(sig12);
|
386
|
+
} else {
|
387
|
+
/* Interpret s12_a12 as distance */
|
388
|
+
real
|
389
|
+
tau12 = s12_a12 / (l->b * (1 + l->A1m1)),
|
390
|
+
s = sin(tau12),
|
391
|
+
c = cos(tau12);
|
392
|
+
/* tau2 = tau1 + tau12 */
|
393
|
+
B12 = - SinCosSeries(TRUE,
|
394
|
+
l->stau1 * c + l->ctau1 * s,
|
395
|
+
l->ctau1 * c - l->stau1 * s,
|
396
|
+
l->C1pa, nC1p);
|
397
|
+
sig12 = tau12 - (B12 - l->B11);
|
398
|
+
ssig12 = sin(sig12); csig12 = cos(sig12);
|
399
|
+
if (fabs(l->f) > 0.01) {
|
400
|
+
/* Reverted distance series is inaccurate for |f| > 1/100, so correct
|
401
|
+
* sig12 with 1 Newton iteration. The following table shows the
|
402
|
+
* approximate maximum error for a = WGS_a() and various f relative to
|
403
|
+
* GeodesicExact.
|
404
|
+
* erri = the error in the inverse solution (nm)
|
405
|
+
* errd = the error in the direct solution (series only) (nm)
|
406
|
+
* errda = the error in the direct solution (series + 1 Newton) (nm)
|
407
|
+
*
|
408
|
+
* f erri errd errda
|
409
|
+
* -1/5 12e6 1.2e9 69e6
|
410
|
+
* -1/10 123e3 12e6 765e3
|
411
|
+
* -1/20 1110 108e3 7155
|
412
|
+
* -1/50 18.63 200.9 27.12
|
413
|
+
* -1/100 18.63 23.78 23.37
|
414
|
+
* -1/150 18.63 21.05 20.26
|
415
|
+
* 1/150 22.35 24.73 25.83
|
416
|
+
* 1/100 22.35 25.03 25.31
|
417
|
+
* 1/50 29.80 231.9 30.44
|
418
|
+
* 1/20 5376 146e3 10e3
|
419
|
+
* 1/10 829e3 22e6 1.5e6
|
420
|
+
* 1/5 157e6 3.8e9 280e6 */
|
421
|
+
real
|
422
|
+
ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12,
|
423
|
+
csig2 = l->csig1 * csig12 - l->ssig1 * ssig12,
|
424
|
+
serr;
|
425
|
+
B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1);
|
426
|
+
serr = (1 + l->A1m1) * (sig12 + (B12 - l->B11)) - s12_a12 / l->b;
|
427
|
+
sig12 = sig12 - serr / sqrt(1 + l->k2 * sq(ssig2));
|
428
|
+
ssig12 = sin(sig12); csig12 = cos(sig12);
|
429
|
+
/* Update B12 below */
|
430
|
+
}
|
431
|
+
}
|
432
|
+
|
433
|
+
/* sig2 = sig1 + sig12 */
|
434
|
+
ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12;
|
435
|
+
csig2 = l->csig1 * csig12 - l->ssig1 * ssig12;
|
436
|
+
dn2 = sqrt(1 + l->k2 * sq(ssig2));
|
437
|
+
if (outmask & (GEOD_DISTANCE | GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) {
|
438
|
+
if (arcmode || fabs(l->f) > 0.01)
|
439
|
+
B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1);
|
440
|
+
AB1 = (1 + l->A1m1) * (B12 - l->B11);
|
441
|
+
}
|
442
|
+
/* sin(bet2) = cos(alp0) * sin(sig2) */
|
443
|
+
sbet2 = l->calp0 * ssig2;
|
444
|
+
/* Alt: cbet2 = hypot(csig2, salp0 * ssig2); */
|
445
|
+
cbet2 = hypotx(l->salp0, l->calp0 * csig2);
|
446
|
+
if (cbet2 == 0)
|
447
|
+
/* I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case */
|
448
|
+
cbet2 = csig2 = tiny;
|
449
|
+
/* tan(omg2) = sin(alp0) * tan(sig2) */
|
450
|
+
somg2 = l->salp0 * ssig2; comg2 = csig2; /* No need to normalize */
|
451
|
+
/* tan(alp0) = cos(sig2)*tan(alp2) */
|
452
|
+
salp2 = l->salp0; calp2 = l->calp0 * csig2; /* No need to normalize */
|
453
|
+
/* omg12 = omg2 - omg1 */
|
454
|
+
omg12 = atan2(somg2 * l->comg1 - comg2 * l->somg1,
|
455
|
+
comg2 * l->comg1 + somg2 * l->somg1);
|
456
|
+
|
457
|
+
if (outmask & GEOD_DISTANCE)
|
458
|
+
s12 = arcmode ? l->b * ((1 + l->A1m1) * sig12 + AB1) : s12_a12;
|
459
|
+
|
460
|
+
if (outmask & GEOD_LONGITUDE) {
|
461
|
+
lam12 = omg12 + l->A3c *
|
462
|
+
( sig12 + (SinCosSeries(TRUE, ssig2, csig2, l->C3a, nC3-1)
|
463
|
+
- l->B31));
|
464
|
+
lon12 = lam12 / degree;
|
465
|
+
/* Use AngNormalize2 because longitude might have wrapped multiple
|
466
|
+
* times. */
|
467
|
+
lon12 = AngNormalize2(lon12);
|
468
|
+
lon2 = AngNormalize(l->lon1 + lon12);
|
469
|
+
}
|
470
|
+
|
471
|
+
if (outmask & GEOD_LATITUDE)
|
472
|
+
lat2 = atan2(sbet2, l->f1 * cbet2) / degree;
|
473
|
+
|
474
|
+
if (outmask & GEOD_AZIMUTH)
|
475
|
+
/* minus signs give range [-180, 180). 0- converts -0 to +0. */
|
476
|
+
azi2 = 0 - atan2(-salp2, calp2) / degree;
|
477
|
+
|
478
|
+
if (outmask & (GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) {
|
479
|
+
real
|
480
|
+
B22 = SinCosSeries(TRUE, ssig2, csig2, l->C2a, nC2),
|
481
|
+
AB2 = (1 + l->A2m1) * (B22 - l->B21),
|
482
|
+
J12 = (l->A1m1 - l->A2m1) * sig12 + (AB1 - AB2);
|
483
|
+
if (outmask & GEOD_REDUCEDLENGTH)
|
484
|
+
/* Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure
|
485
|
+
* accurate cancellation in the case of coincident points. */
|
486
|
+
m12 = l->b * ((dn2 * (l->csig1 * ssig2) - l->dn1 * (l->ssig1 * csig2))
|
487
|
+
- l->csig1 * csig2 * J12);
|
488
|
+
if (outmask & GEOD_GEODESICSCALE) {
|
489
|
+
real t = l->k2 * (ssig2 - l->ssig1) * (ssig2 + l->ssig1) / (l->dn1 + dn2);
|
490
|
+
M12 = csig12 + (t * ssig2 - csig2 * J12) * l->ssig1 / l->dn1;
|
491
|
+
M21 = csig12 - (t * l->ssig1 - l->csig1 * J12) * ssig2 / dn2;
|
492
|
+
}
|
493
|
+
}
|
494
|
+
|
495
|
+
if (outmask & GEOD_AREA) {
|
496
|
+
real
|
497
|
+
B42 = SinCosSeries(FALSE, ssig2, csig2, l->C4a, nC4);
|
498
|
+
real salp12, calp12;
|
499
|
+
if (l->calp0 == 0 || l->salp0 == 0) {
|
500
|
+
/* alp12 = alp2 - alp1, used in atan2 so no need to normalized */
|
501
|
+
salp12 = salp2 * l->calp1 - calp2 * l->salp1;
|
502
|
+
calp12 = calp2 * l->calp1 + salp2 * l->salp1;
|
503
|
+
/* The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz
|
504
|
+
* salp12 = -0 and alp12 = -180. However this depends on the sign being
|
505
|
+
* attached to 0 correctly. The following ensures the correct
|
506
|
+
* behavior. */
|
507
|
+
if (salp12 == 0 && calp12 < 0) {
|
508
|
+
salp12 = tiny * l->calp1;
|
509
|
+
calp12 = -1;
|
510
|
+
}
|
511
|
+
} else {
|
512
|
+
/* tan(alp) = tan(alp0) * sec(sig)
|
513
|
+
* tan(alp2-alp1) = (tan(alp2) -tan(alp1)) / (tan(alp2)*tan(alp1)+1)
|
514
|
+
* = calp0 * salp0 * (csig1-csig2) / (salp0^2 + calp0^2 * csig1*csig2)
|
515
|
+
* If csig12 > 0, write
|
516
|
+
* csig1 - csig2 = ssig12 * (csig1 * ssig12 / (1 + csig12) + ssig1)
|
517
|
+
* else
|
518
|
+
* csig1 - csig2 = csig1 * (1 - csig12) + ssig12 * ssig1
|
519
|
+
* No need to normalize */
|
520
|
+
salp12 = l->calp0 * l->salp0 *
|
521
|
+
(csig12 <= 0 ? l->csig1 * (1 - csig12) + ssig12 * l->ssig1 :
|
522
|
+
ssig12 * (l->csig1 * ssig12 / (1 + csig12) + l->ssig1));
|
523
|
+
calp12 = sq(l->salp0) + sq(l->calp0) * l->csig1 * csig2;
|
524
|
+
}
|
525
|
+
S12 = l->c2 * atan2(salp12, calp12) + l->A4 * (B42 - l->B41);
|
526
|
+
}
|
527
|
+
|
528
|
+
if (outmask & GEOD_LATITUDE)
|
529
|
+
*plat2 = lat2;
|
530
|
+
if (outmask & GEOD_LONGITUDE)
|
531
|
+
*plon2 = lon2;
|
532
|
+
if (outmask & GEOD_AZIMUTH)
|
533
|
+
*pazi2 = azi2;
|
534
|
+
if (outmask & GEOD_DISTANCE)
|
535
|
+
*ps12 = s12;
|
536
|
+
if (outmask & GEOD_REDUCEDLENGTH)
|
537
|
+
*pm12 = m12;
|
538
|
+
if (outmask & GEOD_GEODESICSCALE) {
|
539
|
+
if (pM12) *pM12 = M12;
|
540
|
+
if (pM21) *pM21 = M21;
|
541
|
+
}
|
542
|
+
if (outmask & GEOD_AREA)
|
543
|
+
*pS12 = S12;
|
544
|
+
|
545
|
+
return arcmode ? s12_a12 : sig12 / degree;
|
546
|
+
}
|
547
|
+
|
548
|
+
void geod_position(const struct geod_geodesicline* l, real s12,
|
549
|
+
real* plat2, real* plon2, real* pazi2) {
|
550
|
+
geod_genposition(l, FALSE, s12, plat2, plon2, pazi2, 0, 0, 0, 0, 0);
|
551
|
+
}
|
552
|
+
|
553
|
+
real geod_gendirect(const struct geod_geodesic* g,
|
554
|
+
real lat1, real lon1, real azi1,
|
555
|
+
boolx arcmode, real s12_a12,
|
556
|
+
real* plat2, real* plon2, real* pazi2,
|
557
|
+
real* ps12, real* pm12, real* pM12, real* pM21,
|
558
|
+
real* pS12) {
|
559
|
+
struct geod_geodesicline l;
|
560
|
+
unsigned outmask =
|
561
|
+
(plat2 ? GEOD_LATITUDE : 0U) |
|
562
|
+
(plon2 ? GEOD_LONGITUDE : 0U) |
|
563
|
+
(pazi2 ? GEOD_AZIMUTH : 0U) |
|
564
|
+
(ps12 ? GEOD_DISTANCE : 0U) |
|
565
|
+
(pm12 ? GEOD_REDUCEDLENGTH : 0U) |
|
566
|
+
(pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) |
|
567
|
+
(pS12 ? GEOD_AREA : 0U);
|
568
|
+
|
569
|
+
geod_lineinit(&l, g, lat1, lon1, azi1,
|
570
|
+
/* Automatically supply GEOD_DISTANCE_IN if necessary */
|
571
|
+
outmask | (arcmode ? GEOD_NONE : GEOD_DISTANCE_IN));
|
572
|
+
return geod_genposition(&l, arcmode, s12_a12,
|
573
|
+
plat2, plon2, pazi2, ps12, pm12, pM12, pM21, pS12);
|
574
|
+
}
|
575
|
+
|
576
|
+
void geod_direct(const struct geod_geodesic* g,
|
577
|
+
real lat1, real lon1, real azi1,
|
578
|
+
real s12,
|
579
|
+
real* plat2, real* plon2, real* pazi2) {
|
580
|
+
geod_gendirect(g, lat1, lon1, azi1, FALSE, s12, plat2, plon2, pazi2,
|
581
|
+
0, 0, 0, 0, 0);
|
582
|
+
}
|
583
|
+
|
584
|
+
real geod_geninverse(const struct geod_geodesic* g,
|
585
|
+
real lat1, real lon1, real lat2, real lon2,
|
586
|
+
real* ps12, real* pazi1, real* pazi2,
|
587
|
+
real* pm12, real* pM12, real* pM21, real* pS12) {
|
588
|
+
real s12 = 0, azi1 = 0, azi2 = 0, m12 = 0, M12 = 0, M21 = 0, S12 = 0;
|
589
|
+
real lon12;
|
590
|
+
int latsign, lonsign, swapp;
|
591
|
+
real phi, sbet1, cbet1, sbet2, cbet2, s12x = 0, m12x = 0;
|
592
|
+
real dn1, dn2, lam12, slam12, clam12;
|
593
|
+
real a12 = 0, sig12, calp1 = 0, salp1 = 0, calp2 = 0, salp2 = 0;
|
594
|
+
/* index zero elements of these arrays are unused */
|
595
|
+
real C1a[nC1 + 1], C2a[nC2 + 1], C3a[nC3];
|
596
|
+
boolx meridian;
|
597
|
+
real omg12 = 0;
|
598
|
+
|
599
|
+
unsigned outmask =
|
600
|
+
(ps12 ? GEOD_DISTANCE : 0U) |
|
601
|
+
(pazi1 || pazi2 ? GEOD_AZIMUTH : 0U) |
|
602
|
+
(pm12 ? GEOD_REDUCEDLENGTH : 0U) |
|
603
|
+
(pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) |
|
604
|
+
(pS12 ? GEOD_AREA : 0U);
|
605
|
+
|
606
|
+
outmask &= OUT_ALL;
|
607
|
+
/* Compute longitude difference (AngDiff does this carefully). Result is
|
608
|
+
* in [-180, 180] but -180 is only for west-going geodesics. 180 is for
|
609
|
+
* east-going and meridional geodesics. */
|
610
|
+
lon12 = AngDiff(AngNormalize(lon1), AngNormalize(lon2));
|
611
|
+
/* If very close to being on the same half-meridian, then make it so. */
|
612
|
+
lon12 = AngRound(lon12);
|
613
|
+
/* Make longitude difference positive. */
|
614
|
+
lonsign = lon12 >= 0 ? 1 : -1;
|
615
|
+
lon12 *= lonsign;
|
616
|
+
/* If really close to the equator, treat as on equator. */
|
617
|
+
lat1 = AngRound(lat1);
|
618
|
+
lat2 = AngRound(lat2);
|
619
|
+
/* Swap points so that point with higher (abs) latitude is point 1 */
|
620
|
+
swapp = fabs(lat1) >= fabs(lat2) ? 1 : -1;
|
621
|
+
if (swapp < 0) {
|
622
|
+
lonsign *= -1;
|
623
|
+
swapx(&lat1, &lat2);
|
624
|
+
}
|
625
|
+
/* Make lat1 <= 0 */
|
626
|
+
latsign = lat1 < 0 ? 1 : -1;
|
627
|
+
lat1 *= latsign;
|
628
|
+
lat2 *= latsign;
|
629
|
+
/* Now we have
|
630
|
+
*
|
631
|
+
* 0 <= lon12 <= 180
|
632
|
+
* -90 <= lat1 <= 0
|
633
|
+
* lat1 <= lat2 <= -lat1
|
634
|
+
*
|
635
|
+
* longsign, swapp, latsign register the transformation to bring the
|
636
|
+
* coordinates to this canonical form. In all cases, 1 means no change was
|
637
|
+
* made. We make these transformations so that there are few cases to
|
638
|
+
* check, e.g., on verifying quadrants in atan2. In addition, this
|
639
|
+
* enforces some symmetries in the results returned. */
|
640
|
+
|
641
|
+
phi = lat1 * degree;
|
642
|
+
/* Ensure cbet1 = +epsilon at poles */
|
643
|
+
sbet1 = g->f1 * sin(phi);
|
644
|
+
cbet1 = lat1 == -90 ? tiny : cos(phi);
|
645
|
+
SinCosNorm(&sbet1, &cbet1);
|
646
|
+
|
647
|
+
phi = lat2 * degree;
|
648
|
+
/* Ensure cbet2 = +epsilon at poles */
|
649
|
+
sbet2 = g->f1 * sin(phi);
|
650
|
+
cbet2 = fabs(lat2) == 90 ? tiny : cos(phi);
|
651
|
+
SinCosNorm(&sbet2, &cbet2);
|
652
|
+
|
653
|
+
/* If cbet1 < -sbet1, then cbet2 - cbet1 is a sensitive measure of the
|
654
|
+
* |bet1| - |bet2|. Alternatively (cbet1 >= -sbet1), abs(sbet2) + sbet1 is
|
655
|
+
* a better measure. This logic is used in assigning calp2 in Lambda12.
|
656
|
+
* Sometimes these quantities vanish and in that case we force bet2 = +/-
|
657
|
+
* bet1 exactly. An example where is is necessary is the inverse problem
|
658
|
+
* 48.522876735459 0 -48.52287673545898293 179.599720456223079643
|
659
|
+
* which failed with Visual Studio 10 (Release and Debug) */
|
660
|
+
|
661
|
+
if (cbet1 < -sbet1) {
|
662
|
+
if (cbet2 == cbet1)
|
663
|
+
sbet2 = sbet2 < 0 ? sbet1 : -sbet1;
|
664
|
+
} else {
|
665
|
+
if (fabs(sbet2) == -sbet1)
|
666
|
+
cbet2 = cbet1;
|
667
|
+
}
|
668
|
+
|
669
|
+
dn1 = sqrt(1 + g->ep2 * sq(sbet1));
|
670
|
+
dn2 = sqrt(1 + g->ep2 * sq(sbet2));
|
671
|
+
|
672
|
+
lam12 = lon12 * degree;
|
673
|
+
slam12 = lon12 == 180 ? 0 : sin(lam12);
|
674
|
+
clam12 = cos(lam12); /* lon12 == 90 isn't interesting */
|
675
|
+
|
676
|
+
meridian = lat1 == -90 || slam12 == 0;
|
677
|
+
|
678
|
+
if (meridian) {
|
679
|
+
|
680
|
+
/* Endpoints are on a single full meridian, so the geodesic might lie on
|
681
|
+
* a meridian. */
|
682
|
+
|
683
|
+
real ssig1, csig1, ssig2, csig2;
|
684
|
+
calp1 = clam12; salp1 = slam12; /* Head to the target longitude */
|
685
|
+
calp2 = 1; salp2 = 0; /* At the target we're heading north */
|
686
|
+
|
687
|
+
/* tan(bet) = tan(sig) * cos(alp) */
|
688
|
+
ssig1 = sbet1; csig1 = calp1 * cbet1;
|
689
|
+
ssig2 = sbet2; csig2 = calp2 * cbet2;
|
690
|
+
|
691
|
+
/* sig12 = sig2 - sig1 */
|
692
|
+
sig12 = atan2(maxx(csig1 * ssig2 - ssig1 * csig2, (real)(0)),
|
693
|
+
csig1 * csig2 + ssig1 * ssig2);
|
694
|
+
{
|
695
|
+
real dummy;
|
696
|
+
Lengths(g, g->n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
|
697
|
+
cbet1, cbet2, &s12x, &m12x, &dummy,
|
698
|
+
(outmask & GEOD_GEODESICSCALE) != 0U, &M12, &M21, C1a, C2a);
|
699
|
+
}
|
700
|
+
/* Add the check for sig12 since zero length geodesics might yield m12 <
|
701
|
+
* 0. Test case was
|
702
|
+
*
|
703
|
+
* echo 20.001 0 20.001 0 | Geod -i
|
704
|
+
*
|
705
|
+
* In fact, we will have sig12 > pi/2 for meridional geodesic which is
|
706
|
+
* not a shortest path. */
|
707
|
+
if (sig12 < 1 || m12x >= 0) {
|
708
|
+
m12x *= g->b;
|
709
|
+
s12x *= g->b;
|
710
|
+
a12 = sig12 / degree;
|
711
|
+
} else
|
712
|
+
/* m12 < 0, i.e., prolate and too close to anti-podal */
|
713
|
+
meridian = FALSE;
|
714
|
+
}
|
715
|
+
|
716
|
+
if (!meridian &&
|
717
|
+
sbet1 == 0 && /* and sbet2 == 0 */
|
718
|
+
/* Mimic the way Lambda12 works with calp1 = 0 */
|
719
|
+
(g->f <= 0 || lam12 <= pi - g->f * pi)) {
|
720
|
+
|
721
|
+
/* Geodesic runs along equator */
|
722
|
+
calp1 = calp2 = 0; salp1 = salp2 = 1;
|
723
|
+
s12x = g->a * lam12;
|
724
|
+
sig12 = omg12 = lam12 / g->f1;
|
725
|
+
m12x = g->b * sin(sig12);
|
726
|
+
if (outmask & GEOD_GEODESICSCALE)
|
727
|
+
M12 = M21 = cos(sig12);
|
728
|
+
a12 = lon12 / g->f1;
|
729
|
+
|
730
|
+
} else if (!meridian) {
|
731
|
+
|
732
|
+
/* Now point1 and point2 belong within a hemisphere bounded by a
|
733
|
+
* meridian and geodesic is neither meridional or equatorial. */
|
734
|
+
|
735
|
+
/* Figure a starting point for Newton's method */
|
736
|
+
real dnm = 0;
|
737
|
+
sig12 = InverseStart(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2,
|
738
|
+
lam12,
|
739
|
+
&salp1, &calp1, &salp2, &calp2, &dnm,
|
740
|
+
C1a, C2a);
|
741
|
+
|
742
|
+
if (sig12 >= 0) {
|
743
|
+
/* Short lines (InverseStart sets salp2, calp2, dnm) */
|
744
|
+
s12x = sig12 * g->b * dnm;
|
745
|
+
m12x = sq(dnm) * g->b * sin(sig12 / dnm);
|
746
|
+
if (outmask & GEOD_GEODESICSCALE)
|
747
|
+
M12 = M21 = cos(sig12 / dnm);
|
748
|
+
a12 = sig12 / degree;
|
749
|
+
omg12 = lam12 / (g->f1 * dnm);
|
750
|
+
} else {
|
751
|
+
|
752
|
+
/* Newton's method. This is a straightforward solution of f(alp1) =
|
753
|
+
* lambda12(alp1) - lam12 = 0 with one wrinkle. f(alp) has exactly one
|
754
|
+
* root in the interval (0, pi) and its derivative is positive at the
|
755
|
+
* root. Thus f(alp) is positive for alp > alp1 and negative for alp <
|
756
|
+
* alp1. During the course of the iteration, a range (alp1a, alp1b) is
|
757
|
+
* maintained which brackets the root and with each evaluation of
|
758
|
+
* f(alp) the range is shrunk, if possible. Newton's method is
|
759
|
+
* restarted whenever the derivative of f is negative (because the new
|
760
|
+
* value of alp1 is then further from the solution) or if the new
|
761
|
+
* estimate of alp1 lies outside (0,pi); in this case, the new starting
|
762
|
+
* guess is taken to be (alp1a + alp1b) / 2. */
|
763
|
+
real ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0;
|
764
|
+
unsigned numit = 0;
|
765
|
+
/* Bracketing range */
|
766
|
+
real salp1a = tiny, calp1a = 1, salp1b = tiny, calp1b = -1;
|
767
|
+
boolx tripn, tripb;
|
768
|
+
for (tripn = FALSE, tripb = FALSE; numit < maxit2; ++numit) {
|
769
|
+
/* the WGS84 test set: mean = 1.47, sd = 1.25, max = 16
|
770
|
+
* WGS84 and random input: mean = 2.85, sd = 0.60 */
|
771
|
+
real dv,
|
772
|
+
v = (Lambda12(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1,
|
773
|
+
&salp2, &calp2, &sig12, &ssig1, &csig1, &ssig2, &csig2,
|
774
|
+
&eps, &omg12, numit < maxit1, &dv, C1a, C2a, C3a)
|
775
|
+
- lam12);
|
776
|
+
/* 2 * tol0 is approximately 1 ulp for a number in [0, pi]. */
|
777
|
+
/* Reversed test to allow escape with NaNs */
|
778
|
+
if (tripb || !(fabs(v) >= (tripn ? 8 : 2) * tol0)) break;
|
779
|
+
/* Update bracketing values */
|
780
|
+
if (v > 0 && (numit > maxit1 || calp1/salp1 > calp1b/salp1b))
|
781
|
+
{ salp1b = salp1; calp1b = calp1; }
|
782
|
+
else if (v < 0 && (numit > maxit1 || calp1/salp1 < calp1a/salp1a))
|
783
|
+
{ salp1a = salp1; calp1a = calp1; }
|
784
|
+
if (numit < maxit1 && dv > 0) {
|
785
|
+
real
|
786
|
+
dalp1 = -v/dv;
|
787
|
+
real
|
788
|
+
sdalp1 = sin(dalp1), cdalp1 = cos(dalp1),
|
789
|
+
nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
|
790
|
+
if (nsalp1 > 0 && fabs(dalp1) < pi) {
|
791
|
+
calp1 = calp1 * cdalp1 - salp1 * sdalp1;
|
792
|
+
salp1 = nsalp1;
|
793
|
+
SinCosNorm(&salp1, &calp1);
|
794
|
+
/* In some regimes we don't get quadratic convergence because
|
795
|
+
* slope -> 0. So use convergence conditions based on epsilon
|
796
|
+
* instead of sqrt(epsilon). */
|
797
|
+
tripn = fabs(v) <= 16 * tol0;
|
798
|
+
continue;
|
799
|
+
}
|
800
|
+
}
|
801
|
+
/* Either dv was not postive or updated value was outside legal
|
802
|
+
* range. Use the midpoint of the bracket as the next estimate.
|
803
|
+
* This mechanism is not needed for the WGS84 ellipsoid, but it does
|
804
|
+
* catch problems with more eccentric ellipsoids. Its efficacy is
|
805
|
+
* such for the WGS84 test set with the starting guess set to alp1 =
|
806
|
+
* 90deg:
|
807
|
+
* the WGS84 test set: mean = 5.21, sd = 3.93, max = 24
|
808
|
+
* WGS84 and random input: mean = 4.74, sd = 0.99 */
|
809
|
+
salp1 = (salp1a + salp1b)/2;
|
810
|
+
calp1 = (calp1a + calp1b)/2;
|
811
|
+
SinCosNorm(&salp1, &calp1);
|
812
|
+
tripn = FALSE;
|
813
|
+
tripb = (fabs(salp1a - salp1) + (calp1a - calp1) < tolb ||
|
814
|
+
fabs(salp1 - salp1b) + (calp1 - calp1b) < tolb);
|
815
|
+
}
|
816
|
+
{
|
817
|
+
real dummy;
|
818
|
+
Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
|
819
|
+
cbet1, cbet2, &s12x, &m12x, &dummy,
|
820
|
+
(outmask & GEOD_GEODESICSCALE) != 0U, &M12, &M21, C1a, C2a);
|
821
|
+
}
|
822
|
+
m12x *= g->b;
|
823
|
+
s12x *= g->b;
|
824
|
+
a12 = sig12 / degree;
|
825
|
+
omg12 = lam12 - omg12;
|
826
|
+
}
|
827
|
+
}
|
828
|
+
|
829
|
+
if (outmask & GEOD_DISTANCE)
|
830
|
+
s12 = 0 + s12x; /* Convert -0 to 0 */
|
831
|
+
|
832
|
+
if (outmask & GEOD_REDUCEDLENGTH)
|
833
|
+
m12 = 0 + m12x; /* Convert -0 to 0 */
|
834
|
+
|
835
|
+
if (outmask & GEOD_AREA) {
|
836
|
+
real
|
837
|
+
/* From Lambda12: sin(alp1) * cos(bet1) = sin(alp0) */
|
838
|
+
salp0 = salp1 * cbet1,
|
839
|
+
calp0 = hypotx(calp1, salp1 * sbet1); /* calp0 > 0 */
|
840
|
+
real alp12;
|
841
|
+
if (calp0 != 0 && salp0 != 0) {
|
842
|
+
real
|
843
|
+
/* From Lambda12: tan(bet) = tan(sig) * cos(alp) */
|
844
|
+
ssig1 = sbet1, csig1 = calp1 * cbet1,
|
845
|
+
ssig2 = sbet2, csig2 = calp2 * cbet2,
|
846
|
+
k2 = sq(calp0) * g->ep2,
|
847
|
+
eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2),
|
848
|
+
/* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0). */
|
849
|
+
A4 = sq(g->a) * calp0 * salp0 * g->e2;
|
850
|
+
real C4a[nC4];
|
851
|
+
real B41, B42;
|
852
|
+
SinCosNorm(&ssig1, &csig1);
|
853
|
+
SinCosNorm(&ssig2, &csig2);
|
854
|
+
C4f(g, eps, C4a);
|
855
|
+
B41 = SinCosSeries(FALSE, ssig1, csig1, C4a, nC4);
|
856
|
+
B42 = SinCosSeries(FALSE, ssig2, csig2, C4a, nC4);
|
857
|
+
S12 = A4 * (B42 - B41);
|
858
|
+
} else
|
859
|
+
/* Avoid problems with indeterminate sig1, sig2 on equator */
|
860
|
+
S12 = 0;
|
861
|
+
|
862
|
+
if (!meridian &&
|
863
|
+
omg12 < (real)(0.75) * pi && /* Long difference too big */
|
864
|
+
sbet2 - sbet1 < (real)(1.75)) { /* Lat difference too big */
|
865
|
+
/* Use tan(Gamma/2) = tan(omg12/2)
|
866
|
+
* * (tan(bet1/2)+tan(bet2/2))/(1+tan(bet1/2)*tan(bet2/2))
|
867
|
+
* with tan(x/2) = sin(x)/(1+cos(x)) */
|
868
|
+
real
|
869
|
+
somg12 = sin(omg12), domg12 = 1 + cos(omg12),
|
870
|
+
dbet1 = 1 + cbet1, dbet2 = 1 + cbet2;
|
871
|
+
alp12 = 2 * atan2( somg12 * ( sbet1 * dbet2 + sbet2 * dbet1 ),
|
872
|
+
domg12 * ( sbet1 * sbet2 + dbet1 * dbet2 ) );
|
873
|
+
} else {
|
874
|
+
/* alp12 = alp2 - alp1, used in atan2 so no need to normalize */
|
875
|
+
real
|
876
|
+
salp12 = salp2 * calp1 - calp2 * salp1,
|
877
|
+
calp12 = calp2 * calp1 + salp2 * salp1;
|
878
|
+
/* The right thing appears to happen if alp1 = +/-180 and alp2 = 0, viz
|
879
|
+
* salp12 = -0 and alp12 = -180. However this depends on the sign
|
880
|
+
* being attached to 0 correctly. The following ensures the correct
|
881
|
+
* behavior. */
|
882
|
+
if (salp12 == 0 && calp12 < 0) {
|
883
|
+
salp12 = tiny * calp1;
|
884
|
+
calp12 = -1;
|
885
|
+
}
|
886
|
+
alp12 = atan2(salp12, calp12);
|
887
|
+
}
|
888
|
+
S12 += g->c2 * alp12;
|
889
|
+
S12 *= swapp * lonsign * latsign;
|
890
|
+
/* Convert -0 to 0 */
|
891
|
+
S12 += 0;
|
892
|
+
}
|
893
|
+
|
894
|
+
/* Convert calp, salp to azimuth accounting for lonsign, swapp, latsign. */
|
895
|
+
if (swapp < 0) {
|
896
|
+
swapx(&salp1, &salp2);
|
897
|
+
swapx(&calp1, &calp2);
|
898
|
+
if (outmask & GEOD_GEODESICSCALE)
|
899
|
+
swapx(&M12, &M21);
|
900
|
+
}
|
901
|
+
|
902
|
+
salp1 *= swapp * lonsign; calp1 *= swapp * latsign;
|
903
|
+
salp2 *= swapp * lonsign; calp2 *= swapp * latsign;
|
904
|
+
|
905
|
+
if (outmask & GEOD_AZIMUTH) {
|
906
|
+
/* minus signs give range [-180, 180). 0- converts -0 to +0. */
|
907
|
+
azi1 = 0 - atan2(-salp1, calp1) / degree;
|
908
|
+
azi2 = 0 - atan2(-salp2, calp2) / degree;
|
909
|
+
}
|
910
|
+
|
911
|
+
if (outmask & GEOD_DISTANCE)
|
912
|
+
*ps12 = s12;
|
913
|
+
if (outmask & GEOD_AZIMUTH) {
|
914
|
+
if (pazi1) *pazi1 = azi1;
|
915
|
+
if (pazi2) *pazi2 = azi2;
|
916
|
+
}
|
917
|
+
if (outmask & GEOD_REDUCEDLENGTH)
|
918
|
+
*pm12 = m12;
|
919
|
+
if (outmask & GEOD_GEODESICSCALE) {
|
920
|
+
if (pM12) *pM12 = M12;
|
921
|
+
if (pM21) *pM21 = M21;
|
922
|
+
}
|
923
|
+
if (outmask & GEOD_AREA)
|
924
|
+
*pS12 = S12;
|
925
|
+
|
926
|
+
/* Returned value in [0, 180] */
|
927
|
+
return a12;
|
928
|
+
}
|
929
|
+
|
930
|
+
void geod_inverse(const struct geod_geodesic* g,
|
931
|
+
real lat1, real lon1, real lat2, real lon2,
|
932
|
+
real* ps12, real* pazi1, real* pazi2) {
|
933
|
+
geod_geninverse(g, lat1, lon1, lat2, lon2, ps12, pazi1, pazi2, 0, 0, 0, 0);
|
934
|
+
}
|
935
|
+
|
936
|
+
real SinCosSeries(boolx sinp, real sinx, real cosx, const real c[], int n) {
|
937
|
+
/* Evaluate
|
938
|
+
* y = sinp ? sum(c[i] * sin( 2*i * x), i, 1, n) :
|
939
|
+
* sum(c[i] * cos((2*i+1) * x), i, 0, n-1)
|
940
|
+
* using Clenshaw summation. N.B. c[0] is unused for sin series
|
941
|
+
* Approx operation count = (n + 5) mult and (2 * n + 2) add */
|
942
|
+
real ar, y0, y1;
|
943
|
+
c += (n + sinp); /* Point to one beyond last element */
|
944
|
+
ar = 2 * (cosx - sinx) * (cosx + sinx); /* 2 * cos(2 * x) */
|
945
|
+
y0 = n & 1 ? *--c : 0; y1 = 0; /* accumulators for sum */
|
946
|
+
/* Now n is even */
|
947
|
+
n /= 2;
|
948
|
+
while (n--) {
|
949
|
+
/* Unroll loop x 2, so accumulators return to their original role */
|
950
|
+
y1 = ar * y0 - y1 + *--c;
|
951
|
+
y0 = ar * y1 - y0 + *--c;
|
952
|
+
}
|
953
|
+
return sinp
|
954
|
+
? 2 * sinx * cosx * y0 /* sin(2 * x) * y0 */
|
955
|
+
: cosx * (y0 - y1); /* cos(x) * (y0 - y1) */
|
956
|
+
}
|
957
|
+
|
958
|
+
void Lengths(const struct geod_geodesic* g,
|
959
|
+
real eps, real sig12,
|
960
|
+
real ssig1, real csig1, real dn1,
|
961
|
+
real ssig2, real csig2, real dn2,
|
962
|
+
real cbet1, real cbet2,
|
963
|
+
real* ps12b, real* pm12b, real* pm0,
|
964
|
+
boolx scalep, real* pM12, real* pM21,
|
965
|
+
/* Scratch areas of the right size */
|
966
|
+
real C1a[], real C2a[]) {
|
967
|
+
real s12b = 0, m12b = 0, m0 = 0, M12 = 0, M21 = 0;
|
968
|
+
real A1m1, AB1, A2m1, AB2, J12;
|
969
|
+
|
970
|
+
/* Return m12b = (reduced length)/b; also calculate s12b = distance/b,
|
971
|
+
* and m0 = coefficient of secular term in expression for reduced length. */
|
972
|
+
C1f(eps, C1a);
|
973
|
+
C2f(eps, C2a);
|
974
|
+
A1m1 = A1m1f(eps);
|
975
|
+
AB1 = (1 + A1m1) * (SinCosSeries(TRUE, ssig2, csig2, C1a, nC1) -
|
976
|
+
SinCosSeries(TRUE, ssig1, csig1, C1a, nC1));
|
977
|
+
A2m1 = A2m1f(eps);
|
978
|
+
AB2 = (1 + A2m1) * (SinCosSeries(TRUE, ssig2, csig2, C2a, nC2) -
|
979
|
+
SinCosSeries(TRUE, ssig1, csig1, C2a, nC2));
|
980
|
+
m0 = A1m1 - A2m1;
|
981
|
+
J12 = m0 * sig12 + (AB1 - AB2);
|
982
|
+
/* Missing a factor of b.
|
983
|
+
* Add parens around (csig1 * ssig2) and (ssig1 * csig2) to ensure accurate
|
984
|
+
* cancellation in the case of coincident points. */
|
985
|
+
m12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - csig1 * csig2 * J12;
|
986
|
+
/* Missing a factor of b */
|
987
|
+
s12b = (1 + A1m1) * sig12 + AB1;
|
988
|
+
if (scalep) {
|
989
|
+
real csig12 = csig1 * csig2 + ssig1 * ssig2;
|
990
|
+
real t = g->ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
|
991
|
+
M12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1;
|
992
|
+
M21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2;
|
993
|
+
}
|
994
|
+
*ps12b = s12b;
|
995
|
+
*pm12b = m12b;
|
996
|
+
*pm0 = m0;
|
997
|
+
if (scalep) {
|
998
|
+
*pM12 = M12;
|
999
|
+
*pM21 = M21;
|
1000
|
+
}
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
real Astroid(real x, real y) {
|
1004
|
+
/* Solve k^4+2*k^3-(x^2+y^2-1)*k^2-2*y^2*k-y^2 = 0 for positive root k.
|
1005
|
+
* This solution is adapted from Geocentric::Reverse. */
|
1006
|
+
real k;
|
1007
|
+
real
|
1008
|
+
p = sq(x),
|
1009
|
+
q = sq(y),
|
1010
|
+
r = (p + q - 1) / 6;
|
1011
|
+
if ( !(q == 0 && r <= 0) ) {
|
1012
|
+
real
|
1013
|
+
/* Avoid possible division by zero when r = 0 by multiplying equations
|
1014
|
+
* for s and t by r^3 and r, resp. */
|
1015
|
+
S = p * q / 4, /* S = r^3 * s */
|
1016
|
+
r2 = sq(r),
|
1017
|
+
r3 = r * r2,
|
1018
|
+
/* The discrimant of the quadratic equation for T3. This is zero on
|
1019
|
+
* the evolute curve p^(1/3)+q^(1/3) = 1 */
|
1020
|
+
disc = S * (S + 2 * r3);
|
1021
|
+
real u = r;
|
1022
|
+
real v, uv, w;
|
1023
|
+
if (disc >= 0) {
|
1024
|
+
real T3 = S + r3, T;
|
1025
|
+
/* Pick the sign on the sqrt to maximize abs(T3). This minimizes loss
|
1026
|
+
* of precision due to cancellation. The result is unchanged because
|
1027
|
+
* of the way the T is used in definition of u. */
|
1028
|
+
T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc); /* T3 = (r * t)^3 */
|
1029
|
+
/* N.B. cbrtx always returns the real root. cbrtx(-8) = -2. */
|
1030
|
+
T = cbrtx(T3); /* T = r * t */
|
1031
|
+
/* T can be zero; but then r2 / T -> 0. */
|
1032
|
+
u += T + (T != 0 ? r2 / T : 0);
|
1033
|
+
} else {
|
1034
|
+
/* T is complex, but the way u is defined the result is real. */
|
1035
|
+
real ang = atan2(sqrt(-disc), -(S + r3));
|
1036
|
+
/* There are three possible cube roots. We choose the root which
|
1037
|
+
* avoids cancellation. Note that disc < 0 implies that r < 0. */
|
1038
|
+
u += 2 * r * cos(ang / 3);
|
1039
|
+
}
|
1040
|
+
v = sqrt(sq(u) + q); /* guaranteed positive */
|
1041
|
+
/* Avoid loss of accuracy when u < 0. */
|
1042
|
+
uv = u < 0 ? q / (v - u) : u + v; /* u+v, guaranteed positive */
|
1043
|
+
w = (uv - q) / (2 * v); /* positive? */
|
1044
|
+
/* Rearrange expression for k to avoid loss of accuracy due to
|
1045
|
+
* subtraction. Division by 0 not possible because uv > 0, w >= 0. */
|
1046
|
+
k = uv / (sqrt(uv + sq(w)) + w); /* guaranteed positive */
|
1047
|
+
} else { /* q == 0 && r <= 0 */
|
1048
|
+
/* y = 0 with |x| <= 1. Handle this case directly.
|
1049
|
+
* for y small, positive root is k = abs(y)/sqrt(1-x^2) */
|
1050
|
+
k = 0;
|
1051
|
+
}
|
1052
|
+
return k;
|
1053
|
+
}
|
1054
|
+
|
1055
|
+
real InverseStart(const struct geod_geodesic* g,
|
1056
|
+
real sbet1, real cbet1, real dn1,
|
1057
|
+
real sbet2, real cbet2, real dn2,
|
1058
|
+
real lam12,
|
1059
|
+
real* psalp1, real* pcalp1,
|
1060
|
+
/* Only updated if return val >= 0 */
|
1061
|
+
real* psalp2, real* pcalp2,
|
1062
|
+
/* Only updated for short lines */
|
1063
|
+
real* pdnm,
|
1064
|
+
/* Scratch areas of the right size */
|
1065
|
+
real C1a[], real C2a[]) {
|
1066
|
+
real salp1 = 0, calp1 = 0, salp2 = 0, calp2 = 0, dnm = 0;
|
1067
|
+
|
1068
|
+
/* Return a starting point for Newton's method in salp1 and calp1 (function
|
1069
|
+
* value is -1). If Newton's method doesn't need to be used, return also
|
1070
|
+
* salp2 and calp2 and function value is sig12. */
|
1071
|
+
real
|
1072
|
+
sig12 = -1, /* Return value */
|
1073
|
+
/* bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0] */
|
1074
|
+
sbet12 = sbet2 * cbet1 - cbet2 * sbet1,
|
1075
|
+
cbet12 = cbet2 * cbet1 + sbet2 * sbet1;
|
1076
|
+
#if defined(__GNUC__) && __GNUC__ == 4 && \
|
1077
|
+
(__GNUC_MINOR__ < 6 || defined(__MINGW32__))
|
1078
|
+
/* Volatile declaration needed to fix inverse cases
|
1079
|
+
* 88.202499451857 0 -88.202499451857 179.981022032992859592
|
1080
|
+
* 89.262080389218 0 -89.262080389218 179.992207982775375662
|
1081
|
+
* 89.333123580033 0 -89.333123580032997687 179.99295812360148422
|
1082
|
+
* which otherwise fail with g++ 4.4.4 x86 -O3 (Linux)
|
1083
|
+
* and g++ 4.4.0 (mingw) and g++ 4.6.1 (tdm mingw). */
|
1084
|
+
real sbet12a;
|
1085
|
+
{
|
1086
|
+
volatile real xx1 = sbet2 * cbet1;
|
1087
|
+
volatile real xx2 = cbet2 * sbet1;
|
1088
|
+
sbet12a = xx1 + xx2;
|
1089
|
+
}
|
1090
|
+
#else
|
1091
|
+
real sbet12a = sbet2 * cbet1 + cbet2 * sbet1;
|
1092
|
+
#endif
|
1093
|
+
boolx shortline = cbet12 >= 0 && sbet12 < (real)(0.5) &&
|
1094
|
+
cbet2 * lam12 < (real)(0.5);
|
1095
|
+
real omg12 = lam12, somg12, comg12, ssig12, csig12;
|
1096
|
+
if (shortline) {
|
1097
|
+
real sbetm2 = sq(sbet1 + sbet2);
|
1098
|
+
/* sin((bet1+bet2)/2)^2
|
1099
|
+
* = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) */
|
1100
|
+
sbetm2 /= sbetm2 + sq(cbet1 + cbet2);
|
1101
|
+
dnm = sqrt(1 + g->ep2 * sbetm2);
|
1102
|
+
omg12 /= g->f1 * dnm;
|
1103
|
+
}
|
1104
|
+
somg12 = sin(omg12); comg12 = cos(omg12);
|
1105
|
+
|
1106
|
+
salp1 = cbet2 * somg12;
|
1107
|
+
calp1 = comg12 >= 0 ?
|
1108
|
+
sbet12 + cbet2 * sbet1 * sq(somg12) / (1 + comg12) :
|
1109
|
+
sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12);
|
1110
|
+
|
1111
|
+
ssig12 = hypotx(salp1, calp1);
|
1112
|
+
csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12;
|
1113
|
+
|
1114
|
+
if (shortline && ssig12 < g->etol2) {
|
1115
|
+
/* really short lines */
|
1116
|
+
salp2 = cbet1 * somg12;
|
1117
|
+
calp2 = sbet12 - cbet1 * sbet2 *
|
1118
|
+
(comg12 >= 0 ? sq(somg12) / (1 + comg12) : 1 - comg12);
|
1119
|
+
SinCosNorm(&salp2, &calp2);
|
1120
|
+
/* Set return value */
|
1121
|
+
sig12 = atan2(ssig12, csig12);
|
1122
|
+
} else if (fabs(g->n) > (real)(0.1) || /* No astroid calc if too eccentric */
|
1123
|
+
csig12 >= 0 ||
|
1124
|
+
ssig12 >= 6 * fabs(g->n) * pi * sq(cbet1)) {
|
1125
|
+
/* Nothing to do, zeroth order spherical approximation is OK */
|
1126
|
+
} else {
|
1127
|
+
/* Scale lam12 and bet2 to x, y coordinate system where antipodal point
|
1128
|
+
* is at origin and singular point is at y = 0, x = -1. */
|
1129
|
+
real y, lamscale, betscale;
|
1130
|
+
/* Volatile declaration needed to fix inverse case
|
1131
|
+
* 56.320923501171 0 -56.320923501171 179.664747671772880215
|
1132
|
+
* which otherwise fails with g++ 4.4.4 x86 -O3 */
|
1133
|
+
volatile real x;
|
1134
|
+
if (g->f >= 0) { /* In fact f == 0 does not get here */
|
1135
|
+
/* x = dlong, y = dlat */
|
1136
|
+
{
|
1137
|
+
real
|
1138
|
+
k2 = sq(sbet1) * g->ep2,
|
1139
|
+
eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2);
|
1140
|
+
lamscale = g->f * cbet1 * A3f(g, eps) * pi;
|
1141
|
+
}
|
1142
|
+
betscale = lamscale * cbet1;
|
1143
|
+
|
1144
|
+
x = (lam12 - pi) / lamscale;
|
1145
|
+
y = sbet12a / betscale;
|
1146
|
+
} else { /* f < 0 */
|
1147
|
+
/* x = dlat, y = dlong */
|
1148
|
+
real
|
1149
|
+
cbet12a = cbet2 * cbet1 - sbet2 * sbet1,
|
1150
|
+
bet12a = atan2(sbet12a, cbet12a);
|
1151
|
+
real m12b, m0, dummy;
|
1152
|
+
/* In the case of lon12 = 180, this repeats a calculation made in
|
1153
|
+
* Inverse. */
|
1154
|
+
Lengths(g, g->n, pi + bet12a,
|
1155
|
+
sbet1, -cbet1, dn1, sbet2, cbet2, dn2,
|
1156
|
+
cbet1, cbet2, &dummy, &m12b, &m0, FALSE,
|
1157
|
+
&dummy, &dummy, C1a, C2a);
|
1158
|
+
x = -1 + m12b / (cbet1 * cbet2 * m0 * pi);
|
1159
|
+
betscale = x < -(real)(0.01) ? sbet12a / x :
|
1160
|
+
-g->f * sq(cbet1) * pi;
|
1161
|
+
lamscale = betscale / cbet1;
|
1162
|
+
y = (lam12 - pi) / lamscale;
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
if (y > -tol1 && x > -1 - xthresh) {
|
1166
|
+
/* strip near cut */
|
1167
|
+
if (g->f >= 0) {
|
1168
|
+
salp1 = minx((real)(1), -(real)(x)); calp1 = - sqrt(1 - sq(salp1));
|
1169
|
+
} else {
|
1170
|
+
calp1 = maxx((real)(x > -tol1 ? 0 : -1), (real)(x));
|
1171
|
+
salp1 = sqrt(1 - sq(calp1));
|
1172
|
+
}
|
1173
|
+
} else {
|
1174
|
+
/* Estimate alp1, by solving the astroid problem.
|
1175
|
+
*
|
1176
|
+
* Could estimate alpha1 = theta + pi/2, directly, i.e.,
|
1177
|
+
* calp1 = y/k; salp1 = -x/(1+k); for f >= 0
|
1178
|
+
* calp1 = x/(1+k); salp1 = -y/k; for f < 0 (need to check)
|
1179
|
+
*
|
1180
|
+
* However, it's better to estimate omg12 from astroid and use
|
1181
|
+
* spherical formula to compute alp1. This reduces the mean number of
|
1182
|
+
* Newton iterations for astroid cases from 2.24 (min 0, max 6) to 2.12
|
1183
|
+
* (min 0 max 5). The changes in the number of iterations are as
|
1184
|
+
* follows:
|
1185
|
+
*
|
1186
|
+
* change percent
|
1187
|
+
* 1 5
|
1188
|
+
* 0 78
|
1189
|
+
* -1 16
|
1190
|
+
* -2 0.6
|
1191
|
+
* -3 0.04
|
1192
|
+
* -4 0.002
|
1193
|
+
*
|
1194
|
+
* The histogram of iterations is (m = number of iterations estimating
|
1195
|
+
* alp1 directly, n = number of iterations estimating via omg12, total
|
1196
|
+
* number of trials = 148605):
|
1197
|
+
*
|
1198
|
+
* iter m n
|
1199
|
+
* 0 148 186
|
1200
|
+
* 1 13046 13845
|
1201
|
+
* 2 93315 102225
|
1202
|
+
* 3 36189 32341
|
1203
|
+
* 4 5396 7
|
1204
|
+
* 5 455 1
|
1205
|
+
* 6 56 0
|
1206
|
+
*
|
1207
|
+
* Because omg12 is near pi, estimate work with omg12a = pi - omg12 */
|
1208
|
+
real k = Astroid(x, y);
|
1209
|
+
real
|
1210
|
+
omg12a = lamscale * ( g->f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k );
|
1211
|
+
somg12 = sin(omg12a); comg12 = -cos(omg12a);
|
1212
|
+
/* Update spherical estimate of alp1 using omg12 instead of lam12 */
|
1213
|
+
salp1 = cbet2 * somg12;
|
1214
|
+
calp1 = sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12);
|
1215
|
+
}
|
1216
|
+
}
|
1217
|
+
if (salp1 > 0) /* Sanity check on starting guess */
|
1218
|
+
SinCosNorm(&salp1, &calp1);
|
1219
|
+
else {
|
1220
|
+
salp1 = 1; calp1 = 0;
|
1221
|
+
}
|
1222
|
+
|
1223
|
+
*psalp1 = salp1;
|
1224
|
+
*pcalp1 = calp1;
|
1225
|
+
if (shortline)
|
1226
|
+
*pdnm = dnm;
|
1227
|
+
if (sig12 >= 0) {
|
1228
|
+
*psalp2 = salp2;
|
1229
|
+
*pcalp2 = calp2;
|
1230
|
+
}
|
1231
|
+
return sig12;
|
1232
|
+
}
|
1233
|
+
|
1234
|
+
real Lambda12(const struct geod_geodesic* g,
|
1235
|
+
real sbet1, real cbet1, real dn1,
|
1236
|
+
real sbet2, real cbet2, real dn2,
|
1237
|
+
real salp1, real calp1,
|
1238
|
+
real* psalp2, real* pcalp2,
|
1239
|
+
real* psig12,
|
1240
|
+
real* pssig1, real* pcsig1,
|
1241
|
+
real* pssig2, real* pcsig2,
|
1242
|
+
real* peps, real* pdomg12,
|
1243
|
+
boolx diffp, real* pdlam12,
|
1244
|
+
/* Scratch areas of the right size */
|
1245
|
+
real C1a[], real C2a[], real C3a[]) {
|
1246
|
+
real salp2 = 0, calp2 = 0, sig12 = 0,
|
1247
|
+
ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, domg12 = 0, dlam12 = 0;
|
1248
|
+
real salp0, calp0;
|
1249
|
+
real somg1, comg1, somg2, comg2, omg12, lam12;
|
1250
|
+
real B312, h0, k2;
|
1251
|
+
|
1252
|
+
if (sbet1 == 0 && calp1 == 0)
|
1253
|
+
/* Break degeneracy of equatorial line. This case has already been
|
1254
|
+
* handled. */
|
1255
|
+
calp1 = -tiny;
|
1256
|
+
|
1257
|
+
/* sin(alp1) * cos(bet1) = sin(alp0) */
|
1258
|
+
salp0 = salp1 * cbet1;
|
1259
|
+
calp0 = hypotx(calp1, salp1 * sbet1); /* calp0 > 0 */
|
1260
|
+
|
1261
|
+
/* tan(bet1) = tan(sig1) * cos(alp1)
|
1262
|
+
* tan(omg1) = sin(alp0) * tan(sig1) = tan(omg1)=tan(alp1)*sin(bet1) */
|
1263
|
+
ssig1 = sbet1; somg1 = salp0 * sbet1;
|
1264
|
+
csig1 = comg1 = calp1 * cbet1;
|
1265
|
+
SinCosNorm(&ssig1, &csig1);
|
1266
|
+
/* SinCosNorm(&somg1, &comg1); -- don't need to normalize! */
|
1267
|
+
|
1268
|
+
/* Enforce symmetries in the case abs(bet2) = -bet1. Need to be careful
|
1269
|
+
* about this case, since this can yield singularities in the Newton
|
1270
|
+
* iteration.
|
1271
|
+
* sin(alp2) * cos(bet2) = sin(alp0) */
|
1272
|
+
salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1;
|
1273
|
+
/* calp2 = sqrt(1 - sq(salp2))
|
1274
|
+
* = sqrt(sq(calp0) - sq(sbet2)) / cbet2
|
1275
|
+
* and subst for calp0 and rearrange to give (choose positive sqrt
|
1276
|
+
* to give alp2 in [0, pi/2]). */
|
1277
|
+
calp2 = cbet2 != cbet1 || fabs(sbet2) != -sbet1 ?
|
1278
|
+
sqrt(sq(calp1 * cbet1) +
|
1279
|
+
(cbet1 < -sbet1 ?
|
1280
|
+
(cbet2 - cbet1) * (cbet1 + cbet2) :
|
1281
|
+
(sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 :
|
1282
|
+
fabs(calp1);
|
1283
|
+
/* tan(bet2) = tan(sig2) * cos(alp2)
|
1284
|
+
* tan(omg2) = sin(alp0) * tan(sig2). */
|
1285
|
+
ssig2 = sbet2; somg2 = salp0 * sbet2;
|
1286
|
+
csig2 = comg2 = calp2 * cbet2;
|
1287
|
+
SinCosNorm(&ssig2, &csig2);
|
1288
|
+
/* SinCosNorm(&somg2, &comg2); -- don't need to normalize! */
|
1289
|
+
|
1290
|
+
/* sig12 = sig2 - sig1, limit to [0, pi] */
|
1291
|
+
sig12 = atan2(maxx(csig1 * ssig2 - ssig1 * csig2, (real)(0)),
|
1292
|
+
csig1 * csig2 + ssig1 * ssig2);
|
1293
|
+
|
1294
|
+
/* omg12 = omg2 - omg1, limit to [0, pi] */
|
1295
|
+
omg12 = atan2(maxx(comg1 * somg2 - somg1 * comg2, (real)(0)),
|
1296
|
+
comg1 * comg2 + somg1 * somg2);
|
1297
|
+
k2 = sq(calp0) * g->ep2;
|
1298
|
+
eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2);
|
1299
|
+
C3f(g, eps, C3a);
|
1300
|
+
B312 = (SinCosSeries(TRUE, ssig2, csig2, C3a, nC3-1) -
|
1301
|
+
SinCosSeries(TRUE, ssig1, csig1, C3a, nC3-1));
|
1302
|
+
h0 = -g->f * A3f(g, eps);
|
1303
|
+
domg12 = salp0 * h0 * (sig12 + B312);
|
1304
|
+
lam12 = omg12 + domg12;
|
1305
|
+
|
1306
|
+
if (diffp) {
|
1307
|
+
if (calp2 == 0)
|
1308
|
+
dlam12 = - 2 * g->f1 * dn1 / sbet1;
|
1309
|
+
else {
|
1310
|
+
real dummy;
|
1311
|
+
Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
|
1312
|
+
cbet1, cbet2, &dummy, &dlam12, &dummy,
|
1313
|
+
FALSE, &dummy, &dummy, C1a, C2a);
|
1314
|
+
dlam12 *= g->f1 / (calp2 * cbet2);
|
1315
|
+
}
|
1316
|
+
}
|
1317
|
+
|
1318
|
+
*psalp2 = salp2;
|
1319
|
+
*pcalp2 = calp2;
|
1320
|
+
*psig12 = sig12;
|
1321
|
+
*pssig1 = ssig1;
|
1322
|
+
*pcsig1 = csig1;
|
1323
|
+
*pssig2 = ssig2;
|
1324
|
+
*pcsig2 = csig2;
|
1325
|
+
*peps = eps;
|
1326
|
+
*pdomg12 = domg12;
|
1327
|
+
if (diffp)
|
1328
|
+
*pdlam12 = dlam12;
|
1329
|
+
|
1330
|
+
return lam12;
|
1331
|
+
}
|
1332
|
+
|
1333
|
+
real A3f(const struct geod_geodesic* g, real eps) {
|
1334
|
+
/* Evaluate sum(A3x[k] * eps^k, k, 0, nA3x-1) by Horner's method */
|
1335
|
+
real v = 0;
|
1336
|
+
int i;
|
1337
|
+
for (i = nA3x; i; )
|
1338
|
+
v = eps * v + g->A3x[--i];
|
1339
|
+
return v;
|
1340
|
+
}
|
1341
|
+
|
1342
|
+
void C3f(const struct geod_geodesic* g, real eps, real c[]) {
|
1343
|
+
/* Evaluate C3 coeffs by Horner's method
|
1344
|
+
* Elements c[1] thru c[nC3 - 1] are set */
|
1345
|
+
int i, j, k;
|
1346
|
+
real mult = 1;
|
1347
|
+
for (j = nC3x, k = nC3 - 1; k; ) {
|
1348
|
+
real t = 0;
|
1349
|
+
for (i = nC3 - k; i; --i)
|
1350
|
+
t = eps * t + g->C3x[--j];
|
1351
|
+
c[k--] = t;
|
1352
|
+
}
|
1353
|
+
|
1354
|
+
for (k = 1; k < nC3; ) {
|
1355
|
+
mult *= eps;
|
1356
|
+
c[k++] *= mult;
|
1357
|
+
}
|
1358
|
+
}
|
1359
|
+
|
1360
|
+
void C4f(const struct geod_geodesic* g, real eps, real c[]) {
|
1361
|
+
/* Evaluate C4 coeffs by Horner's method
|
1362
|
+
* Elements c[0] thru c[nC4 - 1] are set */
|
1363
|
+
int i, j, k;
|
1364
|
+
real mult = 1;
|
1365
|
+
for (j = nC4x, k = nC4; k; ) {
|
1366
|
+
real t = 0;
|
1367
|
+
for (i = nC4 - k + 1; i; --i)
|
1368
|
+
t = eps * t + g->C4x[--j];
|
1369
|
+
c[--k] = t;
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
for (k = 1; k < nC4; ) {
|
1373
|
+
mult *= eps;
|
1374
|
+
c[k++] *= mult;
|
1375
|
+
}
|
1376
|
+
}
|
1377
|
+
|
1378
|
+
/* Generated by Maxima on 2010-09-04 10:26:17-04:00 */
|
1379
|
+
|
1380
|
+
/* The scale factor A1-1 = mean value of (d/dsigma)I1 - 1 */
|
1381
|
+
real A1m1f(real eps) {
|
1382
|
+
real
|
1383
|
+
eps2 = sq(eps),
|
1384
|
+
t = eps2*(eps2*(eps2+4)+64)/256;
|
1385
|
+
return (t + eps) / (1 - eps);
|
1386
|
+
}
|
1387
|
+
|
1388
|
+
/* The coefficients C1[l] in the Fourier expansion of B1 */
|
1389
|
+
void C1f(real eps, real c[]) {
|
1390
|
+
real
|
1391
|
+
eps2 = sq(eps),
|
1392
|
+
d = eps;
|
1393
|
+
c[1] = d*((6-eps2)*eps2-16)/32;
|
1394
|
+
d *= eps;
|
1395
|
+
c[2] = d*((64-9*eps2)*eps2-128)/2048;
|
1396
|
+
d *= eps;
|
1397
|
+
c[3] = d*(9*eps2-16)/768;
|
1398
|
+
d *= eps;
|
1399
|
+
c[4] = d*(3*eps2-5)/512;
|
1400
|
+
d *= eps;
|
1401
|
+
c[5] = -7*d/1280;
|
1402
|
+
d *= eps;
|
1403
|
+
c[6] = -7*d/2048;
|
1404
|
+
}
|
1405
|
+
|
1406
|
+
/* The coefficients C1p[l] in the Fourier expansion of B1p */
|
1407
|
+
void C1pf(real eps, real c[]) {
|
1408
|
+
real
|
1409
|
+
eps2 = sq(eps),
|
1410
|
+
d = eps;
|
1411
|
+
c[1] = d*(eps2*(205*eps2-432)+768)/1536;
|
1412
|
+
d *= eps;
|
1413
|
+
c[2] = d*(eps2*(4005*eps2-4736)+3840)/12288;
|
1414
|
+
d *= eps;
|
1415
|
+
c[3] = d*(116-225*eps2)/384;
|
1416
|
+
d *= eps;
|
1417
|
+
c[4] = d*(2695-7173*eps2)/7680;
|
1418
|
+
d *= eps;
|
1419
|
+
c[5] = 3467*d/7680;
|
1420
|
+
d *= eps;
|
1421
|
+
c[6] = 38081*d/61440;
|
1422
|
+
}
|
1423
|
+
|
1424
|
+
/* The scale factor A2-1 = mean value of (d/dsigma)I2 - 1 */
|
1425
|
+
real A2m1f(real eps) {
|
1426
|
+
real
|
1427
|
+
eps2 = sq(eps),
|
1428
|
+
t = eps2*(eps2*(25*eps2+36)+64)/256;
|
1429
|
+
return t * (1 - eps) - eps;
|
1430
|
+
}
|
1431
|
+
|
1432
|
+
/* The coefficients C2[l] in the Fourier expansion of B2 */
|
1433
|
+
void C2f(real eps, real c[]) {
|
1434
|
+
real
|
1435
|
+
eps2 = sq(eps),
|
1436
|
+
d = eps;
|
1437
|
+
c[1] = d*(eps2*(eps2+2)+16)/32;
|
1438
|
+
d *= eps;
|
1439
|
+
c[2] = d*(eps2*(35*eps2+64)+384)/2048;
|
1440
|
+
d *= eps;
|
1441
|
+
c[3] = d*(15*eps2+80)/768;
|
1442
|
+
d *= eps;
|
1443
|
+
c[4] = d*(7*eps2+35)/512;
|
1444
|
+
d *= eps;
|
1445
|
+
c[5] = 63*d/1280;
|
1446
|
+
d *= eps;
|
1447
|
+
c[6] = 77*d/2048;
|
1448
|
+
}
|
1449
|
+
|
1450
|
+
/* The scale factor A3 = mean value of (d/dsigma)I3 */
|
1451
|
+
void A3coeff(struct geod_geodesic* g) {
|
1452
|
+
g->A3x[0] = 1;
|
1453
|
+
g->A3x[1] = (g->n-1)/2;
|
1454
|
+
g->A3x[2] = (g->n*(3*g->n-1)-2)/8;
|
1455
|
+
g->A3x[3] = ((-g->n-3)*g->n-1)/16;
|
1456
|
+
g->A3x[4] = (-2*g->n-3)/64;
|
1457
|
+
g->A3x[5] = -3/(real)(128);
|
1458
|
+
}
|
1459
|
+
|
1460
|
+
/* The coefficients C3[l] in the Fourier expansion of B3 */
|
1461
|
+
void C3coeff(struct geod_geodesic* g) {
|
1462
|
+
g->C3x[0] = (1-g->n)/4;
|
1463
|
+
g->C3x[1] = (1-g->n*g->n)/8;
|
1464
|
+
g->C3x[2] = ((3-g->n)*g->n+3)/64;
|
1465
|
+
g->C3x[3] = (2*g->n+5)/128;
|
1466
|
+
g->C3x[4] = 3/(real)(128);
|
1467
|
+
g->C3x[5] = ((g->n-3)*g->n+2)/32;
|
1468
|
+
g->C3x[6] = ((-3*g->n-2)*g->n+3)/64;
|
1469
|
+
g->C3x[7] = (g->n+3)/128;
|
1470
|
+
g->C3x[8] = 5/(real)(256);
|
1471
|
+
g->C3x[9] = (g->n*(5*g->n-9)+5)/192;
|
1472
|
+
g->C3x[10] = (9-10*g->n)/384;
|
1473
|
+
g->C3x[11] = 7/(real)(512);
|
1474
|
+
g->C3x[12] = (7-14*g->n)/512;
|
1475
|
+
g->C3x[13] = 7/(real)(512);
|
1476
|
+
g->C3x[14] = 21/(real)(2560);
|
1477
|
+
}
|
1478
|
+
|
1479
|
+
/* Generated by Maxima on 2012-10-19 08:02:34-04:00 */
|
1480
|
+
|
1481
|
+
/* The coefficients C4[l] in the Fourier expansion of I4 */
|
1482
|
+
void C4coeff(struct geod_geodesic* g) {
|
1483
|
+
g->C4x[0] = (g->n*(g->n*(g->n*(g->n*(100*g->n+208)+572)+3432)-12012)+30030)/
|
1484
|
+
45045;
|
1485
|
+
g->C4x[1] = (g->n*(g->n*(g->n*(64*g->n+624)-4576)+6864)-3003)/15015;
|
1486
|
+
g->C4x[2] = (g->n*((14144-10656*g->n)*g->n-4576)-858)/45045;
|
1487
|
+
g->C4x[3] = ((-224*g->n-4784)*g->n+1573)/45045;
|
1488
|
+
g->C4x[4] = (1088*g->n+156)/45045;
|
1489
|
+
g->C4x[5] = 97/(real)(15015);
|
1490
|
+
g->C4x[6] = (g->n*(g->n*((-64*g->n-624)*g->n+4576)-6864)+3003)/135135;
|
1491
|
+
g->C4x[7] = (g->n*(g->n*(5952*g->n-11648)+9152)-2574)/135135;
|
1492
|
+
g->C4x[8] = (g->n*(5792*g->n+1040)-1287)/135135;
|
1493
|
+
g->C4x[9] = (468-2944*g->n)/135135;
|
1494
|
+
g->C4x[10] = 1/(real)(9009);
|
1495
|
+
g->C4x[11] = (g->n*((4160-1440*g->n)*g->n-4576)+1716)/225225;
|
1496
|
+
g->C4x[12] = ((4992-8448*g->n)*g->n-1144)/225225;
|
1497
|
+
g->C4x[13] = (1856*g->n-936)/225225;
|
1498
|
+
g->C4x[14] = 8/(real)(10725);
|
1499
|
+
g->C4x[15] = (g->n*(3584*g->n-3328)+1144)/315315;
|
1500
|
+
g->C4x[16] = (1024*g->n-208)/105105;
|
1501
|
+
g->C4x[17] = -136/(real)(63063);
|
1502
|
+
g->C4x[18] = (832-2560*g->n)/405405;
|
1503
|
+
g->C4x[19] = -128/(real)(135135);
|
1504
|
+
g->C4x[20] = 128/(real)(99099);
|
1505
|
+
}
|
1506
|
+
|
1507
|
+
int transit(real lon1, real lon2) {
|
1508
|
+
real lon12;
|
1509
|
+
/* Return 1 or -1 if crossing prime meridian in east or west direction.
|
1510
|
+
* Otherwise return zero. */
|
1511
|
+
/* Compute lon12 the same way as Geodesic::Inverse. */
|
1512
|
+
lon1 = AngNormalize(lon1);
|
1513
|
+
lon2 = AngNormalize(lon2);
|
1514
|
+
lon12 = AngDiff(lon1, lon2);
|
1515
|
+
return lon1 < 0 && lon2 >= 0 && lon12 > 0 ? 1 :
|
1516
|
+
(lon2 < 0 && lon1 >= 0 && lon12 < 0 ? -1 : 0);
|
1517
|
+
}
|
1518
|
+
|
1519
|
+
void accini(real s[]) {
|
1520
|
+
/* Initialize an accumulator; this is an array with two elements. */
|
1521
|
+
s[0] = s[1] = 0;
|
1522
|
+
}
|
1523
|
+
|
1524
|
+
void acccopy(const real s[], real t[]) {
|
1525
|
+
/* Copy an accumulator; t = s. */
|
1526
|
+
t[0] = s[0]; t[1] = s[1];
|
1527
|
+
}
|
1528
|
+
|
1529
|
+
void accadd(real s[], real y) {
|
1530
|
+
/* Add y to an accumulator. */
|
1531
|
+
real u, z = sumx(y, s[1], &u);
|
1532
|
+
s[0] = sumx(z, s[0], &s[1]);
|
1533
|
+
if (s[0] == 0)
|
1534
|
+
s[0] = u;
|
1535
|
+
else
|
1536
|
+
s[1] = s[1] + u;
|
1537
|
+
}
|
1538
|
+
|
1539
|
+
real accsum(const real s[], real y) {
|
1540
|
+
/* Return accumulator + y (but don't add to accumulator). */
|
1541
|
+
real t[2];
|
1542
|
+
acccopy(s, t);
|
1543
|
+
accadd(t, y);
|
1544
|
+
return t[0];
|
1545
|
+
}
|
1546
|
+
|
1547
|
+
void accneg(real s[]) {
|
1548
|
+
/* Negate an accumulator. */
|
1549
|
+
s[0] = -s[0]; s[1] = -s[1];
|
1550
|
+
}
|
1551
|
+
|
1552
|
+
void geod_polygon_init(struct geod_polygon* p, boolx polylinep) {
|
1553
|
+
p->lat0 = p->lon0 = p->lat = p->lon = NaN;
|
1554
|
+
p->polyline = (polylinep != 0);
|
1555
|
+
accini(p->P);
|
1556
|
+
accini(p->A);
|
1557
|
+
p->num = p->crossings = 0;
|
1558
|
+
}
|
1559
|
+
|
1560
|
+
void geod_polygon_addpoint(const struct geod_geodesic* g,
|
1561
|
+
struct geod_polygon* p,
|
1562
|
+
real lat, real lon) {
|
1563
|
+
lon = AngNormalize(lon);
|
1564
|
+
if (p->num == 0) {
|
1565
|
+
p->lat0 = p->lat = lat;
|
1566
|
+
p->lon0 = p->lon = lon;
|
1567
|
+
} else {
|
1568
|
+
real s12, S12;
|
1569
|
+
geod_geninverse(g, p->lat, p->lon, lat, lon,
|
1570
|
+
&s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12);
|
1571
|
+
accadd(p->P, s12);
|
1572
|
+
if (!p->polyline) {
|
1573
|
+
accadd(p->A, S12);
|
1574
|
+
p->crossings += transit(p->lon, lon);
|
1575
|
+
}
|
1576
|
+
p->lat = lat; p->lon = lon;
|
1577
|
+
}
|
1578
|
+
++p->num;
|
1579
|
+
}
|
1580
|
+
|
1581
|
+
void geod_polygon_addedge(const struct geod_geodesic* g,
|
1582
|
+
struct geod_polygon* p,
|
1583
|
+
real azi, real s) {
|
1584
|
+
if (p->num) { /* Do nothing is num is zero */
|
1585
|
+
real lat, lon, S12;
|
1586
|
+
geod_gendirect(g, p->lat, p->lon, azi, FALSE, s,
|
1587
|
+
&lat, &lon, 0,
|
1588
|
+
0, 0, 0, 0, p->polyline ? 0 : &S12);
|
1589
|
+
accadd(p->P, s);
|
1590
|
+
if (!p->polyline) {
|
1591
|
+
accadd(p->A, S12);
|
1592
|
+
p->crossings += transit(p->lon, lon);
|
1593
|
+
}
|
1594
|
+
p->lat = lat; p->lon = lon;
|
1595
|
+
++p->num;
|
1596
|
+
}
|
1597
|
+
}
|
1598
|
+
|
1599
|
+
unsigned geod_polygon_compute(const struct geod_geodesic* g,
|
1600
|
+
const struct geod_polygon* p,
|
1601
|
+
boolx reverse, boolx sign,
|
1602
|
+
real* pA, real* pP) {
|
1603
|
+
real s12, S12, t[2], area0;
|
1604
|
+
int crossings;
|
1605
|
+
if (p->num < 2) {
|
1606
|
+
if (pP) *pP = 0;
|
1607
|
+
if (!p->polyline && pA) *pA = 0;
|
1608
|
+
return p->num;
|
1609
|
+
}
|
1610
|
+
if (p->polyline) {
|
1611
|
+
if (pP) *pP = p->P[0];
|
1612
|
+
return p->num;
|
1613
|
+
}
|
1614
|
+
geod_geninverse(g, p->lat, p->lon, p->lat0, p->lon0,
|
1615
|
+
&s12, 0, 0, 0, 0, 0, &S12);
|
1616
|
+
if (pP) *pP = accsum(p->P, s12);
|
1617
|
+
acccopy(p->A, t);
|
1618
|
+
accadd(t, S12);
|
1619
|
+
crossings = p->crossings + transit(p->lon, p->lon0);
|
1620
|
+
area0 = 4 * pi * g->c2;
|
1621
|
+
if (crossings & 1)
|
1622
|
+
accadd(t, (t[0] < 0 ? 1 : -1) * area0/2);
|
1623
|
+
/* area is with the clockwise sense. If !reverse convert to
|
1624
|
+
* counter-clockwise convention. */
|
1625
|
+
if (!reverse)
|
1626
|
+
accneg(t);
|
1627
|
+
/* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */
|
1628
|
+
if (sign) {
|
1629
|
+
if (t[0] > area0/2)
|
1630
|
+
accadd(t, -area0);
|
1631
|
+
else if (t[0] <= -area0/2)
|
1632
|
+
accadd(t, +area0);
|
1633
|
+
} else {
|
1634
|
+
if (t[0] >= area0)
|
1635
|
+
accadd(t, -area0);
|
1636
|
+
else if (t[0] < 0)
|
1637
|
+
accadd(t, +area0);
|
1638
|
+
}
|
1639
|
+
if (pA) *pA = 0 + t[0];
|
1640
|
+
return p->num;
|
1641
|
+
}
|
1642
|
+
|
1643
|
+
unsigned geod_polygon_testpoint(const struct geod_geodesic* g,
|
1644
|
+
const struct geod_polygon* p,
|
1645
|
+
real lat, real lon,
|
1646
|
+
boolx reverse, boolx sign,
|
1647
|
+
real* pA, real* pP) {
|
1648
|
+
real perimeter, tempsum, area0;
|
1649
|
+
int crossings, i;
|
1650
|
+
unsigned num = p->num + 1;
|
1651
|
+
if (num == 1) {
|
1652
|
+
if (pP) *pP = 0;
|
1653
|
+
if (!p->polyline && pA) *pA = 0;
|
1654
|
+
return num;
|
1655
|
+
}
|
1656
|
+
perimeter = p->P[0];
|
1657
|
+
tempsum = p->polyline ? 0 : p->A[0];
|
1658
|
+
crossings = p->crossings;
|
1659
|
+
for (i = 0; i < (p->polyline ? 1 : 2); ++i) {
|
1660
|
+
real s12, S12;
|
1661
|
+
geod_geninverse(g,
|
1662
|
+
i == 0 ? p->lat : lat, i == 0 ? p->lon : lon,
|
1663
|
+
i != 0 ? p->lat0 : lat, i != 0 ? p->lon0 : lon,
|
1664
|
+
&s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12);
|
1665
|
+
perimeter += s12;
|
1666
|
+
if (!p->polyline) {
|
1667
|
+
tempsum += S12;
|
1668
|
+
crossings += transit(i == 0 ? p->lon : lon,
|
1669
|
+
i != 0 ? p->lon0 : lon);
|
1670
|
+
}
|
1671
|
+
}
|
1672
|
+
|
1673
|
+
if (pP) *pP = perimeter;
|
1674
|
+
if (p->polyline)
|
1675
|
+
return num;
|
1676
|
+
|
1677
|
+
area0 = 4 * pi * g->c2;
|
1678
|
+
if (crossings & 1)
|
1679
|
+
tempsum += (tempsum < 0 ? 1 : -1) * area0/2;
|
1680
|
+
/* area is with the clockwise sense. If !reverse convert to
|
1681
|
+
* counter-clockwise convention. */
|
1682
|
+
if (!reverse)
|
1683
|
+
tempsum *= -1;
|
1684
|
+
/* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */
|
1685
|
+
if (sign) {
|
1686
|
+
if (tempsum > area0/2)
|
1687
|
+
tempsum -= area0;
|
1688
|
+
else if (tempsum <= -area0/2)
|
1689
|
+
tempsum += area0;
|
1690
|
+
} else {
|
1691
|
+
if (tempsum >= area0)
|
1692
|
+
tempsum -= area0;
|
1693
|
+
else if (tempsum < 0)
|
1694
|
+
tempsum += area0;
|
1695
|
+
}
|
1696
|
+
if (pA) *pA = 0 + tempsum;
|
1697
|
+
return num;
|
1698
|
+
}
|
1699
|
+
|
1700
|
+
unsigned geod_polygon_testedge(const struct geod_geodesic* g,
|
1701
|
+
const struct geod_polygon* p,
|
1702
|
+
real azi, real s,
|
1703
|
+
boolx reverse, boolx sign,
|
1704
|
+
real* pA, real* pP) {
|
1705
|
+
real perimeter, tempsum, area0;
|
1706
|
+
int crossings;
|
1707
|
+
unsigned num = p->num + 1;
|
1708
|
+
if (num == 1) { /* we don't have a starting point! */
|
1709
|
+
if (pP) *pP = NaN;
|
1710
|
+
if (!p->polyline && pA) *pA = NaN;
|
1711
|
+
return 0;
|
1712
|
+
}
|
1713
|
+
perimeter = p->P[0] + s;
|
1714
|
+
if (p->polyline) {
|
1715
|
+
if (pP) *pP = perimeter;
|
1716
|
+
return num;
|
1717
|
+
}
|
1718
|
+
|
1719
|
+
tempsum = p->A[0];
|
1720
|
+
crossings = p->crossings;
|
1721
|
+
{
|
1722
|
+
real lat, lon, s12, S12;
|
1723
|
+
geod_gendirect(g, p->lat, p->lon, azi, FALSE, s,
|
1724
|
+
&lat, &lon, 0,
|
1725
|
+
0, 0, 0, 0, &S12);
|
1726
|
+
tempsum += S12;
|
1727
|
+
crossings += transit(p->lon, lon);
|
1728
|
+
geod_geninverse(g, lat, lon, p->lat0, p->lon0,
|
1729
|
+
&s12, 0, 0, 0, 0, 0, &S12);
|
1730
|
+
perimeter += s12;
|
1731
|
+
tempsum += S12;
|
1732
|
+
crossings += transit(lon, p->lon0);
|
1733
|
+
}
|
1734
|
+
|
1735
|
+
area0 = 4 * pi * g->c2;
|
1736
|
+
if (crossings & 1)
|
1737
|
+
tempsum += (tempsum < 0 ? 1 : -1) * area0/2;
|
1738
|
+
/* area is with the clockwise sense. If !reverse convert to
|
1739
|
+
* counter-clockwise convention. */
|
1740
|
+
if (!reverse)
|
1741
|
+
tempsum *= -1;
|
1742
|
+
/* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */
|
1743
|
+
if (sign) {
|
1744
|
+
if (tempsum > area0/2)
|
1745
|
+
tempsum -= area0;
|
1746
|
+
else if (tempsum <= -area0/2)
|
1747
|
+
tempsum += area0;
|
1748
|
+
} else {
|
1749
|
+
if (tempsum >= area0)
|
1750
|
+
tempsum -= area0;
|
1751
|
+
else if (tempsum < 0)
|
1752
|
+
tempsum += area0;
|
1753
|
+
}
|
1754
|
+
if (pP) *pP = perimeter;
|
1755
|
+
if (pA) *pA = 0 + tempsum;
|
1756
|
+
return num;
|
1757
|
+
}
|
1758
|
+
|
1759
|
+
void geod_polygonarea(const struct geod_geodesic* g,
|
1760
|
+
real lats[], real lons[], int n,
|
1761
|
+
real* pA, real* pP) {
|
1762
|
+
int i;
|
1763
|
+
struct geod_polygon p;
|
1764
|
+
geod_polygon_init(&p, FALSE);
|
1765
|
+
for (i = 0; i < n; ++i)
|
1766
|
+
geod_polygon_addpoint(g, &p, lats[i], lons[i]);
|
1767
|
+
geod_polygon_compute(g, &p, FALSE, TRUE, pA, pP);
|
1768
|
+
}
|
1769
|
+
|
1770
|
+
/** @endcond */
|