Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/projections/cass.cpp
Line
Count
Source
1
2
3
#include <errno.h>
4
#include <math.h>
5
6
#include "proj.h"
7
#include "proj_internal.h"
8
9
PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell";
10
11
0
#define C1 .16666666666666666666
12
0
#define C2 .00833333333333333333
13
0
#define C3 .04166666666666666666
14
0
#define C4 .33333333333333333333
15
0
#define C5 .06666666666666666666
16
17
namespace { // anonymous namespace
18
struct cass_data {
19
    double *en;
20
    double m0;
21
    bool hyperbolic;
22
};
23
} // anonymous namespace
24
25
0
static PJ_XY cass_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */
26
0
    PJ_XY xy = {0.0, 0.0};
27
0
    struct cass_data *Q = static_cast<struct cass_data *>(P->opaque);
28
29
0
    const double sinphi = sin(lp.phi);
30
0
    const double cosphi = cos(lp.phi);
31
0
    const double M = pj_mlfn(lp.phi, sinphi, cosphi, Q->en);
32
33
0
    const double nu_square = 1. / (1. - P->es * sinphi * sinphi);
34
0
    const double nu = sqrt(nu_square);
35
0
    const double tanphi = tan(lp.phi);
36
0
    const double T = tanphi * tanphi;
37
0
    const double A = lp.lam * cosphi;
38
0
    const double C = P->es * (cosphi * cosphi) / (1 - P->es);
39
0
    const double A2 = A * A;
40
41
0
    xy.x = nu * A * (1. - A2 * T * (C1 + (8. - T + 8. * C) * A2 * C2));
42
0
    xy.y = M - Q->m0 + nu * tanphi * A2 * (.5 + (5. - T + 6. * C) * A2 * C3);
43
0
    if (Q->hyperbolic) {
44
0
        const double rho = nu_square * (1. - P->es) * nu;
45
0
        xy.y -= xy.y * xy.y * xy.y / (6 * rho * nu);
46
0
    }
47
48
0
    return xy;
49
0
}
50
51
0
static PJ_XY cass_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
52
0
    PJ_XY xy = {0.0, 0.0};
53
0
    xy.x = asin(cos(lp.phi) * sin(lp.lam));
54
0
    xy.y = atan2(tan(lp.phi), cos(lp.lam)) - P->phi0;
55
0
    return xy;
56
0
}
57
58
0
static PJ_LP cass_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */
59
0
    PJ_LP lp = {0.0, 0.0};
60
0
    struct cass_data *Q = static_cast<struct cass_data *>(P->opaque);
61
62
0
    const double phi1 = pj_inv_mlfn(Q->m0 + xy.y, Q->en);
63
0
    const double tanphi1 = tan(phi1);
64
0
    const double T1 = tanphi1 * tanphi1;
65
0
    const double sinphi1 = sin(phi1);
66
0
    const double nu1_square = 1. / (1. - P->es * sinphi1 * sinphi1);
67
0
    const double nu1 = sqrt(nu1_square);
68
0
    const double rho1 = nu1_square * (1. - P->es) * nu1;
69
0
    const double D = xy.x / nu1;
70
0
    const double D2 = D * D;
71
0
    lp.phi =
72
0
        phi1 - (nu1 * tanphi1 / rho1) * D2 * (.5 - (1. + 3. * T1) * D2 * C3);
73
0
    lp.lam = D * (1. + T1 * D2 * (-C4 + (1. + 3. * T1) * D2 * C5)) / cos(phi1);
74
75
    // EPSG guidance note 7-2 suggests a custom approximation for the
76
    // 'Vanua Levu 1915 / Vanua Levu Grid' case, but better use the
77
    // generic inversion method
78
    // Actually use it in the non-hyperbolic case. It enables to make the
79
    // 5108.gie roundtripping tests to success, with at most 2 iterations.
80
0
    constexpr double deltaXYTolerance = 1e-12;
81
0
    lp = pj_generic_inverse_2d(xy, P, lp, deltaXYTolerance);
82
83
0
    return lp;
84
0
}
85
86
0
static PJ_LP cass_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
87
0
    PJ_LP lp = {0.0, 0.0};
88
0
    double dd;
89
0
    lp.phi = asin(sin(dd = xy.y + P->phi0) * cos(xy.x));
90
0
    lp.lam = atan2(tan(xy.x), cos(dd));
91
0
    return lp;
92
0
}
93
94
11
static PJ *pj_cass_destructor(PJ *P, int errlev) { /* Destructor */
95
11
    if (nullptr == P)
96
0
        return nullptr;
97
98
11
    if (nullptr == P->opaque)
99
0
        return pj_default_destructor(P, errlev);
100
101
11
    free(static_cast<struct cass_data *>(P->opaque)->en);
102
11
    return pj_default_destructor(P, errlev);
103
11
}
104
105
14
PJ *PJ_PROJECTION(cass) {
106
107
    /* Spheroidal? */
108
14
    if (0 == P->es) {
109
3
        P->inv = cass_s_inverse;
110
3
        P->fwd = cass_s_forward;
111
3
        return P;
112
3
    }
113
114
    /* otherwise it's ellipsoidal */
115
11
    auto Q =
116
11
        static_cast<struct cass_data *>(calloc(1, sizeof(struct cass_data)));
117
11
    P->opaque = Q;
118
11
    if (nullptr == P->opaque)
119
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
120
11
    P->destructor = pj_cass_destructor;
121
122
11
    Q->en = pj_enfn(P->n);
123
11
    if (nullptr == Q->en)
124
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
125
126
11
    Q->m0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en);
127
11
    if (pj_param_exists(P->params, "hyperbolic"))
128
5
        Q->hyperbolic = true;
129
11
    P->inv = cass_e_inverse;
130
11
    P->fwd = cass_e_forward;
131
132
11
    return P;
133
11
}
134
135
#undef C1
136
#undef C2
137
#undef C3
138
#undef C4
139
#undef C5