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