/src/PROJ/src/projections/vandg.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Changes to handle +over are: Copyright 2011-2014 Morelli Informatik |
2 | | |
3 | | #include "proj.h" |
4 | | #include "proj_internal.h" |
5 | | |
6 | | PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph"; |
7 | | |
8 | 101k | #define TOL 1.e-10 |
9 | 0 | #define THIRD .33333333333333333333 |
10 | 0 | #define C2_27 .07407407407407407407 // 2/27 |
11 | 0 | #define PI4_3 4.18879020478639098458 // 4*pi/3 |
12 | 0 | #define PISQ 9.86960440108935861869 // pi^2 |
13 | 0 | #define TPISQ 19.73920880217871723738 // 2*pi^2 |
14 | 0 | #define HPISQ 4.93480220054467930934 // pi^2/2 |
15 | | |
16 | 16.9k | static PJ_XY vandg_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
17 | 16.9k | PJ_XY xy = {0.0, 0.0}; |
18 | 16.9k | double al, al2, g, g2, p2; |
19 | | // Comments tie this formulation to Snyder (1987), p. 241. |
20 | 16.9k | p2 = fabs(lp.phi / M_HALFPI); // sin(theta) from (29-6) |
21 | 16.9k | if ((p2 - TOL) > 1.) { |
22 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
23 | 0 | return xy; |
24 | 0 | } |
25 | 16.9k | int sign = 1; |
26 | 16.9k | if (P->over && fabs(lp.lam) > M_PI) |
27 | 0 | sign = -1; |
28 | 16.9k | if (p2 > 1.) |
29 | 0 | p2 = 1.; |
30 | 16.9k | if (fabs(lp.phi) <= TOL) { |
31 | 0 | xy.x = lp.lam; |
32 | 0 | xy.y = 0.; |
33 | 16.9k | } else if (fabs(lp.lam) <= TOL || fabs(p2 - 1.) < TOL) { |
34 | 0 | xy.x = 0.; |
35 | 0 | xy.y = M_PI * tan(.5 * asin(p2)); |
36 | 0 | if (lp.phi < 0.) |
37 | 0 | xy.y = -xy.y; |
38 | 16.9k | } else { |
39 | 16.9k | al = .5 * sign * fabs(M_PI / lp.lam - lp.lam / M_PI); // A from (29-3) |
40 | 16.9k | al2 = al * al; // A^2 |
41 | 16.9k | g = sqrt(1. - p2 * p2); // cos(theta) |
42 | 16.9k | g = g / (p2 + g - 1.); // G from (29-4) |
43 | 16.9k | g2 = g * g; // G^2 |
44 | 16.9k | p2 = g * (2. / p2 - 1.); // P from (29-5) |
45 | 16.9k | p2 = p2 * p2; // P^2 |
46 | 16.9k | { |
47 | | // Force floating-point computations done on 80 bit on |
48 | | // i386 to go back to 64 bit. This is to avoid the test failures |
49 | | // of |
50 | | // https://github.com/OSGeo/PROJ/issues/1906#issuecomment-583168348 |
51 | | // The choice of p2 is completely arbitrary. |
52 | 16.9k | volatile double p2_tmp = p2; |
53 | | // cppcheck-suppress redundantAssignment |
54 | 16.9k | p2 = p2_tmp; |
55 | 16.9k | } |
56 | 16.9k | xy.x = g - p2; // G - P^2 |
57 | 16.9k | g = p2 + al2; // P^2 + A^2 |
58 | | // (29-1) |
59 | 16.9k | xy.x = M_PI * |
60 | 16.9k | fabs(al * xy.x + sqrt(al2 * xy.x * xy.x - g * (g2 - p2))) / g; |
61 | 16.9k | if (lp.lam < 0.) |
62 | 16.2k | xy.x = -xy.x; |
63 | 16.9k | xy.y = fabs(xy.x / M_PI); |
64 | | // y from (29-2) has been expressed in terms of x here |
65 | 16.9k | xy.y = 1. - xy.y * (xy.y + 2. * al); |
66 | 16.9k | if (xy.y < -TOL) { |
67 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
68 | 0 | return xy; |
69 | 0 | } |
70 | 16.9k | if (xy.y < 0.) |
71 | 0 | xy.y = 0.; |
72 | 16.9k | else |
73 | 16.9k | xy.y = sqrt(xy.y) * (lp.phi < 0. ? -M_PI : M_PI); |
74 | 16.9k | } |
75 | | |
76 | 16.9k | return xy; |
77 | 16.9k | } |
78 | | |
79 | 0 | static PJ_LP vandg_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
80 | 0 | PJ_LP lp = {0.0, 0.0}; |
81 | 0 | double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2; |
82 | | // Comments tie this formulation to Snyder (1987), p. 242. |
83 | 0 | x2 = xy.x * xy.x; // pi^2 * X^2 |
84 | 0 | if ((ay = fabs(xy.y)) < TOL) { |
85 | 0 | lp.phi = 0.; |
86 | 0 | t = x2 * x2 + TPISQ * (x2 + HPISQ); |
87 | 0 | lp.lam = fabs(xy.x) <= TOL ? 0. : .5 * (x2 - PISQ + sqrt(t)) / xy.x; |
88 | 0 | return (lp); |
89 | 0 | } |
90 | 0 | y2 = xy.y * xy.y; // pi^2 * Y^2 |
91 | 0 | r = x2 + y2; // pi^2 * (X^2+Y^2) |
92 | 0 | r2 = r * r; // pi^4 * (X^2+Y^2)^2 |
93 | 0 | c1 = -M_PI * ay * (r + PISQ); // pi^4 * c1 (29-11) |
94 | | // pi^4 * c3 (29-13) |
95 | 0 | c3 = r2 + M_TWOPI * (ay * r + M_PI * (y2 + M_PI * (ay + M_HALFPI))); |
96 | 0 | c2 = c1 + PISQ * (r - 3. * y2); // pi^4 * c2 (29-12) |
97 | 0 | c0 = M_PI * ay; // pi^2 * Y |
98 | 0 | c2 /= c3; // c2/c3 |
99 | 0 | al = c1 / c3 - THIRD * c2 * c2; // a1 (29-15) |
100 | 0 | m = 2. * sqrt(-THIRD * al); // m1 (29-16) |
101 | 0 | d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3; // d (29-14) |
102 | 0 | const double al_mul_m = al * m; // a1*m1 |
103 | 0 | if (fabs(al_mul_m) < 1e-16) { |
104 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
105 | 0 | return proj_coord_error().lp; |
106 | 0 | } |
107 | 0 | d = 3. * d / al_mul_m; // cos(3*theta1) (29-17) |
108 | 0 | t = fabs(d); |
109 | 0 | if ((t - TOL) <= 1.) { |
110 | 0 | d = t > 1. ? (d > 0. ? 0. : M_PI) : acos(d); // 3*theta1 (29-17) |
111 | 0 | if (r > PISQ) { |
112 | | // This code path is triggered for coordinates generated in the |
113 | | // forward path when |long|>180deg and +over |
114 | 0 | d = M_TWOPI - d; |
115 | 0 | } |
116 | | // (29-18) but change pi/3 to 4*pi/3 to flip sign of cos |
117 | 0 | lp.phi = M_PI * (m * cos(d * THIRD + PI4_3) - THIRD * c2); |
118 | 0 | if (xy.y < 0.) |
119 | 0 | lp.phi = -lp.phi; |
120 | 0 | t = r2 + TPISQ * (x2 - y2 + HPISQ); |
121 | 0 | lp.lam = fabs(xy.x) <= TOL |
122 | 0 | ? 0. |
123 | 0 | : .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy.x; |
124 | 0 | } else { |
125 | 0 | proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
126 | 0 | return lp; |
127 | 0 | } |
128 | | |
129 | 0 | return lp; |
130 | 0 | } |
131 | | |
132 | 217 | PJ *PJ_PROJECTION(vandg) { |
133 | 217 | P->es = 0.; |
134 | 217 | P->inv = vandg_s_inverse; |
135 | 217 | P->fwd = vandg_s_forward; |
136 | | |
137 | 217 | return P; |
138 | 217 | } |
139 | | |
140 | | #undef TOL |
141 | | #undef THIRD |
142 | | #undef C2_27 |
143 | | #undef PI4_3 |
144 | | #undef PISQ |
145 | | #undef TPISQ |
146 | | #undef HPISQ |