Coverage Report

Created: 2026-04-01 06:20

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