Coverage Report

Created: 2025-11-09 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/PROJ/src/projections/gn_sinu.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(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
1.76k
#define MAX_ITER 8
16
6.65k
#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
3.48k
static PJ_XY gn_sinu_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
56
3.48k
    PJ_XY xy = {0.0, 0.0};
57
3.48k
    struct pj_gn_sinu_data *Q =
58
3.48k
        static_cast<struct pj_gn_sinu_data *>(P->opaque);
59
60
3.48k
    if (Q->m == 0.0)
61
1.71k
        lp.phi = Q->n != 1. ? aasin(P->ctx, Q->n * sin(lp.phi)) : lp.phi;
62
1.76k
    else {
63
1.76k
        int i;
64
65
1.76k
        const double k = Q->n * sin(lp.phi);
66
6.65k
        for (i = MAX_ITER; i; --i) {
67
6.65k
            const double V =
68
6.65k
                (Q->m * lp.phi + sin(lp.phi) - k) / (Q->m + cos(lp.phi));
69
6.65k
            lp.phi -= V;
70
6.65k
            if (fabs(V) < LOOP_TOL)
71
1.76k
                break;
72
6.65k
        }
73
1.76k
        if (!i) {
74
0
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
75
0
            return xy;
76
0
        }
77
1.76k
    }
78
3.48k
    xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi));
79
3.48k
    xy.y = Q->C_y * lp.phi;
80
81
3.48k
    return xy;
82
3.48k
}
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
780
static PJ *pj_gn_sinu_destructor(PJ *P, int errlev) { /* Destructor */
98
780
    if (nullptr == P)
99
0
        return nullptr;
100
101
780
    if (nullptr == P->opaque)
102
0
        return pj_default_destructor(P, errlev);
103
104
780
    free(static_cast<struct pj_gn_sinu_data *>(P->opaque)->en);
105
780
    return pj_default_destructor(P, errlev);
106
780
}
107
108
/* for spheres, only */
109
777
static void pj_gn_sinu_setup(PJ *P) {
110
777
    struct pj_gn_sinu_data *Q =
111
777
        static_cast<struct pj_gn_sinu_data *>(P->opaque);
112
777
    P->es = 0;
113
777
    P->inv = gn_sinu_s_inverse;
114
777
    P->fwd = gn_sinu_s_forward;
115
116
777
    Q->C_y = sqrt((Q->m + 1.) / Q->n);
117
777
    Q->C_x = Q->C_y / (Q->m + 1.);
118
777
}
119
120
747
PJ *PJ_PROJECTION(sinu) {
121
747
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
122
747
        calloc(1, sizeof(struct pj_gn_sinu_data)));
123
747
    if (nullptr == Q)
124
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
125
747
    P->opaque = Q;
126
747
    P->destructor = pj_gn_sinu_destructor;
127
128
747
    if (!(Q->en = pj_enfn(P->n)))
129
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
130
131
747
    if (P->es != 0.0) {
132
3
        P->inv = gn_sinu_e_inverse;
133
3
        P->fwd = gn_sinu_e_forward;
134
744
    } else {
135
744
        Q->n = 1.;
136
744
        Q->m = 0.;
137
744
        pj_gn_sinu_setup(P);
138
744
    }
139
747
    return P;
140
747
}
141
142
30
PJ *PJ_PROJECTION(eck6) {
143
30
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
144
30
        calloc(1, sizeof(struct pj_gn_sinu_data)));
145
30
    if (nullptr == Q)
146
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
147
30
    P->opaque = Q;
148
30
    P->destructor = pj_gn_sinu_destructor;
149
150
30
    Q->m = 1.;
151
30
    Q->n = 2.570796326794896619231321691;
152
30
    pj_gn_sinu_setup(P);
153
154
30
    return P;
155
30
}
156
157
3
PJ *PJ_PROJECTION(mbtfps) {
158
3
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
159
3
        calloc(1, sizeof(struct pj_gn_sinu_data)));
160
3
    if (nullptr == Q)
161
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
162
3
    P->opaque = Q;
163
3
    P->destructor = pj_gn_sinu_destructor;
164
165
3
    Q->m = 0.5;
166
3
    Q->n = 1.785398163397448309615660845;
167
3
    pj_gn_sinu_setup(P);
168
169
3
    return P;
170
3
}
171
172
4
PJ *PJ_PROJECTION(gn_sinu) {
173
4
    struct pj_gn_sinu_data *Q = static_cast<struct pj_gn_sinu_data *>(
174
4
        calloc(1, sizeof(struct pj_gn_sinu_data)));
175
4
    if (nullptr == Q)
176
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
177
4
    P->opaque = Q;
178
4
    P->destructor = pj_gn_sinu_destructor;
179
180
4
    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
3
    if (!pj_param(P->ctx, P->params, "tm").i) {
185
1
        proj_log_error(P, _("Missing parameter m."));
186
1
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_MISSING_ARG);
187
1
    }
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
2
        proj_log_error(P, _("Invalid value for n: it should be > 0."));
193
2
        return pj_default_destructor(P, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
194
2
    }
195
0
    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
0
    pj_gn_sinu_setup(P);
201
202
0
    return P;
203
0
}