/src/proj/src/projections/eck4.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | |
3 | | #include <math.h> |
4 | | |
5 | | #include "proj.h" |
6 | | #include "proj_internal.h" |
7 | | |
8 | | PROJ_HEAD(eck4, "Eckert IV") "\n\tPCyl, Sph"; |
9 | | |
10 | | // C_x = 2 / sqrt(4 * M_PI + M_PI * M_PI) |
11 | | constexpr double C_x = .42223820031577120149; |
12 | | |
13 | | // C_y = 2 * sqrt(M_PI / (4 + M_PI)) |
14 | | constexpr double C_y = 1.32650042817700232218; |
15 | | |
16 | | // RC_y = 1. / C_y |
17 | | constexpr double RC_y = .75386330736002178205; |
18 | | |
19 | | // C_p = 2 + M_PI / 2 |
20 | | constexpr double C_p = 3.57079632679489661922; |
21 | | |
22 | | // RC_p = 1. / C_p |
23 | | constexpr double RC_p = .28004957675577868795; |
24 | | |
25 | 0 | static PJ_XY eck4_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */ |
26 | 0 | PJ_XY xy = {0.0, 0.0}; |
27 | 0 | int i; |
28 | 0 | (void)P; |
29 | | |
30 | | // Newton's iterative method to find theta such as |
31 | | // theta + sin(theta) * cos(theta) + 2 * sin(theta) == C_p * sin(phi) |
32 | 0 | const double p = C_p * sin(lp.phi); |
33 | 0 | double V = lp.phi * lp.phi; |
34 | 0 | double theta = lp.phi * (0.895168 + V * (0.0218849 + V * 0.00826809)); |
35 | 0 | constexpr int NITER = 6; |
36 | 0 | for (i = NITER; i; --i) { |
37 | 0 | const double c = cos(theta); |
38 | 0 | const double s = sin(theta); |
39 | 0 | V = (theta + s * (c + 2.) - p) / (1. + c * (c + 2.) - s * s); |
40 | 0 | theta -= V; |
41 | 0 | constexpr double EPS = 1e-7; |
42 | 0 | if (fabs(V) < EPS) |
43 | 0 | break; |
44 | 0 | } |
45 | 0 | if (!i) { |
46 | 0 | xy.x = C_x * lp.lam; |
47 | 0 | xy.y = theta < 0. ? -C_y : C_y; |
48 | 0 | } else { |
49 | 0 | xy.x = C_x * lp.lam * (1. + cos(theta)); |
50 | 0 | xy.y = C_y * sin(theta); |
51 | 0 | } |
52 | 0 | return xy; |
53 | 0 | } |
54 | | |
55 | 0 | static PJ_LP eck4_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */ |
56 | 0 | PJ_LP lp = {0.0, 0.0}; |
57 | |
|
58 | 0 | const double sin_theta = xy.y * RC_y; |
59 | 0 | const double one_minus_abs_sin_theta = 1.0 - fabs(sin_theta); |
60 | 0 | if (one_minus_abs_sin_theta >= 0.0 && one_minus_abs_sin_theta <= 1e-12) { |
61 | 0 | lp.lam = xy.x / C_x; |
62 | 0 | lp.phi = sin_theta > 0 ? M_PI / 2 : -M_PI / 2; |
63 | 0 | } else { |
64 | 0 | const double theta = aasin(P->ctx, sin_theta); |
65 | 0 | const double cos_theta = cos(theta); |
66 | 0 | lp.lam = xy.x / (C_x * (1. + cos_theta)); |
67 | 0 | const double sin_phi = (theta + sin_theta * (cos_theta + 2.)) * RC_p; |
68 | 0 | lp.phi = aasin(P->ctx, sin_phi); |
69 | 0 | } |
70 | 0 | if (!P->over) { |
71 | 0 | const double fabs_lam_minus_pi = fabs(lp.lam) - M_PI; |
72 | 0 | if (fabs_lam_minus_pi > 0.0) { |
73 | 0 | if (fabs_lam_minus_pi > 1e-10) { |
74 | 0 | proj_errno_set( |
75 | 0 | P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN); |
76 | 0 | return lp; |
77 | 0 | } else { |
78 | 0 | lp.lam = lp.lam > 0 ? M_PI : -M_PI; |
79 | 0 | } |
80 | 0 | } |
81 | 0 | } |
82 | 0 | return lp; |
83 | 0 | } |
84 | | |
85 | 318 | PJ *PJ_PROJECTION(eck4) { |
86 | 318 | P->es = 0.0; |
87 | 318 | P->inv = eck4_s_inverse; |
88 | 318 | P->fwd = eck4_s_forward; |
89 | | |
90 | 318 | return P; |
91 | 318 | } |