Coverage Report

Created: 2025-07-11 06:33

/src/PROJ/src/projections/gn_sinu.cpp
Line
Count
Source (jump to first uncovered line)
1
2
3
#include <errno.h>
4
#include <math.h>
5
6
#include "proj.h"
7
#include "proj_internal.h"
8
9
PROJ_HEAD(gn_sinu, "General Sinusoidal Series") "\n\tPCyl, Sph\n\tm= n=";
10
PROJ_HEAD(sinu, "Sinusoidal (Sanson-Flamsteed)") "\n\tPCyl, Sph&Ell";
11
PROJ_HEAD(eck6, "Eckert VI") "\n\tPCyl, Sph";
12
PROJ_HEAD(mbtfps, "McBryde-Thomas Flat-Polar Sinusoidal") "\n\tPCyl, Sph";
13
14
0
#define EPS10 1e-10
15
840
#define MAX_ITER 8
16
3.12k
#define LOOP_TOL 1e-7
17
18
namespace { // anonymous namespace
19
struct pj_gn_sinu_data {
20
    double *en;
21
    double m, n, C_x, C_y;
22
};
23
} // anonymous namespace
24
25
0
static PJ_XY gn_sinu_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */
26
0
    PJ_XY xy = {0.0, 0.0};
27
28
0
    const double s = sin(lp.phi);
29
0
    const double c = cos(lp.phi);
30
0
    xy.y = pj_mlfn(lp.phi, s, c,
31
0
                   static_cast<struct pj_gn_sinu_data *>(P->opaque)->en);
32
0
    xy.x = lp.lam * c / sqrt(1. - P->es * s * s);
33
0
    return xy;
34
0
}
35
36
0
static PJ_LP gn_sinu_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */
37
0
    PJ_LP lp = {0.0, 0.0};
38
0
    double s;
39
40
0
    lp.phi =
41
0
        pj_inv_mlfn(xy.y, static_cast<struct pj_gn_sinu_data *>(P->opaque)->en);
42
0
    s = fabs(lp.phi);
43
0
    if (s < M_HALFPI) {
44
0
        s = sin(lp.phi);
45
0
        lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi);
46
0
    } else if ((s - EPS10) < M_HALFPI) {
47
0
        lp.lam = 0.;
48
0
    } else {
49
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
50
0
    }
51
52
0
    return lp;
53
0
}
54
55
2.53k
static PJ_XY gn_sinu_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
56
2.53k
    PJ_XY xy = {0.0, 0.0};
57
2.53k
    struct pj_gn_sinu_data *Q =
58
2.53k
        static_cast<struct pj_gn_sinu_data *>(P->opaque);
59
60
2.53k
    if (Q->m == 0.0)
61
1.69k
        lp.phi = Q->n != 1. ? aasin(P->ctx, Q->n * sin(lp.phi)) : lp.phi;
62
840
    else {
63
840
        int i;
64
65
840
        const double k = Q->n * sin(lp.phi);
66
3.12k
        for (i = MAX_ITER; i; --i) {
67
3.12k
            const double V =
68
3.12k
                (Q->m * lp.phi + sin(lp.phi) - k) / (Q->m + cos(lp.phi));
69
3.12k
            lp.phi -= V;
70
3.12k
            if (fabs(V) < LOOP_TOL)
71
840
                break;
72
3.12k
        }
73
840
        if (!i) {
74
0
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
75
0
            return xy;
76
0
        }
77
840
    }
78
2.53k
    xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi));
79
2.53k
    xy.y = Q->C_y * lp.phi;
80
81
2.53k
    return xy;
82
2.53k
}
83
84
0
static PJ_LP gn_sinu_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
85
0
    PJ_LP lp = {0.0, 0.0};
86
0
    struct pj_gn_sinu_data *Q =
87
0
        static_cast<struct pj_gn_sinu_data *>(P->opaque);
88
89
0
    xy.y /= Q->C_y;
90
0
    lp.phi = (Q->m != 0.0)
91
0
                 ? aasin(P->ctx, (Q->m * xy.y + sin(xy.y)) / Q->n)
92
0
                 : (Q->n != 1. ? aasin(P->ctx, sin(xy.y) / Q->n) : xy.y);
93
0
    lp.lam = xy.x / (Q->C_x * (Q->m + cos(xy.y)));
