Line | Count | Source |
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 | 288k | #define NULLPTR 0 |
36 | | #endif |
37 | | |
38 | 27.9M | #define GEOGRAPHICLIB_GEODESIC_ORDER 6 |
39 | 65.1k | #define nA1 GEOGRAPHICLIB_GEODESIC_ORDER |
40 | 877k | #define nC1 GEOGRAPHICLIB_GEODESIC_ORDER |
41 | 0 | #define nC1p GEOGRAPHICLIB_GEODESIC_ORDER |
42 | 65.1k | #define nA2 GEOGRAPHICLIB_GEODESIC_ORDER |
43 | 1.32M | #define nC2 GEOGRAPHICLIB_GEODESIC_ORDER |
44 | 3.08M | #define nA3 GEOGRAPHICLIB_GEODESIC_ORDER |
45 | | #define nA3x nA3 |
46 | 12.1M | #define nC3 GEOGRAPHICLIB_GEODESIC_ORDER |
47 | | #define nC3x ((nC3 * (nC3 - 1)) / 2) |
48 | 10.3M | #define nC4 GEOGRAPHICLIB_GEODESIC_ORDER |
49 | | #define nC4x ((nC4 * (nC4 + 1)) / 2) |
50 | | #define nC (GEOGRAPHICLIB_GEODESIC_ORDER + 1) |
51 | | |
52 | | typedef int boolx; |
53 | | enum booly { FALSE = 0, TRUE = 1 }; |
54 | | /* qd = quarter turn / degree |
55 | | * hd = half turn / degree |
56 | | * td = full turn / degree */ |
57 | | enum dms { qd = 90, hd = 2 * qd, td = 2 * hd }; |
58 | | |
59 | | static unsigned init = 0; |
60 | | static unsigned digits, maxit1, maxit2; |
61 | | static double epsilon, realmin, pi, degree, NaN, |
62 | | tiny, tol0, tol1, tol2, tolb, xthresh; |
63 | | |
64 | 1 | static void Init(void) { |
65 | 1 | if (!init) { |
66 | 1 | digits = DBL_MANT_DIG; |
67 | 1 | epsilon = DBL_EPSILON; |
68 | 1 | realmin = DBL_MIN; |
69 | | #if defined(M_PI) |
70 | | pi = M_PI; |
71 | | #else |
72 | 1 | pi = atan2(0.0, -1.0); |
73 | 1 | #endif |
74 | 1 | maxit1 = 20; |
75 | 1 | maxit2 = maxit1 + digits + 10; |
76 | 1 | tiny = sqrt(realmin); |
77 | 1 | 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 | 1 | tol1 = 200 * tol0; |
82 | 1 | tol2 = sqrt(tol0); |
83 | | /* Check on bisection interval */ |
84 | 1 | tolb = tol0; |
85 | 1 | xthresh = 1000 * tol2; |
86 | 1 | degree = pi/hd; |
87 | 1 | NaN = nan("0"); |
88 | 1 | init = 1; |
89 | 1 | } |
90 | 1 | } |
91 | | |
92 | | enum 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 | | |
103 | 1.31M | static double sq(double x) { return x * x; } |
104 | | |
105 | 30.4k | static double sumx(double u, double v, double* t) { |
106 | 30.4k | volatile double s = u + v; |
107 | 30.4k | volatile double up = s - v; |
108 | 30.4k | volatile double vpp = s - up; |
109 | 30.4k | up -= u; |
110 | 30.4k | vpp -= v; |
111 | 30.4k | if (t) *t = s != 0 ? 0 - (up + vpp) : s; |
112 | | /* error-free sum: |
113 | | * u + v = s + t |
114 | | * = round(u + v) + t */ |
115 | 30.4k | return s; |
116 | 30.4k | } |
117 | | |
118 | 13.9M | static double polyvalx(int N, const double p[], double x) { |
119 | 13.9M | double y = N < 0 ? 0 : *p++; |
120 | 32.9M | while (--N >= 0) y = y * x + *p++; |
121 | 13.9M | return y; |
122 | 13.9M | } |
123 | | |
124 | | static void swapx(double* x, double* y) |
125 | 54.0k | { double t = *x; *x = *y; *y = t; } |
126 | | |
127 | 180k | static 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 | 180k | double r = hypot(*sinx, *cosx); |
140 | 180k | #endif |
141 | 180k | *sinx /= r; |
142 | 180k | *cosx /= r; |
143 | 180k | } |
144 | | |
145 | 0 | static double AngNormalize(double x) { |
146 | 0 | double y = remainder(x, (double)td); |
147 | 0 | return fabs(y) == hd ? copysign((double)hd, x) : y; |
148 | 0 | } |
149 | | |
150 | | static double LatFix(double x) |
151 | 30.4k | { return fabs(x) > qd ? NaN : x; } |
152 | | |
153 | 15.2k | static 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 | 15.2k | 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 | 15.2k | d = sumx(remainder(d, (double)td), t, &t); |
160 | | /* Fix the sign if d = -180, 0, 180. */ |
161 | 15.2k | 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 | 46 | d = copysign(d, t == 0 ? y - x : -t); |
165 | 15.2k | if (e) *e = t; |
166 | 15.2k | return d; |
167 | 15.2k | } |
168 | | |
169 | 45.6k | static double AngRound(double x) { |
170 | | /* False positive in cppcheck requires "1.0" instead of "1" */ |
171 | 45.6k | const double z = 1.0/16.0; |
172 | 45.6k | volatile double y = fabs(x); |
173 | 45.6k | volatile double w = z - y; |
174 | | /* The compiler mustn't "simplify" z - (z - y) to y */ |
175 | 45.6k | y = w > 0 ? z - w : y; |
176 | 45.6k | return copysign(y, x); |
177 | 45.6k | } |
178 | | |
179 | 30.4k | static 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 | 30.4k | double r, s, c; int q = 0; |
183 | 30.4k | r = remquo(x, (double)qd, &q); |
184 | | /* now abs(r) <= 45 */ |
185 | 30.4k | r *= degree; |
186 | | /* Possibly could call the gnu extension sincos */ |
187 | 30.4k | s = sin(r); c = cos(r); |
188 | 30.4k | switch ((unsigned)q & 3U) { |
189 | 18.6k | case 0U: *sinx = s; *cosx = c; break; |
190 | 0 | case 1U: *sinx = c; *cosx = -s; break; |
191 | 0 | case 2U: *sinx = -s; *cosx = -c; break; |
192 | 11.7k | default: *sinx = -c; *cosx = s; break; /* case 3U */ |
193 | 30.4k | } |
194 | | /* http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1950.pdf */ |
195 | 30.4k | *cosx += 0; /* special values from F.10.1.12 */ |
196 | | /* special values from F.10.1.13 */ |
197 | 30.4k | if (*sinx == 0) *sinx = copysign(*sinx, x); |
198 | 30.4k | } |
199 | | |
200 | 15.2k | static 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 | 15.2k | double r, s, c; int q = 0; |
204 | 15.2k | r = AngRound(remquo(x, (double)qd, &q) + t); |
205 | | /* now abs(r) <= 45 */ |
206 | 15.2k | r *= degree; |
207 | | /* Possibly could call the gnu extension sincos */ |
208 | 15.2k | s = sin(r); c = cos(r); |
209 | 15.2k | switch ((unsigned)q & 3U) { |
210 | 9.87k | case 0U: *sinx = s; *cosx = c; break; |
211 | 4.36k | case 1U: *sinx = c; *cosx = -s; break; |
212 | 961 | case 2U: *sinx = -s; *cosx = -c; break; |
213 | 0 | default: *sinx = -c; *cosx = s; break; /* case 3U */ |
214 | 15.2k | } |
215 | | /* http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1950.pdf */ |
216 | 15.2k | *cosx += 0; /* special values from F.10.1.12 */ |
217 | | /* special values from F.10.1.13 */ |
218 | 15.2k | if (*sinx == 0) *sinx = copysign(*sinx, x); |
219 | 15.2k | } |
220 | | |
221 | 27.5k | static 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 | 27.5k | int q = 0; double ang; |
227 | 27.5k | if (fabs(y) > fabs(x)) { swapx(&x, &y); q = 2; } |
228 | 27.5k | if (signbit(x)) { x = -x; ++q; } |
229 | | /* here x >= 0 and x >= abs(y), so angle is in [-pi/4, pi/4] */ |
230 | 27.5k | ang = atan2(y, x) / degree; |
231 | 27.5k | switch (q) { |
232 | 276 | case 1: ang = copysign((double)hd, y) - ang; break; |
233 | 0 | case 2: ang = qd - ang; break; |
234 | 5.56k | case 3: ang = -qd + ang; break; |
235 | 21.7k | default: break; |
236 | 27.5k | } |
237 | 27.5k | return ang; |
238 | 27.5k | } |
239 | | |
240 | | static void A3coeff(struct geod_geodesic* g); |
241 | | static void C3coeff(struct geod_geodesic* g); |
242 | | static void C4coeff(struct geod_geodesic* g); |
243 | | static double SinCosSeries(boolx sinp, |
244 | | double sinx, double cosx, |
245 | | const double c[], int n); |
246 | | static 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[]); |
255 | | static double Astroid(double x, double y); |
256 | | static 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[]); |
267 | | static 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[]); |
281 | | static double A3f(const struct geod_geodesic* g, double eps); |
282 | | static void C3f(const struct geod_geodesic* g, double eps, double c[]); |
283 | | static void C4f(const struct geod_geodesic* g, double eps, double c[]); |
284 | | static double A1m1f(double eps); |
285 | | static void C1f(double eps, double c[]); |
286 | | static void C1pf(double eps, double c[]); |
287 | | static double A2m1f(double eps); |
288 | | static void C2f(double eps, double c[]); |
289 | | static int transit(double lon1, double lon2); |
290 | | static int transitdirect(double lon1, double lon2); |
291 | | static void accini(double s[]); |
292 | | static void acccopy(const double s[], double t[]); |
293 | | static void accadd(double s[], double y); |
294 | | static double accsum(const double s[], double y); |
295 | | static void accneg(double s[]); |
296 | | static void accrem(double s[], double y); |
297 | | static double areareduceA(double area[], double area0, |
298 | | int crossings, boolx reverse, boolx sign); |
299 | | static double areareduceB(double area, double area0, |
300 | | int crossings, boolx reverse, boolx sign); |
301 | | |
302 | 303k | void geod_init(struct geod_geodesic* g, double a, double f) { |
303 | 303k | if (!init) Init(); |
304 | 303k | g->a = a; |
305 | 303k | g->f = f; |
306 | 303k | g->f1 = 1 - g->f; |
307 | 303k | g->e2 = g->f * (2 - g->f); |
308 | 303k | g->ep2 = g->e2 / sq(g->f1); /* e2 / (1 - e2) */ |
309 | 303k | g->n = g->f / ( 2 - g->f); |
310 | 303k | g->b = g->a * g->f1; |
311 | 303k | g->c2 = (sq(g->a) + sq(g->b) * |
312 | 303k | (g->e2 == 0 ? 1 : |
313 | 303k | (g->e2 > 0 ? atanh(sqrt(g->e2)) : atan(sqrt(-g->e2))) / |
314 | 289k | 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 | 303k | g->etol2 = 0.1 * tol2 / |
325 | 303k | sqrt( fmax(0.001, fabs(g->f)) * fmin(1.0, 1 - g->f/2) / 2 ); |
326 | | |
327 | 303k | A3coeff(g); |
328 | 303k | C3coeff(g); |
329 | 303k | C4coeff(g); |
330 | 303k | } |
331 | | |
332 | | static 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 | 0 | unsigned caps) { |
337 | 0 | double cbet1, sbet1, eps; |
338 | 0 | l->a = g->a; |
339 | 0 | l->f = g->f; |
340 | 0 | l->b = g->b; |
341 | 0 | l->c2 = g->c2; |
342 | 0 | l->f1 = g->f1; |
343 | | /* If caps is 0 assume the standard direct calculation */ |
344 | 0 | l->caps = (caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE) | |
345 | | /* always allow latitude and azimuth and unrolling of longitude */ |
346 | 0 | GEOD_LATITUDE | GEOD_AZIMUTH | GEOD_LONG_UNROLL; |
347 | |
|
348 | 0 | l->lat1 = LatFix(lat1); |
349 | 0 | l->lon1 = lon1; |
350 | 0 | l->azi1 = azi1; |
351 | 0 | l->salp1 = salp1; |
352 | 0 | l->calp1 = calp1; |
353 | |
|
354 | 0 | sincosdx(AngRound(l->lat1), &sbet1, &cbet1); sbet1 *= l->f1; |
355 | | /* Ensure cbet1 = +epsilon at poles */ |
356 | 0 | norm2(&sbet1, &cbet1); cbet1 = fmax(tiny, cbet1); |
357 | 0 | l->dn1 = sqrt(1 + g->ep2 * sq(sbet1)); |
358 | | |
359 | | /* Evaluate alp0 from sin(alp1) * cos(bet1) = sin(alp0), */ |
360 | 0 | 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 | 0 | 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 | 0 | l->ssig1 = sbet1; l->somg1 = l->salp0 * sbet1; |
374 | 0 | l->csig1 = l->comg1 = sbet1 != 0 || l->calp1 != 0 ? cbet1 * l->calp1 : 1; |
375 | 0 | norm2(&l->ssig1, &l->csig1); /* sig1 in (-pi, pi] */ |
376 | | /* norm2(somg1, comg1); -- don't need to normalize! */ |
377 | |
|
378 | 0 | l->k2 = sq(l->calp0) * g->ep2; |
379 | 0 | eps = l->k2 / (2 * (1 + sqrt(1 + l->k2)) + l->k2); |
380 | |
|
381 | 0 | if (l->caps & CAP_C1) { |
382 | 0 | double s, c; |
383 | 0 | l->A1m1 = A1m1f(eps); |
384 | 0 | C1f(eps, l->C1a); |
385 | 0 | l->B11 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C1a, nC1); |
386 | 0 | s = sin(l->B11); c = cos(l->B11); |
387 | | /* tau1 = sig1 + B11 */ |
388 | 0 | l->stau1 = l->ssig1 * c + l->csig1 * s; |
389 | 0 | l->ctau1 = l->csig1 * c - l->ssig1 * s; |
390 | | /* Not necessary because C1pa reverts C1a |
391 | | * B11 = -SinCosSeries(TRUE, stau1, ctau1, C1pa, nC1p); */ |
392 | 0 | } |
393 | |
|
394 | 0 | if (l->caps & CAP_C1p) |
395 | 0 | C1pf(eps, l->C1pa); |
396 | |
|
397 | 0 | if (l->caps & CAP_C2) { |
398 | 0 | l->A2m1 = A2m1f(eps); |
399 | 0 | C2f(eps, l->C2a); |
400 | 0 | l->B21 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C2a, nC2); |
401 | 0 | } |
402 | |
|
403 | 0 | if (l->caps & CAP_C3) { |
404 | 0 | C3f(g, eps, l->C3a); |
405 | 0 | l->A3c = -l->f * l->salp0 * A3f(g, eps); |
406 | 0 | l->B31 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C3a, nC3-1); |
407 | 0 | } |
408 | |
|
409 | 0 | if (l->caps & CAP_C4) { |
410 | 0 | C4f(g, eps, l->C4a); |
411 | | /* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0) */ |
412 | 0 | l->A4 = sq(l->a) * l->calp0 * l->salp0 * g->e2; |
413 | 0 | l->B41 = SinCosSeries(FALSE, l->ssig1, l->csig1, l->C4a, nC4); |
414 | 0 | } |
415 | |
|
416 | 0 | l->a13 = l->s13 = NaN; |
417 | 0 | } |
418 | | |
419 | | void geod_lineinit(struct geod_geodesicline* l, |
420 | | const struct geod_geodesic* g, |
421 | 0 | double lat1, double lon1, double azi1, unsigned caps) { |
422 | 0 | double salp1, calp1; |
423 | 0 | azi1 = AngNormalize(azi1); |
424 | | /* Guard against underflow in salp0 */ |
425 | 0 | sincosdx(AngRound(azi1), &salp1, &calp1); |
426 | 0 | geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps); |
427 | 0 | } |
428 | | |
429 | | void geod_gendirectline(struct geod_geodesicline* l, |
430 | | const struct geod_geodesic* g, |
431 | | double lat1, double lon1, double azi1, |
432 | | unsigned flags, double s12_a12, |
433 | 0 | unsigned caps) { |
434 | 0 | geod_lineinit(l, g, lat1, lon1, azi1, caps); |
435 | 0 | geod_gensetdistance(l, flags, s12_a12); |
436 | 0 | } |
437 | | |
438 | | void geod_directline(struct geod_geodesicline* l, |
439 | | const struct geod_geodesic* g, |
440 | | double lat1, double lon1, double azi1, |
441 | 0 | double s12, unsigned caps) { |
442 | 0 | geod_gendirectline(l, g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, caps); |
443 | 0 | } |
444 | | |
445 | | double 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 | 0 | double* pS12) { |
451 | 0 | double lat2 = 0, lon2 = 0, azi2 = 0, s12 = 0, |
452 | 0 | m12 = 0, M12 = 0, M21 = 0, S12 = 0; |
453 | | /* Avoid warning about uninitialized B12. */ |
454 | 0 | double sig12, ssig12, csig12, B12 = 0, AB1 = 0; |
455 | 0 | double omg12, lam12, lon12; |
456 | 0 | double ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2, dn2; |
457 | 0 | unsigned outmask = |
458 | 0 | (plat2 ? GEOD_LATITUDE : GEOD_NONE) | |
459 | 0 | (plon2 ? GEOD_LONGITUDE : GEOD_NONE) | |
460 | 0 | (pazi2 ? GEOD_AZIMUTH : GEOD_NONE) | |
461 | 0 | (ps12 ? GEOD_DISTANCE : GEOD_NONE) | |
462 | 0 | (pm12 ? GEOD_REDUCEDLENGTH : GEOD_NONE) | |
463 | 0 | (pM12 || pM21 ? GEOD_GEODESICSCALE : GEOD_NONE) | |
464 | 0 | (pS12 ? GEOD_AREA : GEOD_NONE); |
465 | |
|
466 | 0 | outmask &= l->caps & OUT_ALL; |
467 | 0 | if (!( (flags & GEOD_ARCMODE || |
468 | 0 | ((unsigned)l->caps & |
469 | 0 | ((unsigned)GEOD_DISTANCE_IN & (unsigned)OUT_ALL))) )) |
470 | | /* Impossible distance calculation requested */ |
471 | 0 | return NaN; |
472 | | |
473 | 0 | if (flags & GEOD_ARCMODE) { |
474 | | /* Interpret s12_a12 as spherical arc length */ |
475 | 0 | sig12 = s12_a12 * degree; |
476 | 0 | sincosdx(s12_a12, &ssig12, &csig12); |
477 | 0 | } else { |
478 | | /* Interpret s12_a12 as distance */ |
479 | 0 | double |
480 | 0 | tau12 = s12_a12 / (l->b * (1 + l->A1m1)), |
481 | 0 | s = sin(tau12), |
482 | 0 | c = cos(tau12); |
483 | | /* tau2 = tau1 + tau12 */ |
484 | 0 | B12 = - SinCosSeries(TRUE, |
485 | 0 | l->stau1 * c + l->ctau1 * s, |
486 | 0 | l->ctau1 * c - l->stau1 * s, |
487 | 0 | l->C1pa, nC1p); |
488 | 0 | sig12 = tau12 - (B12 - l->B11); |
489 | 0 | ssig12 = sin(sig12); csig12 = cos(sig12); |
490 | 0 | 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 | 0 | double serr; |
513 | 0 | ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12; |
514 | 0 | csig2 = l->csig1 * csig12 - l->ssig1 * ssig12; |
515 | 0 | B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1); |
516 | 0 | serr = (1 + l->A1m1) * (sig12 + (B12 - l->B11)) - s12_a12 / l->b; |
517 | 0 | sig12 = sig12 - serr / sqrt(1 + l->k2 * sq(ssig2)); |
518 | 0 | ssig12 = sin(sig12); csig12 = cos(sig12); |
519 | | /* Update B12 below */ |
520 | 0 | } |
521 | 0 | } |
522 | | |
523 | | /* sig2 = sig1 + sig12 */ |
524 | 0 | ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12; |
525 | 0 | csig2 = l->csig1 * csig12 - l->ssig1 * ssig12; |
526 | 0 | dn2 = sqrt(1 + l->k2 * sq(ssig2)); |
527 | 0 | if (outmask & (GEOD_DISTANCE | GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) { |
528 | 0 | if (flags & GEOD_ARCMODE || fabs(l->f) > 0.01) |
529 | 0 | B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1); |
530 | 0 | AB1 = (1 + l->A1m1) * (B12 - l->B11); |
531 | 0 | } |
532 | | /* sin(bet2) = cos(alp0) * sin(sig2) */ |
533 | 0 | sbet2 = l->calp0 * ssig2; |
534 | | /* Alt: cbet2 = hypot(csig2, salp0 * ssig2); */ |
535 | 0 | cbet2 = hypot(l->salp0, l->calp0 * csig2); |
536 | 0 | if (cbet2 == 0) |
537 | | /* I.e., salp0 = 0, csig2 = 0. Break the degeneracy in this case */ |
538 | 0 | cbet2 = csig2 = tiny; |
539 | | /* tan(alp0) = cos(sig2)*tan(alp2) */ |
540 | 0 | salp2 = l->salp0; calp2 = l->calp0 * csig2; /* No need to normalize */ |
541 | |
|
542 | 0 | if (outmask & GEOD_DISTANCE) |
543 | 0 | s12 = (flags & GEOD_ARCMODE) ? |
544 | 0 | l->b * ((1 + l->A1m1) * sig12 + AB1) : |
545 | 0 | s12_a12; |
546 | |
|
547 | 0 | if (outmask & GEOD_LONGITUDE) { |
548 | 0 | double E = copysign(1, l->salp0); /* east or west going? */ |
549 | | /* tan(omg2) = sin(alp0) * tan(sig2) */ |
550 | 0 | somg2 = l->salp0 * ssig2; comg2 = csig2; /* No need to normalize */ |
551 | | /* omg12 = omg2 - omg1 */ |
552 | 0 | omg12 = (flags & GEOD_LONG_UNROLL) |
553 | 0 | ? E * (sig12 |
554 | 0 | - (atan2( ssig2, csig2) - atan2( l->ssig1, l->csig1)) |
555 | 0 | + (atan2(E * somg2, comg2) - atan2(E * l->somg1, l->comg1))) |
556 | 0 | : atan2(somg2 * l->comg1 - comg2 * l->somg1, |
557 | 0 | comg2 * l->comg1 + somg2 * l->somg1); |
558 | 0 | lam12 = omg12 + l->A3c * |
559 | 0 | ( sig12 + (SinCosSeries(TRUE, ssig2, csig2, l->C3a, nC3-1) |
560 | 0 | - l->B31)); |
561 | 0 | lon12 = lam12 / degree; |
562 | 0 | lon2 = (flags & GEOD_LONG_UNROLL) ? l->lon1 + lon12 : |
563 | 0 | AngNormalize(AngNormalize(l->lon1) + AngNormalize(lon12)); |
564 | 0 | } |
565 | |
|
566 | 0 | if (outmask & GEOD_LATITUDE) |
567 | 0 | lat2 = atan2dx(sbet2, l->f1 * cbet2); |
568 | |
|
569 | 0 | if (outmask & GEOD_AZIMUTH) |
570 | 0 | azi2 = atan2dx(salp2, calp2); |
571 | |
|
572 | 0 | if (outmask & (GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) { |
573 | 0 | double |
574 | 0 | B22 = SinCosSeries(TRUE, ssig2, csig2, l->C2a, nC2), |
575 | 0 | AB2 = (1 + l->A2m1) * (B22 - l->B21), |
576 | 0 | J12 = (l->A1m1 - l->A2m1) * sig12 + (AB1 - AB2); |
577 | 0 | 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 | 0 | m12 = l->b * ((dn2 * (l->csig1 * ssig2) - l->dn1 * (l->ssig1 * csig2)) |
581 | 0 | - l->csig1 * csig2 * J12); |
582 | 0 | if (outmask & GEOD_GEODESICSCALE) { |
583 | 0 | double t = l->k2 * (ssig2 - l->ssig1) * (ssig2 + l->ssig1) / |
584 | 0 | (l->dn1 + dn2); |
585 | 0 | M12 = csig12 + (t * ssig2 - csig2 * J12) * l->ssig1 / l->dn1; |
586 | 0 | M21 = csig12 - (t * l->ssig1 - l->csig1 * J12) * ssig2 / dn2; |
587 | 0 | } |
588 | 0 | } |
589 | |
|
590 | 0 | if (outmask & GEOD_AREA) { |
591 | 0 | double |
592 | 0 | B42 = SinCosSeries(FALSE, ssig2, csig2, l->C4a, nC4); |
593 | 0 | double salp12, calp12; |
594 | 0 | if (l->calp0 == 0 || l->salp0 == 0) { |
595 | | /* alp12 = alp2 - alp1, used in atan2 so no need to normalize */ |
596 | 0 | salp12 = salp2 * l->calp1 - calp2 * l->salp1; |
597 | 0 | calp12 = calp2 * l->calp1 + salp2 * l->salp1; |
598 | 0 | } 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 | 0 | salp12 = l->calp0 * l->salp0 * |
608 | 0 | (csig12 <= 0 ? l->csig1 * (1 - csig12) + ssig12 * l->ssig1 : |
609 | 0 | ssig12 * (l->csig1 * ssig12 / (1 + csig12) + l->ssig1)); |
610 | 0 | calp12 = sq(l->salp0) + sq(l->calp0) * l->csig1 * csig2; |
611 | 0 | } |
612 | 0 | S12 = l->c2 * atan2(salp12, calp12) + l->A4 * (B42 - l->B41); |
613 | 0 | } |
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 | 0 | if ((outmask & GEOD_LATITUDE) && plat2) |
624 | 0 | *plat2 = lat2; |
625 | 0 | if ((outmask & GEOD_LONGITUDE) && plon2) |
626 | 0 | *plon2 = lon2; |
627 | 0 | if ((outmask & GEOD_AZIMUTH) && pazi2) |
628 | 0 | *pazi2 = azi2; |
629 | 0 | if ((outmask & GEOD_DISTANCE) && ps12) |
630 | 0 | *ps12 = s12; |
631 | 0 | if ((outmask & GEOD_REDUCEDLENGTH) && pm12) |
632 | 0 | *pm12 = m12; |
633 | 0 | if (outmask & GEOD_GEODESICSCALE) { |
634 | 0 | if (pM12) *pM12 = M12; |
635 | 0 | if (pM21) *pM21 = M21; |
636 | 0 | } |
637 | 0 | if ((outmask & GEOD_AREA) && pS12) |
638 | 0 | *pS12 = S12; |
639 | |
|
640 | 0 | return (flags & GEOD_ARCMODE) ? s12_a12 : sig12 / degree; |
641 | 0 | } |
642 | | |
643 | 0 | void geod_setdistance(struct geod_geodesicline* l, double s13) { |
644 | 0 | l->s13 = s13; |
645 | 0 | l->a13 = geod_genposition(l, GEOD_NOFLAGS, l->s13, NULLPTR, NULLPTR, NULLPTR, |
646 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR); |
647 | 0 | } |
648 | | |
649 | 0 | static void geod_setarc(struct geod_geodesicline* l, double a13) { |
650 | 0 | l->a13 = a13; l->s13 = NaN; |
651 | 0 | geod_genposition(l, GEOD_ARCMODE, l->a13, NULLPTR, NULLPTR, NULLPTR, &l->s13, |
652 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR); |
653 | 0 | } |
654 | | |
655 | | void geod_gensetdistance(struct geod_geodesicline* l, |
656 | 0 | unsigned flags, double s13_a13) { |
657 | 0 | (flags & GEOD_ARCMODE) ? |
658 | 0 | geod_setarc(l, s13_a13) : |
659 | 0 | geod_setdistance(l, s13_a13); |
660 | 0 | } |
661 | | |
662 | | void geod_position(const struct geod_geodesicline* l, double s12, |
663 | 0 | double* plat2, double* plon2, double* pazi2) { |
664 | 0 | geod_genposition(l, FALSE, s12, plat2, plon2, pazi2, |
665 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR); |
666 | 0 | } |
667 | | |
668 | | double 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 | 0 | double* pS12) { |
674 | 0 | struct geod_geodesicline l; |
675 | 0 | unsigned outmask = |
676 | 0 | (plat2 ? GEOD_LATITUDE : GEOD_NONE) | |
677 | 0 | (plon2 ? GEOD_LONGITUDE : GEOD_NONE) | |
678 | 0 | (pazi2 ? GEOD_AZIMUTH : GEOD_NONE) | |
679 | 0 | (ps12 ? GEOD_DISTANCE : GEOD_NONE) | |
680 | 0 | (pm12 ? GEOD_REDUCEDLENGTH : GEOD_NONE) | |
681 | 0 | (pM12 || pM21 ? GEOD_GEODESICSCALE : GEOD_NONE) | |
682 | 0 | (pS12 ? GEOD_AREA : GEOD_NONE); |
683 | |
|
684 | 0 | geod_lineinit(&l, g, lat1, lon1, azi1, |
685 | | /* Automatically supply GEOD_DISTANCE_IN if necessary */ |
686 | 0 | outmask | |
687 | 0 | ((flags & GEOD_ARCMODE) ? GEOD_NONE : GEOD_DISTANCE_IN)); |
688 | 0 | return geod_genposition(&l, flags, s12_a12, |
689 | 0 | plat2, plon2, pazi2, ps12, pm12, pM12, pM21, pS12); |
690 | 0 | } |
691 | | |
692 | | void geod_direct(const struct geod_geodesic* g, |
693 | | double lat1, double lon1, double azi1, |
694 | | double s12, |
695 | 0 | double* plat2, double* plon2, double* pazi2) { |
696 | 0 | geod_gendirect(g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, plat2, plon2, pazi2, |
697 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR); |
698 | 0 | } |
699 | | |
700 | | static 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 | 15.2k | double* pS12) { |
708 | 15.2k | double s12 = 0, m12 = 0, M12 = 0, M21 = 0, S12 = 0; |
709 | 15.2k | double lon12, lon12s; |
710 | 15.2k | int latsign, lonsign, swapp; |
711 | 15.2k | double sbet1, cbet1, sbet2, cbet2, s12x = 0, m12x = 0; |
712 | 15.2k | double dn1, dn2, lam12, slam12, clam12; |
713 | 15.2k | double a12 = 0, sig12, calp1 = 0, salp1 = 0, calp2 = 0, salp2 = 0; |
714 | 15.2k | double Ca[nC]; |
715 | 15.2k | boolx meridian; |
716 | | /* somg12 == 2 marks that it needs to be calculated */ |
717 | 15.2k | double omg12 = 0, somg12 = 2, comg12 = 0; |
718 | | |
719 | 15.2k | unsigned outmask = |
720 | 15.2k | (ps12 ? GEOD_DISTANCE : GEOD_NONE) | |
721 | 15.2k | (pm12 ? GEOD_REDUCEDLENGTH : GEOD_NONE) | |
722 | 15.2k | (pM12 || pM21 ? GEOD_GEODESICSCALE : GEOD_NONE) | |
723 | 15.2k | (pS12 ? GEOD_AREA : GEOD_NONE); |
724 | | |
725 | 15.2k | 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 | 15.2k | lon12 = AngDiff(lon1, lon2, &lon12s); |
730 | | /* Make longitude difference positive. */ |
731 | 15.2k | lonsign = signbit(lon12) ? -1 : 1; |
732 | 15.2k | lon12 *= lonsign; lon12s *= lonsign; |
733 | 15.2k | lam12 = lon12 * degree; |
734 | | /* Calculate sincos of lon12 + error (this applies AngRound internally). */ |
735 | 15.2k | sincosde(lon12, lon12s, &slam12, &clam12); |
736 | 15.2k | lon12s = (hd - lon12) - lon12s; /* the supplementary longitude difference */ |
737 | | |
738 | | /* If really close to the equator, treat as on equator. */ |
739 | 15.2k | lat1 = AngRound(LatFix(lat1)); |
740 | 15.2k | 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 | 15.2k | swapp = fabs(lat1) < fabs(lat2) || lat2 != lat2 ? -1 : 1; |
744 | 15.2k | if (swapp < 0) { |
745 | 15.2k | lonsign *= -1; |
746 | 15.2k | swapx(&lat1, &lat2); |
747 | 15.2k | } |
748 | | /* Make lat1 <= -0 */ |
749 | 15.2k | latsign = signbit(lat1) ? 1 : -1; |
750 | 15.2k | lat1 *= latsign; |
751 | 15.2k | 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 | 15.2k | sincosdx(lat1, &sbet1, &cbet1); sbet1 *= g->f1; |
765 | | /* Ensure cbet1 = +epsilon at poles */ |
766 | 15.2k | norm2(&sbet1, &cbet1); cbet1 = fmax(tiny, cbet1); |
767 | | |
768 | 15.2k | sincosdx(lat2, &sbet2, &cbet2); sbet2 *= g->f1; |
769 | | /* Ensure cbet2 = +epsilon at poles */ |
770 | 15.2k | 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 | 15.2k | if (cbet1 < -sbet1) { |
781 | 11.7k | if (cbet2 == cbet1) |
782 | 0 | sbet2 = copysign(sbet1, sbet2); |
783 | 11.7k | } else { |
784 | 3.46k | if (fabs(sbet2) == -sbet1) |
785 | 0 | cbet2 = cbet1; |
786 | 3.46k | } |
787 | | |
788 | 15.2k | dn1 = sqrt(1 + g->ep2 * sq(sbet1)); |
789 | 15.2k | dn2 = sqrt(1 + g->ep2 * sq(sbet2)); |
790 | | |
791 | 15.2k | meridian = lat1 == -qd || slam12 == 0; |
792 | | |
793 | 15.2k | if (meridian) { |
794 | | |
795 | | /* Endpoints are on a single full meridian, so the geodesic might lie on |
796 | | * a meridian. */ |
797 | | |
798 | 46 | double ssig1, csig1, ssig2, csig2; |
799 | 46 | calp1 = clam12; salp1 = slam12; /* Head to the target longitude */ |
800 | 46 | calp2 = 1; salp2 = 0; /* At the target we're heading north */ |
801 | | |
802 | | /* tan(bet) = tan(sig) * cos(alp) */ |
803 | 46 | ssig1 = sbet1; csig1 = calp1 * cbet1; |
804 | 46 | ssig2 = sbet2; csig2 = calp2 * cbet2; |
805 | | |
806 | | /* sig12 = sig2 - sig1 */ |
807 | 46 | sig12 = atan2(fmax(0.0, csig1 * ssig2 - ssig1 * csig2) + 0, |
808 | 46 | csig1 * csig2 + ssig1 * ssig2); |
809 | 46 | Lengths(g, g->n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, |
810 | 46 | cbet1, cbet2, &s12x, &m12x, NULLPTR, |
811 | 46 | (outmask & GEOD_GEODESICSCALE) ? &M12 : NULLPTR, |
812 | 46 | (outmask & GEOD_GEODESICSCALE) ? &M21 : NULLPTR, |
813 | 46 | 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 | 46 | if (sig12 < tol2 || m12x >= 0) { |
820 | | /* Need at least 2, to handle 90 0 90 180 */ |
821 | 46 | if (sig12 < 3 * tiny || |
822 | | /* Prevent negative s12 or m12 for short lines */ |
823 | 46 | (sig12 < tol0 && (s12x < 0 || m12x < 0))) |
824 | 0 | sig12 = m12x = s12x = 0; |
825 | 46 | m12x *= g->b; |
826 | 46 | s12x *= g->b; |
827 | 46 | a12 = sig12 / degree; |
828 | 46 | } else |
829 | | /* m12 < 0, i.e., prolate and too close to anti-podal */ |
830 | 0 | meridian = FALSE; |
831 | 46 | } |
832 | | |
833 | 15.2k | if (!meridian && |
834 | 15.1k | sbet1 == 0 && /* and sbet2 == 0 */ |
835 | | /* Mimic the way Lambda12 works with calp1 = 0 */ |
836 | 0 | (g->f <= 0 || lon12s >= g->f * hd)) { |
837 | | |
838 | | /* Geodesic runs along equator */ |
839 | 0 | calp1 = calp2 = 0; salp1 = salp2 = 1; |
840 | 0 | s12x = g->a * lam12; |
841 | 0 | sig12 = omg12 = lam12 / g->f1; |
842 | 0 | m12x = g->b * sin(sig12); |
843 | 0 | if (outmask & GEOD_GEODESICSCALE) |
844 | 0 | M12 = M21 = cos(sig12); |
845 | 0 | a12 = lon12 / g->f1; |
846 | |
|
847 | 15.2k | } 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 | 15.1k | double dnm = 0; |
854 | 15.1k | sig12 = InverseStart(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, |
855 | 15.1k | lam12, slam12, clam12, |
856 | 15.1k | &salp1, &calp1, &salp2, &calp2, &dnm, |
857 | 15.1k | Ca); |
858 | | |
859 | 15.1k | if (sig12 >= 0) { |
860 | | /* Short lines (InverseStart sets salp2, calp2, dnm) */ |
861 | 0 | s12x = sig12 * g->b * dnm; |
862 | 0 | m12x = sq(dnm) * g->b * sin(sig12 / dnm); |
863 | 0 | if (outmask & GEOD_GEODESICSCALE) |
864 | 0 | M12 = M21 = cos(sig12 / dnm); |
865 | 0 | a12 = sig12 / degree; |
866 | 0 | omg12 = lam12 / (g->f1 * dnm); |
867 | 15.1k | } 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 | 15.1k | double ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, domg12 = 0; |
881 | 15.1k | unsigned numit = 0; |
882 | | /* Bracketing range */ |
883 | 15.1k | double salp1a = tiny, calp1a = 1, salp1b = tiny, calp1b = -1; |
884 | 15.1k | boolx tripn = FALSE; |
885 | 15.1k | boolx tripb = FALSE; |
886 | 49.9k | 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 | 49.9k | double dv = 0, |
890 | 49.9k | v = Lambda12(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1, |
891 | 49.9k | slam12, clam12, |
892 | 49.9k | &salp2, &calp2, &sig12, &ssig1, &csig1, &ssig2, &csig2, |
893 | 49.9k | &eps, &domg12, numit < maxit1, &dv, Ca); |
894 | 49.9k | if (tripb || |
895 | | /* Reversed test to allow escape with NaNs */ |
896 | 49.9k | !(fabs(v) >= (tripn ? 8 : 1) * tol0) || |
897 | | /* Enough bisections to get accurate result */ |
898 | 34.7k | numit == maxit2) |
899 | 15.1k | break; |
900 | | /* Update bracketing values */ |
901 | 34.7k | if (v > 0 && (numit > maxit1 || calp1/salp1 > calp1b/salp1b)) |
902 | 15.0k | { salp1b = salp1; calp1b = calp1; } |
903 | 19.7k | else if (v < 0 && (numit > maxit1 || calp1/salp1 < calp1a/salp1a)) |
904 | 19.7k | { salp1a = salp1; calp1a = calp1; } |
905 | 34.7k | if (numit < maxit1 && dv > 0) { |
906 | 34.7k | double |
907 | 34.7k | dalp1 = -v/dv; |
908 | 34.7k | if (fabs(dalp1) < pi) { |
909 | 34.7k | double |
910 | 34.7k | sdalp1 = sin(dalp1), cdalp1 = cos(dalp1), |
911 | 34.7k | nsalp1 = salp1 * cdalp1 + calp1 * sdalp1; |
912 | 34.7k | if (nsalp1 > 0) { |
913 | 34.7k | calp1 = calp1 * cdalp1 - salp1 * sdalp1; |
914 | 34.7k | salp1 = nsalp1; |
915 | 34.7k | 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 | 34.7k | tripn = fabs(v) <= 16 * tol0; |
920 | 34.7k | continue; |
921 | 34.7k | } |
922 | 34.7k | } |
923 | 34.7k | } |
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 | 0 | salp1 = (salp1a + salp1b)/2; |
933 | 0 | calp1 = (calp1a + calp1b)/2; |
934 | 0 | norm2(&salp1, &calp1); |
935 | 0 | tripn = FALSE; |
936 | 0 | tripb = (fabs(salp1a - salp1) + (calp1a - calp1) < tolb || |
937 | 0 | fabs(salp1 - salp1b) + (calp1 - calp1b) < tolb); |
938 | 0 | } |
939 | 15.1k | Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, |
940 | 15.1k | cbet1, cbet2, &s12x, &m12x, NULLPTR, |
941 | 15.1k | (outmask & GEOD_GEODESICSCALE) ? &M12 : NULLPTR, |
942 | 15.1k | (outmask & GEOD_GEODESICSCALE) ? &M21 : NULLPTR, Ca); |
943 | 15.1k | m12x *= g->b; |
944 | 15.1k | s12x *= g->b; |
945 | 15.1k | a12 = sig12 / degree; |
946 | 15.1k | if (outmask & GEOD_AREA) { |
947 | | /* omg12 = lam12 - domg12 */ |
948 | 0 | double sdomg12 = sin(domg12), cdomg12 = cos(domg12); |
949 | 0 | somg12 = slam12 * cdomg12 - clam12 * sdomg12; |
950 | 0 | comg12 = clam12 * cdomg12 + slam12 * sdomg12; |
951 | 0 | } |
952 | 15.1k | } |
953 | 15.1k | } |
954 | | |
955 | 15.2k | if (outmask & GEOD_DISTANCE) |
956 | 12.3k | s12 = 0 + s12x; /* Convert -0 to 0 */ |
957 | | |
958 | 15.2k | if (outmask & GEOD_REDUCEDLENGTH) |
959 | 2.85k | m12 = 0 + m12x; /* Convert -0 to 0 */ |
960 | | |
961 | 15.2k | if (outmask & GEOD_AREA) { |
962 | 0 | double |
963 | | /* From Lambda12: sin(alp1) * cos(bet1) = sin(alp0) */ |
964 | 0 | salp0 = salp1 * cbet1, |
965 | 0 | calp0 = hypot(calp1, salp1 * sbet1); /* calp0 > 0 */ |
966 | 0 | double alp12; |
967 | 0 | if (calp0 != 0 && salp0 != 0) { |
968 | 0 | double |
969 | | /* From Lambda12: tan(bet) = tan(sig) * cos(alp) */ |
970 | 0 | ssig1 = sbet1, csig1 = calp1 * cbet1, |
971 | 0 | ssig2 = sbet2, csig2 = calp2 * cbet2, |
972 | 0 | k2 = sq(calp0) * g->ep2, |
973 | 0 | eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2), |
974 | | /* Multiplier = a^2 * e^2 * cos(alpha0) * sin(alpha0). */ |
975 | 0 | A4 = sq(g->a) * calp0 * salp0 * g->e2; |
976 | 0 | double B41, B42; |
977 | 0 | norm2(&ssig1, &csig1); |
978 | 0 | norm2(&ssig2, &csig2); |
979 | 0 | C4f(g, eps, Ca); |
980 | 0 | B41 = SinCosSeries(FALSE, ssig1, csig1, Ca, nC4); |
981 | 0 | B42 = SinCosSeries(FALSE, ssig2, csig2, Ca, nC4); |
982 | 0 | S12 = A4 * (B42 - B41); |
983 | 0 | } else |
984 | | /* Avoid problems with indeterminate sig1, sig2 on equator */ |
985 | 0 | S12 = 0; |
986 | |
|
987 | 0 | if (!meridian && somg12 == 2) { |
988 | 0 | somg12 = sin(omg12); comg12 = cos(omg12); |
989 | 0 | } |
990 | |
|
991 | 0 | if (!meridian && |
992 | | /* omg12 < 3/4 * pi */ |
993 | 0 | comg12 > -0.7071 && /* Long difference not too big */ |
994 | 0 | 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 | 0 | double |
999 | 0 | domg12 = 1 + comg12, dbet1 = 1 + cbet1, dbet2 = 1 + cbet2; |
1000 | 0 | alp12 = 2 * atan2( somg12 * ( sbet1 * dbet2 + sbet2 * dbet1 ), |
1001 | 0 | domg12 * ( sbet1 * sbet2 + dbet1 * dbet2 ) ); |
1002 | 0 | } else { |
1003 | | /* alp12 = alp2 - alp1, used in atan2 so no need to normalize */ |
1004 | 0 | double |
1005 | 0 | salp12 = salp2 * calp1 - calp2 * salp1, |
1006 | 0 | 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 | 0 | if (salp12 == 0 && calp12 < 0) { |
1012 | 0 | salp12 = tiny * calp1; |
1013 | 0 | calp12 = -1; |
1014 | 0 | } |
1015 | 0 | alp12 = atan2(salp12, calp12); |
1016 | 0 | } |
1017 | 0 | S12 += g->c2 * alp12; |
1018 | 0 | S12 *= swapp * lonsign * latsign; |
1019 | | /* Convert -0 to 0 */ |
1020 | 0 | S12 += 0; |
1021 | 0 | } |
1022 | | |
1023 | | /* Convert calp, salp to azimuth accounting for lonsign, swapp, latsign. */ |
1024 | 15.2k | if (swapp < 0) { |
1025 | 15.2k | swapx(&salp1, &salp2); |
1026 | 15.2k | swapx(&calp1, &calp2); |
1027 | 15.2k | if (outmask & GEOD_GEODESICSCALE) |
1028 | 2.85k | swapx(&M12, &M21); |
1029 | 15.2k | } |
1030 | | |
1031 | 15.2k | salp1 *= swapp * lonsign; calp1 *= swapp * latsign; |
1032 | 15.2k | salp2 *= swapp * lonsign; calp2 *= swapp * latsign; |
1033 | | |
1034 | 15.2k | if (psalp1) *psalp1 = salp1; |
1035 | 15.2k | if (pcalp1) *pcalp1 = calp1; |
1036 | 15.2k | if (psalp2) *psalp2 = salp2; |
1037 | 15.2k | if (pcalp2) *pcalp2 = calp2; |
1038 | | |
1039 | 15.2k | if (outmask & GEOD_DISTANCE) |
1040 | 12.3k | *ps12 = s12; |
1041 | 15.2k | if (outmask & GEOD_REDUCEDLENGTH) |
1042 | 2.85k | *pm12 = m12; |
1043 | 15.2k | if (outmask & GEOD_GEODESICSCALE) { |
1044 | 2.85k | if (pM12) *pM12 = M12; |
1045 | 2.85k | if (pM21) *pM21 = M21; |
1046 | 2.85k | } |
1047 | 15.2k | if (outmask & GEOD_AREA) |
1048 | 0 | *pS12 = S12; |
1049 | | |
1050 | | /* Returned value in [0, 180] */ |
1051 | 15.2k | return a12; |
1052 | 15.2k | } |
1053 | | |
1054 | | double 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 | 15.2k | double* pS12) { |
1059 | 15.2k | double salp1, calp1, salp2, calp2, |
1060 | 15.2k | a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, ps12, |
1061 | 15.2k | &salp1, &calp1, &salp2, &calp2, |
1062 | 15.2k | pm12, pM12, pM21, pS12); |
1063 | 15.2k | if (pazi1) *pazi1 = atan2dx(salp1, calp1); |
1064 | 15.2k | if (pazi2) *pazi2 = atan2dx(salp2, calp2); |
1065 | 15.2k | return a12; |
1066 | 15.2k | } |
1067 | | |
1068 | | void geod_inverseline(struct geod_geodesicline* l, |
1069 | | const struct geod_geodesic* g, |
1070 | | double lat1, double lon1, double lat2, double lon2, |
1071 | 0 | unsigned caps) { |
1072 | 0 | double salp1, calp1, |
1073 | 0 | a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, NULLPTR, |
1074 | 0 | &salp1, &calp1, NULLPTR, NULLPTR, |
1075 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR), |
1076 | 0 | azi1 = atan2dx(salp1, calp1); |
1077 | 0 | caps = caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE; |
1078 | | /* Ensure that a12 can be converted to a distance */ |
1079 | 0 | if (caps & ((unsigned)OUT_ALL & (unsigned)GEOD_DISTANCE_IN)) |
1080 | 0 | caps |= GEOD_DISTANCE; |
1081 | 0 | geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps); |
1082 | 0 | geod_setarc(l, a12); |
1083 | 0 | } |
1084 | | |
1085 | | void geod_inverse(const struct geod_geodesic* g, |
1086 | | double lat1, double lon1, double lat2, double lon2, |
1087 | 12.3k | double* ps12, double* pazi1, double* pazi2) { |
1088 | 12.3k | geod_geninverse(g, lat1, lon1, lat2, lon2, ps12, pazi1, pazi2, |
1089 | 12.3k | NULLPTR, NULLPTR, NULLPTR, NULLPTR); |
1090 | 12.3k | } |
1091 | | |
1092 | | double SinCosSeries(boolx sinp, double sinx, double cosx, |
1093 | 260k | 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 | 260k | double ar, y0, y1; |
1100 | 260k | c += (n + sinp); /* Point to one beyond last element */ |
1101 | 260k | ar = 2 * (cosx - sinx) * (cosx + sinx); /* 2 * cos(2 * x) */ |
1102 | 260k | y0 = (n & 1) ? *--c : 0; y1 = 0; /* accumulators for sum */ |
1103 | | /* Now n is even */ |
1104 | 260k | n /= 2; |
1105 | 942k | while (n--) { |
1106 | | /* Unroll loop x 2, so accumulators return to their original role */ |
1107 | 681k | y1 = ar * y0 - y1 + *--c; |
1108 | 681k | y0 = ar * y1 - y0 + *--c; |
1109 | 681k | } |
1110 | 260k | return sinp |
1111 | 260k | ? 2 * sinx * cosx * y0 /* sin(2 * x) * y0 */ |
1112 | 260k | : cosx * (y0 - y1); /* cos(x) * (y0 - y1) */ |
1113 | 260k | } |
1114 | | |
1115 | | void 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 | 65.1k | double Ca[]) { |
1124 | 65.1k | double m0 = 0, J12 = 0, A1 = 0, A2 = 0; |
1125 | 65.1k | 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 | 65.1k | boolx redlp = pm12b || pm0 || pM12 || pM21; |
1130 | 65.1k | if (ps12b || redlp) { |
1131 | 65.1k | A1 = A1m1f(eps); |
1132 | 65.1k | C1f(eps, Ca); |
1133 | 65.1k | if (redlp) { |
1134 | 65.1k | A2 = A2m1f(eps); |
1135 | 65.1k | C2f(eps, Cb); |
1136 | 65.1k | m0 = A1 - A2; |
1137 | 65.1k | A2 = 1 + A2; |
1138 | 65.1k | } |
1139 | 65.1k | A1 = 1 + A1; |
1140 | 65.1k | } |
1141 | 65.1k | if (ps12b) { |
1142 | 15.2k | double B1 = SinCosSeries(TRUE, ssig2, csig2, Ca, nC1) - |
1143 | 15.2k | SinCosSeries(TRUE, ssig1, csig1, Ca, nC1); |
1144 | | /* Missing a factor of b */ |
1145 | 15.2k | *ps12b = A1 * (sig12 + B1); |
1146 | 15.2k | if (redlp) { |
1147 | 15.2k | double B2 = SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) - |
1148 | 15.2k | SinCosSeries(TRUE, ssig1, csig1, Cb, nC2); |
1149 | 15.2k | J12 = m0 * sig12 + (A1 * B1 - A2 * B2); |
1150 | 15.2k | } |
1151 | 49.9k | } else if (redlp) { |
1152 | | /* Assume here that nC1 >= nC2 */ |
1153 | 49.9k | int l; |
1154 | 349k | for (l = 1; l <= nC2; ++l) |
1155 | 299k | Cb[l] = A1 * Ca[l] - A2 * Cb[l]; |
1156 | 49.9k | J12 = m0 * sig12 + (SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) - |
1157 | 49.9k | SinCosSeries(TRUE, ssig1, csig1, Cb, nC2)); |
1158 | 49.9k | } |
1159 | 65.1k | if (pm0) *pm0 = m0; |
1160 | 65.1k | 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 | 65.1k | *pm12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) - |
1165 | 65.1k | csig1 * csig2 * J12; |
1166 | 65.1k | if (pM12 || pM21) { |
1167 | 2.85k | double csig12 = csig1 * csig2 + ssig1 * ssig2; |
1168 | 2.85k | double t = g->ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2); |
1169 | 2.85k | if (pM12) |
1170 | 2.85k | *pM12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1; |
1171 | 2.85k | if (pM21) |
1172 | 2.85k | *pM21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2; |
1173 | 2.85k | } |
1174 | 65.1k | } |
1175 | | |
1176 | 0 | double 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 | 0 | double k; |
1180 | 0 | double |
1181 | 0 | p = sq(x), |
1182 | 0 | q = sq(y), |
1183 | 0 | r = (p + q - 1) / 6; |
1184 | 0 | if ( !(q == 0 && r <= 0) ) { |
1185 | 0 | 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 | 0 | S = p * q / 4, /* S = r^3 * s */ |
1189 | 0 | r2 = sq(r), |
1190 | 0 | 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 | 0 | disc = S * (S + 2 * r3); |
1194 | 0 | double u = r; |
1195 | 0 | double v, uv, w; |
1196 | 0 | if (disc >= 0) { |
1197 | 0 | 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 | 0 | T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc); /* T3 = (r * t)^3 */ |
1202 | | /* N.B. cbrt always returns the double root. cbrt(-8) = -2. */ |
1203 | 0 | T = cbrt(T3); /* T = r * t */ |
1204 | | /* T can be zero; but then r2 / T -> 0. */ |
1205 | 0 | u += T + (T != 0 ? r2 / T : 0); |
1206 | 0 | } else { |
1207 | | /* T is complex, but the way u is defined the result is double. */ |
1208 | 0 | 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 | 0 | u += 2 * r * cos(ang / 3); |
1212 | 0 | } |
1213 | 0 | v = sqrt(sq(u) + q); /* guaranteed positive */ |
1214 | | /* Avoid loss of accuracy when u < 0. */ |
1215 | 0 | uv = u < 0 ? q / (v - u) : u + v; /* u+v, guaranteed positive */ |
1216 | 0 | 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 | 0 | k = uv / (sqrt(uv + sq(w)) + w); /* guaranteed positive */ |
1220 | 0 | } 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 | 0 | k = 0; |
1224 | 0 | } |
1225 | 0 | return k; |
1226 | 0 | } |
1227 | | |
1228 | | double 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 | 15.1k | double Ca[]) { |
1239 | 15.1k | 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 | 15.1k | double |
1245 | 15.1k | sig12 = -1, /* Return value */ |
1246 | | /* bet12 = bet2 - bet1 in [0, pi); bet12a = bet2 + bet1 in (-pi, 0] */ |
1247 | 15.1k | sbet12 = sbet2 * cbet1 - cbet2 * sbet1, |
1248 | 15.1k | cbet12 = cbet2 * cbet1 + sbet2 * sbet1; |
1249 | 15.1k | double sbet12a; |
1250 | 15.1k | boolx shortline = cbet12 >= 0 && sbet12 < 0.5 && cbet2 * lam12 < 0.5; |
1251 | 15.1k | double somg12, comg12, ssig12, csig12; |
1252 | 15.1k | sbet12a = sbet2 * cbet1 + cbet2 * sbet1; |
1253 | 15.1k | if (shortline) { |
1254 | 0 | double sbetm2 = sq(sbet1 + sbet2), omg12; |
1255 | | /* sin((bet1+bet2)/2)^2 |
1256 | | * = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) */ |
1257 | 0 | sbetm2 /= sbetm2 + sq(cbet1 + cbet2); |
1258 | 0 | dnm = sqrt(1 + g->ep2 * sbetm2); |
1259 | 0 | omg12 = lam12 / (g->f1 * dnm); |
1260 | 0 | somg12 = sin(omg12); comg12 = cos(omg12); |
1261 | 15.1k | } else { |
1262 | 15.1k | somg12 = slam12; comg12 = clam12; |
1263 | 15.1k | } |
1264 | | |
1265 | 15.1k | salp1 = cbet2 * somg12; |
1266 | 15.1k | calp1 = comg12 >= 0 ? |
1267 | 12.6k | sbet12 + cbet2 * sbet1 * sq(somg12) / (1 + comg12) : |
1268 | 15.1k | sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12); |
1269 | | |
1270 | 15.1k | ssig12 = hypot(salp1, calp1); |
1271 | 15.1k | csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12; |
1272 | | |
1273 | 15.1k | if (shortline && ssig12 < g->etol2) { |
1274 | | /* really short lines */ |
1275 | 0 | salp2 = cbet1 * somg12; |
1276 | 0 | calp2 = sbet12 - cbet1 * sbet2 * |
1277 | 0 | (comg12 >= 0 ? sq(somg12) / (1 + comg12) : 1 - comg12); |
1278 | 0 | norm2(&salp2, &calp2); |
1279 | | /* Set return value */ |
1280 | 0 | sig12 = atan2(ssig12, csig12); |
1281 | 15.1k | } else if (fabs(g->n) > 0.1 || /* No astroid calc if too eccentric */ |
1282 | 15.1k | csig12 >= 0 || |
1283 | 15.1k | ssig12 >= 6 * fabs(g->n) * pi * sq(cbet1)) { |
1284 | | /* Nothing to do, zeroth order spherical approximation is OK */ |
1285 | 15.1k | } 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 | 0 | double x, y, lamscale, betscale; |
1289 | 0 | double lam12x = atan2(-slam12, -clam12); /* lam12 - pi */ |
1290 | 0 | if (g->f >= 0) { /* In fact f == 0 does not get here */ |
1291 | | /* x = dlong, y = dlat */ |
1292 | 0 | { |
1293 | 0 | double |
1294 | 0 | k2 = sq(sbet1) * g->ep2, |
1295 | 0 | eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); |
1296 | 0 | lamscale = g->f * cbet1 * A3f(g, eps) * pi; |
1297 | 0 | } |
1298 | 0 | betscale = lamscale * cbet1; |
1299 | |
|
1300 | 0 | x = lam12x / lamscale; |
1301 | 0 | y = sbet12a / betscale; |
1302 | 0 | } else { /* f < 0 */ |
1303 | | /* x = dlat, y = dlong */ |
1304 | 0 | double |
1305 | 0 | cbet12a = cbet2 * cbet1 - sbet2 * sbet1, |
1306 | 0 | bet12a = atan2(sbet12a, cbet12a); |
1307 | 0 | double m12b, m0; |
1308 | | /* In the case of lon12 = 180, this repeats a calculation made in |
1309 | | * Inverse. */ |
1310 | 0 | Lengths(g, g->n, pi + bet12a, |
1311 | 0 | sbet1, -cbet1, dn1, sbet2, cbet2, dn2, |
1312 | 0 | cbet1, cbet2, NULLPTR, &m12b, &m0, NULLPTR, NULLPTR, Ca); |
1313 | 0 | x = -1 + m12b / (cbet1 * cbet2 * m0 * pi); |
1314 | 0 | betscale = x < -0.01 ? sbet12a / x : |
1315 | 0 | -g->f * sq(cbet1) * pi; |
1316 | 0 | lamscale = betscale / cbet1; |
1317 | 0 | y = lam12x / lamscale; |
1318 | 0 | } |
1319 | |
|
1320 | 0 | if (y > -tol1 && x > -1 - xthresh) { |
1321 | | /* strip near cut */ |
1322 | 0 | if (g->f >= 0) { |
1323 | 0 | salp1 = fmin(1.0, -x); calp1 = - sqrt(1 - sq(salp1)); |
1324 | 0 | } else { |
1325 | 0 | calp1 = fmax(x > -tol1 ? 0.0 : -1.0, x); |
1326 | 0 | salp1 = sqrt(1 - sq(calp1)); |
1327 | 0 | } |
1328 | 0 | } 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 | 0 | double k = Astroid(x, y); |
1364 | 0 | double |
1365 | 0 | omg12a = lamscale * ( g->f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k ); |
1366 | 0 | somg12 = sin(omg12a); comg12 = -cos(omg12a); |
1367 | | /* Update spherical estimate of alp1 using omg12 instead of lam12 */ |
1368 | 0 | salp1 = cbet2 * somg12; |
1369 | 0 | calp1 = sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12); |
1370 | 0 | } |
1371 | 0 | } |
1372 | | /* Sanity check on starting guess. Backwards check allows NaN through. */ |
1373 | 15.1k | if (!(salp1 <= 0)) |
1374 | 15.1k | norm2(&salp1, &calp1); |
1375 | 0 | else { |
1376 | 0 | salp1 = 1; calp1 = 0; |
1377 | 0 | } |
1378 | | |
1379 | 15.1k | *psalp1 = salp1; |
1380 | 15.1k | *pcalp1 = calp1; |
1381 | 15.1k | if (shortline) |
1382 | 0 | *pdnm = dnm; |
1383 | 15.1k | if (sig12 >= 0) { |
1384 | 0 | *psalp2 = salp2; |
1385 | 0 | *pcalp2 = calp2; |
1386 | 0 | } |
1387 | 15.1k | return sig12; |
1388 | 15.1k | } |
1389 | | |
1390 | | double 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 | 49.9k | double Ca[]) { |
1404 | 49.9k | double salp2 = 0, calp2 = 0, sig12 = 0, |
1405 | 49.9k | ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, |
1406 | 49.9k | domg12 = 0, dlam12 = 0; |
1407 | 49.9k | double salp0, calp0; |
1408 | 49.9k | double somg1, comg1, somg2, comg2, somg12, comg12, lam12; |
1409 | 49.9k | double B312, eta, k2; |
1410 | | |
1411 | 49.9k | if (sbet1 == 0 && calp1 == 0) |
1412 | | /* Break degeneracy of equatorial line. This case has already been |
1413 | | * handled. */ |
1414 | 0 | calp1 = -tiny; |
1415 | | |
1416 | | /* sin(alp1) * cos(bet1) = sin(alp0) */ |
1417 | 49.9k | salp0 = salp1 * cbet1; |
1418 | 49.9k | 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 | 49.9k | ssig1 = sbet1; somg1 = salp0 * sbet1; |
1423 | 49.9k | csig1 = comg1 = calp1 * cbet1; |
1424 | 49.9k | 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 | 49.9k | 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 | 49.9k | calp2 = cbet2 != cbet1 || fabs(sbet2) != -sbet1 ? |
1437 | 49.9k | sqrt(sq(calp1 * cbet1) + |
1438 | 49.9k | (cbet1 < -sbet1 ? |
1439 | 36.1k | (cbet2 - cbet1) * (cbet1 + cbet2) : |
1440 | 49.9k | (sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 : |
1441 | 49.9k | fabs(calp1); |
1442 | | /* tan(bet2) = tan(sig2) * cos(alp2) |
1443 | | * tan(omg2) = sin(alp0) * tan(sig2). */ |
1444 | 49.9k | ssig2 = sbet2; somg2 = salp0 * sbet2; |
1445 | 49.9k | csig2 = comg2 = calp2 * cbet2; |
1446 | 49.9k | norm2(&ssig2, &csig2); |
1447 | | /* norm2(&somg2, &comg2); -- don't need to normalize! */ |
1448 | | |
1449 | | /* sig12 = sig2 - sig1, limit to [0, pi] */ |
1450 | 49.9k | sig12 = atan2(fmax(0.0, csig1 * ssig2 - ssig1 * csig2) + 0, |
1451 | 49.9k | csig1 * csig2 + ssig1 * ssig2); |
1452 | | |
1453 | | /* omg12 = omg2 - omg1, limit to [0, pi] */ |
1454 | 49.9k | somg12 = fmax(0.0, comg1 * somg2 - somg1 * comg2) + 0; |
1455 | 49.9k | comg12 = comg1 * comg2 + somg1 * somg2; |
1456 | | /* eta = omg12 - lam120 */ |
1457 | 49.9k | eta = atan2(somg12 * clam120 - comg12 * slam120, |
1458 | 49.9k | comg12 * clam120 + somg12 * slam120); |
1459 | 49.9k | k2 = sq(calp0) * g->ep2; |
1460 | 49.9k | eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2); |
1461 | 49.9k | C3f(g, eps, Ca); |
1462 | 49.9k | B312 = (SinCosSeries(TRUE, ssig2, csig2, Ca, nC3-1) - |
1463 | 49.9k | SinCosSeries(TRUE, ssig1, csig1, Ca, nC3-1)); |
1464 | 49.9k | domg12 = -g->f * A3f(g, eps) * salp0 * (sig12 + B312); |
1465 | 49.9k | lam12 = eta + domg12; |
1466 | | |
1467 | 49.9k | if (diffp) { |
1468 | 49.9k | if (calp2 == 0) |
1469 | 0 | dlam12 = - 2 * g->f1 * dn1 / sbet1; |
1470 | 49.9k | else { |
1471 | 49.9k | Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2, |
1472 | 49.9k | cbet1, cbet2, NULLPTR, &dlam12, NULLPTR, NULLPTR, NULLPTR, Ca); |
1473 | 49.9k | dlam12 *= g->f1 / (calp2 * cbet2); |
1474 | 49.9k | } |
1475 | 49.9k | } |
1476 | | |
1477 | 49.9k | *psalp2 = salp2; |
1478 | 49.9k | *pcalp2 = calp2; |
1479 | 49.9k | *psig12 = sig12; |
1480 | 49.9k | *pssig1 = ssig1; |
1481 | 49.9k | *pcsig1 = csig1; |
1482 | 49.9k | *pssig2 = ssig2; |
1483 | 49.9k | *pcsig2 = csig2; |
1484 | 49.9k | *peps = eps; |
1485 | 49.9k | *pdomg12 = domg12; |
1486 | 49.9k | if (diffp) |
1487 | 49.9k | *pdlam12 = dlam12; |
1488 | | |
1489 | 49.9k | return lam12; |
1490 | 49.9k | } |
1491 | | |
1492 | 49.9k | double A3f(const struct geod_geodesic* g, double eps) { |
1493 | | /* Evaluate A3 */ |
1494 | 49.9k | return polyvalx(nA3 - 1, g->A3x, eps); |
1495 | 49.9k | } |
1496 | | |
1497 | 49.9k | void 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 | 49.9k | double mult = 1; |
1501 | 49.9k | int o = 0, l; |
1502 | 299k | for (l = 1; l < nC3; ++l) { /* l is index of C3[l] */ |
1503 | 249k | int m = nC3 - l - 1; /* order of polynomial in eps */ |
1504 | 249k | mult *= eps; |
1505 | 249k | c[l] = mult * polyvalx(m, g->C3x + o, eps); |
1506 | 249k | o += m + 1; |
1507 | 249k | } |
1508 | 49.9k | } |
1509 | | |
1510 | 0 | void 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 | 0 | double mult = 1; |
1514 | 0 | int o = 0, l; |
1515 | 0 | for (l = 0; l < nC4; ++l) { /* l is index of C4[l] */ |
1516 | 0 | int m = nC4 - l - 1; /* order of polynomial in eps */ |
1517 | 0 | c[l] = mult * polyvalx(m, g->C4x + o, eps); |
1518 | 0 | o += m + 1; |
1519 | 0 | mult *= eps; |
1520 | 0 | } |
1521 | 0 | } |
1522 | | |
1523 | | /* The scale factor A1-1 = mean value of (d/dsigma)I1 - 1 */ |
1524 | 65.1k | double A1m1f(double eps) { |
1525 | 65.1k | static const double coeff[] = { |
1526 | | /* (1-eps)*A1-1, polynomial in eps2 of order 3 */ |
1527 | 65.1k | 1, 4, 64, 0, 256, |
1528 | 65.1k | }; |
1529 | 65.1k | int m = nA1/2; |
1530 | 65.1k | double t = polyvalx(m, coeff, sq(eps)) / coeff[m + 1]; |
1531 | 65.1k | return (t + eps) / (1 - eps); |
1532 | 65.1k | } |
1533 | | |
1534 | | /* The coefficients C1[l] in the Fourier expansion of B1 */ |
1535 | 65.1k | void C1f(double eps, double c[]) { |
1536 | 65.1k | static const double coeff[] = { |
1537 | | /* C1[1]/eps^1, polynomial in eps2 of order 2 */ |
1538 | 65.1k | -1, 6, -16, 32, |
1539 | | /* C1[2]/eps^2, polynomial in eps2 of order 2 */ |
1540 | 65.1k | -9, 64, -128, 2048, |
1541 | | /* C1[3]/eps^3, polynomial in eps2 of order 1 */ |
1542 | 65.1k | 9, -16, 768, |
1543 | | /* C1[4]/eps^4, polynomial in eps2 of order 1 */ |
1544 | 65.1k | 3, -5, 512, |
1545 | | /* C1[5]/eps^5, polynomial in eps2 of order 0 */ |
1546 | 65.1k | -7, 1280, |
1547 | | /* C1[6]/eps^6, polynomial in eps2 of order 0 */ |
1548 | 65.1k | -7, 2048, |
1549 | 65.1k | }; |
1550 | 65.1k | double |
1551 | 65.1k | eps2 = sq(eps), |
1552 | 65.1k | d = eps; |
1553 | 65.1k | int o = 0, l; |
1554 | 455k | for (l = 1; l <= nC1; ++l) { /* l is index of C1p[l] */ |
1555 | 390k | int m = (nC1 - l) / 2; /* order of polynomial in eps^2 */ |
1556 | 390k | c[l] = d * polyvalx(m, coeff + o, eps2) / coeff[o + m + 1]; |
1557 | 390k | o += m + 2; |
1558 | 390k | d *= eps; |
1559 | 390k | } |
1560 | 65.1k | } |
1561 | | |
1562 | | /* The coefficients C1p[l] in the Fourier expansion of B1p */ |
1563 | 0 | void C1pf(double eps, double c[]) { |
1564 | 0 | static const double coeff[] = { |
1565 | | /* C1p[1]/eps^1, polynomial in eps2 of order 2 */ |
1566 | 0 | 205, -432, 768, 1536, |
1567 | | /* C1p[2]/eps^2, polynomial in eps2 of order 2 */ |
1568 | 0 | 4005, -4736, 3840, 12288, |
1569 | | /* C1p[3]/eps^3, polynomial in eps2 of order 1 */ |
1570 | 0 | -225, 116, 384, |
1571 | | /* C1p[4]/eps^4, polynomial in eps2 of order 1 */ |
1572 | 0 | -7173, 2695, 7680, |
1573 | | /* C1p[5]/eps^5, polynomial in eps2 of order 0 */ |
1574 | 0 | 3467, 7680, |
1575 | | /* C1p[6]/eps^6, polynomial in eps2 of order 0 */ |
1576 | 0 | 38081, 61440, |
1577 | 0 | }; |
1578 | 0 | double |
1579 | 0 | eps2 = sq(eps), |
1580 | 0 | d = eps; |
1581 | 0 | int o = 0, l; |
1582 | 0 | for (l = 1; l <= nC1p; ++l) { /* l is index of C1p[l] */ |
1583 | 0 | int m = (nC1p - l) / 2; /* order of polynomial in eps^2 */ |
1584 | 0 | c[l] = d * polyvalx(m, coeff + o, eps2) / coeff[o + m + 1]; |
1585 | 0 | o += m + 2; |
1586 | 0 | d *= eps; |
1587 | 0 | } |
1588 | 0 | } |
1589 | | |
1590 | | /* The scale factor A2-1 = mean value of (d/dsigma)I2 - 1 */ |
1591 | 65.1k | double A2m1f(double eps) { |
1592 | 65.1k | static const double coeff[] = { |
1593 | | /* (eps+1)*A2-1, polynomial in eps2 of order 3 */ |
1594 | 65.1k | -11, -28, -192, 0, 256, |
1595 | 65.1k | }; |
1596 | 65.1k | int m = nA2/2; |
1597 | 65.1k | double t = polyvalx(m, coeff, sq(eps)) / coeff[m + 1]; |
1598 | 65.1k | return (t - eps) / (1 + eps); |
1599 | 65.1k | } |
1600 | | |
1601 | | /* The coefficients C2[l] in the Fourier expansion of B2 */ |
1602 | 65.1k | void C2f(double eps, double c[]) { |
1603 | 65.1k | static const double coeff[] = { |
1604 | | /* C2[1]/eps^1, polynomial in eps2 of order 2 */ |
1605 | 65.1k | 1, 2, 16, 32, |
1606 | | /* C2[2]/eps^2, polynomial in eps2 of order 2 */ |
1607 | 65.1k | 35, 64, 384, 2048, |
1608 | | /* C2[3]/eps^3, polynomial in eps2 of order 1 */ |
1609 | 65.1k | 15, 80, 768, |
1610 | | /* C2[4]/eps^4, polynomial in eps2 of order 1 */ |
1611 | 65.1k | 7, 35, 512, |
1612 | | /* C2[5]/eps^5, polynomial in eps2 of order 0 */ |
1613 | 65.1k | 63, 1280, |
1614 | | /* C2[6]/eps^6, polynomial in eps2 of order 0 */ |
1615 | 65.1k | 77, 2048, |
1616 | 65.1k | }; |
1617 | 65.1k | double |
1618 | 65.1k | eps2 = sq(eps), |
1619 | 65.1k | d = eps; |
1620 | 65.1k | int o = 0, l; |
1621 | 455k | for (l = 1; l <= nC2; ++l) { /* l is index of C2[l] */ |
1622 | 390k | int m = (nC2 - l) / 2; /* order of polynomial in eps^2 */ |
1623 | 390k | c[l] = d * polyvalx(m, coeff + o, eps2) / coeff[o + m + 1]; |
1624 | 390k | o += m + 2; |
1625 | 390k | d *= eps; |
1626 | 390k | } |
1627 | 65.1k | } |
1628 | | |
1629 | | /* The scale factor A3 = mean value of (d/dsigma)I3 */ |
1630 | 303k | void A3coeff(struct geod_geodesic* g) { |
1631 | 303k | static const double coeff[] = { |
1632 | | /* A3, coeff of eps^5, polynomial in n of order 0 */ |
1633 | 303k | -3, 128, |
1634 | | /* A3, coeff of eps^4, polynomial in n of order 1 */ |
1635 | 303k | -2, -3, 64, |
1636 | | /* A3, coeff of eps^3, polynomial in n of order 2 */ |
1637 | 303k | -1, -3, -1, 16, |
1638 | | /* A3, coeff of eps^2, polynomial in n of order 2 */ |
1639 | 303k | 3, -1, -2, 8, |
1640 | | /* A3, coeff of eps^1, polynomial in n of order 1 */ |
1641 | 303k | 1, -1, 2, |
1642 | | /* A3, coeff of eps^0, polynomial in n of order 0 */ |
1643 | 303k | 1, 1, |
1644 | 303k | }; |
1645 | 303k | int o = 0, k = 0, j; |
1646 | 2.12M | for (j = nA3 - 1; j >= 0; --j) { /* coeff of eps^j */ |
1647 | 1.82M | int m = nA3 - j - 1 < j ? nA3 - j - 1 : j; /* order of polynomial in n */ |
1648 | 1.82M | g->A3x[k++] = polyvalx(m, coeff + o, g->n) / coeff[o + m + 1]; |
1649 | 1.82M | o += m + 2; |
1650 | 1.82M | } |
1651 | 303k | } |
1652 | | |
1653 | | /* The coefficients C3[l] in the Fourier expansion of B3 */ |
1654 | 303k | void C3coeff(struct geod_geodesic* g) { |
1655 | 303k | static const double coeff[] = { |
1656 | | /* C3[1], coeff of eps^5, polynomial in n of order 0 */ |
1657 | 303k | 3, 128, |
1658 | | /* C3[1], coeff of eps^4, polynomial in n of order 1 */ |
1659 | 303k | 2, 5, 128, |
1660 | | /* C3[1], coeff of eps^3, polynomial in n of order 2 */ |
1661 | 303k | -1, 3, 3, 64, |
1662 | | /* C3[1], coeff of eps^2, polynomial in n of order 2 */ |
1663 | 303k | -1, 0, 1, 8, |
1664 | | /* C3[1], coeff of eps^1, polynomial in n of order 1 */ |
1665 | 303k | -1, 1, 4, |
1666 | | /* C3[2], coeff of eps^5, polynomial in n of order 0 */ |
1667 | 303k | 5, 256, |
1668 | | /* C3[2], coeff of eps^4, polynomial in n of order 1 */ |
1669 | 303k | 1, 3, 128, |
1670 | | /* C3[2], coeff of eps^3, polynomial in n of order 2 */ |
1671 | 303k | -3, -2, 3, 64, |
1672 | | /* C3[2], coeff of eps^2, polynomial in n of order 2 */ |
1673 | 303k | 1, -3, 2, 32, |
1674 | | /* C3[3], coeff of eps^5, polynomial in n of order 0 */ |
1675 | 303k | 7, 512, |
1676 | | /* C3[3], coeff of eps^4, polynomial in n of order 1 */ |
1677 | 303k | -10, 9, 384, |
1678 | | /* C3[3], coeff of eps^3, polynomial in n of order 2 */ |
1679 | 303k | 5, -9, 5, 192, |
1680 | | /* C3[4], coeff of eps^5, polynomial in n of order 0 */ |
1681 | 303k | 7, 512, |
1682 | | /* C3[4], coeff of eps^4, polynomial in n of order 1 */ |
1683 | 303k | -14, 7, 512, |
1684 | | /* C3[5], coeff of eps^5, polynomial in n of order 0 */ |
1685 | 303k | 21, 2560, |
1686 | 303k | }; |
1687 | 303k | int o = 0, k = 0, l, j; |
1688 | 1.82M | for (l = 1; l < nC3; ++l) { /* l is index of C3[l] */ |
1689 | 6.07M | for (j = nC3 - 1; j >= l; --j) { /* coeff of eps^j */ |
1690 | 4.55M | int m = nC3 - j - 1 < j ? nC3 - j - 1 : j; /* order of polynomial in n */ |
1691 | 4.55M | g->C3x[k++] = polyvalx(m, coeff + o, g->n) / coeff[o + m + 1]; |
1692 | 4.55M | o += m + 2; |
1693 | 4.55M | } |
1694 | 1.51M | } |
1695 | 303k | } |
1696 | | |
1697 | | /* The coefficients C4[l] in the Fourier expansion of I4 */ |
1698 | 303k | void C4coeff(struct geod_geodesic* g) { |
1699 | 303k | static const double coeff[] = { |
1700 | | /* C4[0], coeff of eps^5, polynomial in n of order 0 */ |
1701 | 303k | 97, 15015, |
1702 | | /* C4[0], coeff of eps^4, polynomial in n of order 1 */ |
1703 | 303k | 1088, 156, 45045, |
1704 | | /* C4[0], coeff of eps^3, polynomial in n of order 2 */ |
1705 | 303k | -224, -4784, 1573, 45045, |
1706 | | /* C4[0], coeff of eps^2, polynomial in n of order 3 */ |
1707 | 303k | -10656, 14144, -4576, -858, 45045, |
1708 | | /* C4[0], coeff of eps^1, polynomial in n of order 4 */ |
1709 | 303k | 64, 624, -4576, 6864, -3003, 15015, |
1710 | | /* C4[0], coeff of eps^0, polynomial in n of order 5 */ |
1711 | 303k | 100, 208, 572, 3432, -12012, 30030, 45045, |
1712 | | /* C4[1], coeff of eps^5, polynomial in n of order 0 */ |
1713 | 303k | 1, 9009, |
1714 | | /* C4[1], coeff of eps^4, polynomial in n of order 1 */ |
1715 | 303k | -2944, 468, 135135, |
1716 | | /* C4[1], coeff of eps^3, polynomial in n of order 2 */ |
1717 | 303k | 5792, 1040, -1287, 135135, |
1718 | | /* C4[1], coeff of eps^2, polynomial in n of order 3 */ |
1719 | 303k | 5952, -11648, 9152, -2574, 135135, |
1720 | | /* C4[1], coeff of eps^1, polynomial in n of order 4 */ |
1721 | 303k | -64, -624, 4576, -6864, 3003, 135135, |
1722 | | /* C4[2], coeff of eps^5, polynomial in n of order 0 */ |
1723 | 303k | 8, 10725, |
1724 | | /* C4[2], coeff of eps^4, polynomial in n of order 1 */ |
1725 | 303k | 1856, -936, 225225, |
1726 | | /* C4[2], coeff of eps^3, polynomial in n of order 2 */ |
1727 | 303k | -8448, 4992, -1144, 225225, |
1728 | | /* C4[2], coeff of eps^2, polynomial in n of order 3 */ |
1729 | 303k | -1440, 4160, -4576, 1716, 225225, |
1730 | | /* C4[3], coeff of eps^5, polynomial in n of order 0 */ |
1731 | 303k | -136, 63063, |
1732 | | /* C4[3], coeff of eps^4, polynomial in n of order 1 */ |
1733 | 303k | 1024, -208, 105105, |
1734 | | /* C4[3], coeff of eps^3, polynomial in n of order 2 */ |
1735 | 303k | 3584, -3328, 1144, 315315, |
1736 | | /* C4[4], coeff of eps^5, polynomial in n of order 0 */ |
1737 | 303k | -128, 135135, |
1738 | | /* C4[4], coeff of eps^4, polynomial in n of order 1 */ |
1739 | 303k | -2560, 832, 405405, |
1740 | | /* C4[5], coeff of eps^5, polynomial in n of order 0 */ |
1741 | 303k | 128, 99099, |
1742 | 303k | }; |
1743 | 303k | int o = 0, k = 0, l, j; |
1744 | 2.12M | for (l = 0; l < nC4; ++l) { /* l is index of C4[l] */ |
1745 | 8.20M | for (j = nC4 - 1; j >= l; --j) { /* coeff of eps^j */ |
1746 | 6.38M | int m = nC4 - j - 1; /* order of polynomial in n */ |
1747 | 6.38M | g->C4x[k++] = polyvalx(m, coeff + o, g->n) / coeff[o + m + 1]; |
1748 | 6.38M | o += m + 2; |
1749 | 6.38M | } |
1750 | 1.82M | } |
1751 | 303k | } |
1752 | | |
1753 | 0 | int transit(double lon1, double lon2) { |
1754 | 0 | 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 | 0 | lon12 = AngDiff(lon1, lon2, NULLPTR); |
1759 | 0 | lon1 = AngNormalize(lon1); |
1760 | 0 | lon2 = AngNormalize(lon2); |
1761 | 0 | return |
1762 | 0 | lon12 > 0 && ((lon1 < 0 && lon2 >= 0) || |
1763 | 0 | (lon1 > 0 && lon2 == 0)) ? 1 : |
1764 | 0 | (lon12 < 0 && lon1 >= 0 && lon2 < 0 ? -1 : 0); |
1765 | 0 | } |
1766 | | |
1767 | 0 | int transitdirect(double lon1, double lon2) { |
1768 | | /* Compute exactly the parity of |
1769 | | * int(floor(lon2 / 360)) - int(floor(lon1 / 360)) */ |
1770 | 0 | lon1 = remainder(lon1, 2.0 * td); lon2 = remainder(lon2, 2.0 * td); |
1771 | 0 | return ( (lon2 >= 0 && lon2 < td ? 0 : 1) - |
1772 | 0 | (lon1 >= 0 && lon1 < td ? 0 : 1) ); |
1773 | 0 | } |
1774 | | |
1775 | 0 | void accini(double s[]) { |
1776 | | /* Initialize an accumulator; this is an array with two elements. */ |
1777 | 0 | s[0] = s[1] = 0; |
1778 | 0 | } |
1779 | | |
1780 | 0 | void acccopy(const double s[], double t[]) { |
1781 | | /* Copy an accumulator; t = s. */ |
1782 | 0 | t[0] = s[0]; t[1] = s[1]; |
1783 | 0 | } |
1784 | | |
1785 | 0 | void accadd(double s[], double y) { |
1786 | | /* Add y to an accumulator. */ |
1787 | 0 | double u, z = sumx(y, s[1], &u); |
1788 | 0 | s[0] = sumx(z, s[0], &s[1]); |
1789 | 0 | if (s[0] == 0) |
1790 | 0 | s[0] = u; |
1791 | 0 | else |
1792 | 0 | s[1] = s[1] + u; |
1793 | 0 | } |
1794 | | |
1795 | 0 | double accsum(const double s[], double y) { |
1796 | | /* Return accumulator + y (but don't add to accumulator). */ |
1797 | 0 | double t[2]; |
1798 | 0 | acccopy(s, t); |
1799 | 0 | accadd(t, y); |
1800 | 0 | return t[0]; |
1801 | 0 | } |
1802 | | |
1803 | 0 | void accneg(double s[]) { |
1804 | | /* Negate an accumulator. */ |
1805 | 0 | s[0] = -s[0]; s[1] = -s[1]; |
1806 | 0 | } |
1807 | | |
1808 | 0 | void accrem(double s[], double y) { |
1809 | | /* Reduce to [-y/2, y/2]. */ |
1810 | 0 | s[0] = remainder(s[0], y); |
1811 | 0 | accadd(s, 0.0); |
1812 | 0 | } |
1813 | | |
1814 | 0 | void geod_polygon_init(struct geod_polygon* p, boolx polylinep) { |
1815 | 0 | p->polyline = (polylinep != 0); |
1816 | 0 | geod_polygon_clear(p); |
1817 | 0 | } |
1818 | | |
1819 | 0 | void geod_polygon_clear(struct geod_polygon* p) { |
1820 | 0 | p->lat0 = p->lon0 = p->lat = p->lon = NaN; |
1821 | 0 | accini(p->P); |
1822 | 0 | accini(p->A); |
1823 | 0 | p->num = p->crossings = 0; |
1824 | 0 | } |
1825 | | |
1826 | | void geod_polygon_addpoint(const struct geod_geodesic* g, |
1827 | | struct geod_polygon* p, |
1828 | 0 | double lat, double lon) { |
1829 | 0 | if (p->num == 0) { |
1830 | 0 | p->lat0 = p->lat = lat; |
1831 | 0 | p->lon0 = p->lon = lon; |
1832 | 0 | } else { |
1833 | 0 | double s12, S12 = 0; /* Initialize S12 to stop Visual Studio warning */ |
1834 | 0 | geod_geninverse(g, p->lat, p->lon, lat, lon, |
1835 | 0 | &s12, NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR, |
1836 | 0 | p->polyline ? NULLPTR : &S12); |
1837 | 0 | accadd(p->P, s12); |
1838 | 0 | if (!p->polyline) { |
1839 | 0 | accadd(p->A, S12); |
1840 | 0 | p->crossings += transit(p->lon, lon); |
1841 | 0 | } |
1842 | 0 | p->lat = lat; p->lon = lon; |
1843 | 0 | } |
1844 | 0 | ++p->num; |
1845 | 0 | } |
1846 | | |
1847 | | void geod_polygon_addedge(const struct geod_geodesic* g, |
1848 | | struct geod_polygon* p, |
1849 | 0 | double azi, double s) { |
1850 | 0 | 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 | 0 | double lat = 0, lon = 0, S12 = 0; |
1854 | 0 | geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s, |
1855 | 0 | &lat, &lon, NULLPTR, |
1856 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR, |
1857 | 0 | p->polyline ? NULLPTR : &S12); |
1858 | 0 | accadd(p->P, s); |
1859 | 0 | if (!p->polyline) { |
1860 | 0 | accadd(p->A, S12); |
1861 | 0 | p->crossings += transitdirect(p->lon, lon); |
1862 | 0 | } |
1863 | 0 | p->lat = lat; p->lon = lon; |
1864 | 0 | ++p->num; |
1865 | 0 | } |
1866 | 0 | } |
1867 | | |
1868 | | unsigned geod_polygon_compute(const struct geod_geodesic* g, |
1869 | | const struct geod_polygon* p, |
1870 | | boolx reverse, boolx sign, |
1871 | 0 | double* pA, double* pP) { |
1872 | 0 | double s12, S12, t[2]; |
1873 | 0 | if (p->num < 2) { |
1874 | 0 | if (pP) *pP = 0; |
1875 | 0 | if (!p->polyline && pA) *pA = 0; |
1876 | 0 | return p->num; |
1877 | 0 | } |
1878 | 0 | if (p->polyline) { |
1879 | 0 | if (pP) *pP = p->P[0]; |
1880 | 0 | return p->num; |
1881 | 0 | } |
1882 | 0 | geod_geninverse(g, p->lat, p->lon, p->lat0, p->lon0, |
1883 | 0 | &s12, NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR, &S12); |
1884 | 0 | if (pP) *pP = accsum(p->P, s12); |
1885 | 0 | acccopy(p->A, t); |
1886 | 0 | accadd(t, S12); |
1887 | 0 | if (pA) *pA = areareduceA(t, 4 * pi * g->c2, |
1888 | 0 | p->crossings + transit(p->lon, p->lon0), |
1889 | 0 | reverse, sign); |
1890 | 0 | return p->num; |
1891 | 0 | } |
1892 | | |
1893 | | unsigned 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 | 0 | double* pA, double* pP) { |
1898 | 0 | double perimeter, tempsum; |
1899 | 0 | int crossings, i; |
1900 | 0 | unsigned num = p->num + 1; |
1901 | 0 | if (num == 1) { |
1902 | 0 | if (pP) *pP = 0; |
1903 | 0 | if (!p->polyline && pA) *pA = 0; |
1904 | 0 | return num; |
1905 | 0 | } |
1906 | 0 | perimeter = p->P[0]; |
1907 | 0 | tempsum = p->polyline ? 0 : p->A[0]; |
1908 | 0 | crossings = p->crossings; |
1909 | 0 | for (i = 0; i < (p->polyline ? 1 : 2); ++i) { |
1910 | 0 | double s12, S12 = 0; /* Initialize S12 to stop Visual Studio warning */ |
1911 | 0 | geod_geninverse(g, |
1912 | 0 | i == 0 ? p->lat : lat, i == 0 ? p->lon : lon, |
1913 | 0 | i != 0 ? p->lat0 : lat, i != 0 ? p->lon0 : lon, |
1914 | 0 | &s12, NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR, |
1915 | 0 | p->polyline ? NULLPTR : &S12); |
1916 | 0 | perimeter += s12; |
1917 | 0 | if (!p->polyline) { |
1918 | 0 | tempsum += S12; |
1919 | 0 | crossings += transit(i == 0 ? p->lon : lon, |
1920 | 0 | i != 0 ? p->lon0 : lon); |
1921 | 0 | } |
1922 | 0 | } |
1923 | |
|
1924 | 0 | if (pP) *pP = perimeter; |
1925 | 0 | if (p->polyline) |
1926 | 0 | return num; |
1927 | | |
1928 | 0 | if (pA) *pA = areareduceB(tempsum, 4 * pi * g->c2, crossings, reverse, sign); |
1929 | 0 | return num; |
1930 | 0 | } |
1931 | | |
1932 | | unsigned 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 | 0 | double* pA, double* pP) { |
1937 | 0 | double perimeter, tempsum; |
1938 | 0 | int crossings; |
1939 | 0 | unsigned num = p->num + 1; |
1940 | 0 | if (num == 1) { /* we don't have a starting point! */ |
1941 | 0 | if (pP) *pP = NaN; |
1942 | 0 | if (!p->polyline && pA) *pA = NaN; |
1943 | 0 | return 0; |
1944 | 0 | } |
1945 | 0 | perimeter = p->P[0] + s; |
1946 | 0 | if (p->polyline) { |
1947 | 0 | if (pP) *pP = perimeter; |
1948 | 0 | return num; |
1949 | 0 | } |
1950 | | |
1951 | 0 | tempsum = p->A[0]; |
1952 | 0 | crossings = p->crossings; |
1953 | 0 | { |
1954 | | /* Initialization of lat, lon, and S12 is to make CLang static analyzer |
1955 | | * happy. */ |
1956 | 0 | double lat = 0, lon = 0, s12, S12 = 0; |
1957 | 0 | geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s, |
1958 | 0 | &lat, &lon, NULLPTR, |
1959 | 0 | NULLPTR, NULLPTR, NULLPTR, NULLPTR, &S12); |
1960 | 0 | tempsum += S12; |
1961 | 0 | crossings += transitdirect(p->lon, lon); |
1962 | 0 | geod_geninverse(g, lat, lon, p->lat0, p->lon0, |
1963 | 0 | &s12, NULLPTR, NULLPTR, NULLPTR, NULLPTR, NULLPTR, &S12); |
1964 | 0 | perimeter += s12; |
1965 | 0 | tempsum += S12; |
1966 | 0 | crossings += transit(lon, p->lon0); |
1967 | 0 | } |
1968 | |
|
1969 | 0 | if (pP) *pP = perimeter; |
1970 | 0 | if (pA) *pA = areareduceB(tempsum, 4 * pi * g->c2, crossings, reverse, sign); |
1971 | 0 | return num; |
1972 | 0 | } |
1973 | | |
1974 | | void geod_polygonarea(const struct geod_geodesic* g, |
1975 | | double lats[], double lons[], int n, |
1976 | 0 | double* pA, double* pP) { |
1977 | 0 | int i; |
1978 | 0 | struct geod_polygon p; |
1979 | 0 | geod_polygon_init(&p, FALSE); |
1980 | 0 | for (i = 0; i < n; ++i) |
1981 | 0 | geod_polygon_addpoint(g, &p, lats[i], lons[i]); |
1982 | 0 | geod_polygon_compute(g, &p, FALSE, TRUE, pA, pP); |
1983 | 0 | } |
1984 | | |
1985 | | double areareduceA(double area[], double area0, |
1986 | 0 | int crossings, boolx reverse, boolx sign) { |
1987 | 0 | accrem(area, area0); |
1988 | 0 | if (crossings & 1) |
1989 | 0 | 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 | 0 | if (!reverse) |
1993 | 0 | accneg(area); |
1994 | | /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ |
1995 | 0 | if (sign) { |
1996 | 0 | if (area[0] > area0/2) |
1997 | 0 | accadd(area, -area0); |
1998 | 0 | else if (area[0] <= -area0/2) |
1999 | 0 | accadd(area, +area0); |
2000 | 0 | } else { |
2001 | 0 | if (area[0] >= area0) |
2002 | 0 | accadd(area, -area0); |
2003 | 0 | else if (area[0] < 0) |
2004 | 0 | accadd(area, +area0); |
2005 | 0 | } |
2006 | 0 | return 0 + area[0]; |
2007 | 0 | } |
2008 | | |
2009 | | double areareduceB(double area, double area0, |
2010 | 0 | int crossings, boolx reverse, boolx sign) { |
2011 | 0 | area = remainder(area, area0); |
2012 | 0 | if (crossings & 1) |
2013 | 0 | area += (area < 0 ? 1 : -1) * area0/2; |
2014 | | /* area is with the clockwise sense. If !reverse convert to |
2015 | | * counter-clockwise convention. */ |
2016 | 0 | if (!reverse) |
2017 | 0 | area *= -1; |
2018 | | /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ |
2019 | 0 | if (sign) { |
2020 | 0 | if (area > area0/2) |
2021 | 0 | area -= area0; |
2022 | 0 | else if (area <= -area0/2) |
2023 | 0 | area += area0; |
2024 | 0 | } else { |
2025 | 0 | if (area >= area0) |
2026 | 0 | area -= area0; |
2027 | 0 | else if (area < 0) |
2028 | 0 | area += area0; |
2029 | 0 | } |
2030 | 0 | return 0 + area; |
2031 | 0 | } |
2032 | | |
2033 | | /** @endcond */ |