Coverage Report

Created: 2025-06-13 06:29

/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
}