94
0
    return lp;
95
0
}
96
97
631
static PJ *pj_gn_sinu_destructor(PJ *P, int errlev) { /* Destructor */
98
631
    if (nullptr == P)
99
0
        return nullptr;
100
101
631
    if (nullptr == P->opaque)
102
0
        return pj_default_destructor(P, errlev);
103
104
631
    free(static_cast<struct pj_gn_sinu_data *>(P->opaque)->en);
105
631
    return pj_default_destructor(P, errlev);
106
631
}
107
108
/* for spheres, only */
109
625
static void pj_gn_sinu_setup(PJ *P) {
110
625
    struct pj_gn_sinu_data *Q =
111
625
        static_cast<struct pj_gn_sinu_data *>(P->opaque);
112
625
    P->es = 0;
113
625
    P->inv = gn_sinu_s_inverse;
114
625
    P->fwd = gn_sinu_s_forward;
115
116
625
    Q->C_y = sqrt((Q->m + 1.) / Q->n);
117
625
    Q->C_x = Q->C_y / (Q->m + 1.);
118
625
}
119
120
608
PJ *PJ_PROJECTION(sinu) {
121
608
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
122
608
        calloc(1, sizeof(struct pj_gn_sinu_data)));
123
608
    if (nullptr == Q)
124
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
125
608
    P->opaque = Q;
126
608
    P->destructor = pj_gn_sinu_destructor;
127
128
608
    if (!(Q->en = pj_enfn(P->n)))
129
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
130
131
608
    if (P->es != 0.0) {
132
6
        P->inv = gn_sinu_e_inverse;
133
6
        P->fwd = gn_sinu_e_forward;
134
602
    } else {
135
602
        Q->n = 1.;
136
602
        Q->m = 0.;
137
602
        pj_gn_sinu_setup(P);
138
602
    }
139
608
    return P;
140
608
}
141
142
19
PJ *PJ_PROJECTION(eck6) {
143
19
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
144
19
        calloc(1, sizeof(struct pj_gn_sinu_data)));
145
19
    if (nullptr == Q)
146
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
147
19
    P->opaque = Q;
148
19
    P->destructor = pj_gn_sinu_destructor;
149
150
19
    Q->m = 1.;
151
19
    Q->n = 2.570796326794896619231321691;
152
19
    pj_gn_sinu_setup(P);
153
154
19
    return P;
155
19
}
156
157
2
PJ *PJ_PROJECTION(mbtfps) {
158
2
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
159
2
        calloc(1, sizeof(struct pj_gn_sinu_data)));
160
2
    if (nullptr == Q)
161
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
162
2
    P->opaque = Q;
163
2
    P->destructor = pj_gn_sinu_destructor;
164
165
2
    Q->m = 0.5;
166
2
    Q->n = 1.785398163397448309615660845;
167
2
    pj_gn_sinu_setup(P);
168
169
2
    return P;
170
2
}
171
172
6
PJ *PJ_PROJECTION(gn_sinu) {
173
6
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
174
6
        calloc(1, sizeof(struct pj_gn_sinu_data)));
175
6
    if (nullptr == Q)
176
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
177
6
    P->opaque = Q;
178
6
    P->destructor = pj_gn_sinu_destructor;
179
180
6
    if (!pj_param(P->ctx, P->params, "tn").i) {
181
1
        proj_log_error(P, _("Missing parameter n."));
182
1
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_MISSING_ARG);
183
1
    }
184
5
    if (!pj_param(P->ctx, P->params, "tm").i) {
185
3
        proj_log_error(P, _("Missing parameter m."));
186
3
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_MISSING_ARG);
187
3
    }
188
189
2
    Q->n = pj_param(P->ctx, P->params, "dn").f;
190
2
    Q->m = pj_param(P->ctx, P->params, "dm").f;
191
2
    if (Q->n <= 0) {
192
0
        proj_log_error(P, _("Invalid value for n: it should be > 0."));
193
0
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
194
0
    }
195
2
    if (Q->m < 0) {
196
0
        proj_log_error(P, _("Invalid value for m: it should be >= 0."));
197
0
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
198
0
    }
199
200
2
    pj_gn_sinu_setup(P);
201
202
2
    return P;
203
2
}