Coverage Report

Created: 2025-06-13 06:29

/src/proj/src/projections/mod_ster.cpp
Line
Count
Source (jump to first uncovered line)
1
/* based upon Snyder and Linck, USGS-NMD */
2
3
#include "proj.h"
4
#include "proj_internal.h"
5
#include <errno.h>
6
#include <math.h>
7
8
PROJ_HEAD(mil_os, "Miller Oblated Stereographic") "\n\tAzi(mod)";
9
PROJ_HEAD(lee_os, "Lee Oblated Stereographic") "\n\tAzi(mod)";
10
PROJ_HEAD(gs48, "Modified Stereographic of 48 U.S.") "\n\tAzi(mod)";
11
PROJ_HEAD(alsk, "Modified Stereographic of Alaska") "\n\tAzi(mod)";
12
PROJ_HEAD(gs50, "Modified Stereographic of 50 U.S.") "\n\tAzi(mod)";
13
14
0
#define EPSLN 1e-12
15
16
namespace { // anonymous namespace
17
struct pj_mod_ster_data {
18
    const COMPLEX *zcoeff;
19
    double cchio, schio;
20
    int n;
21
};
22
} // anonymous namespace
23
24
0
static PJ_XY mod_ster_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */
25
0
    PJ_XY xy = {0.0, 0.0};
26
0
    struct pj_mod_ster_data *Q =
27
0
        static_cast<struct pj_mod_ster_data *>(P->opaque);
28
0
    double sinlon, coslon, esphi, chi, schi, cchi, s;
29
0
    COMPLEX p;
30
31
0
    sinlon = sin(lp.lam);
32
0
    coslon = cos(lp.lam);
33
0
    esphi = P->e * sin(lp.phi);
34
0
    chi = 2. * atan(tan((M_HALFPI + lp.phi) * .5) *
35
0
                    pow((1. - esphi) / (1. + esphi), P->e * .5)) -
36
0
          M_HALFPI;
37
0
    schi = sin(chi);
38
0
    cchi = cos(chi);
39
0
    const double denom = 1. + Q->schio * schi + Q->cchio * cchi * coslon;
40
0
    if (denom == 0) {
41
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
42
0
        return xy;
43
0
    }
44
0
    s = 2. / denom;
45
0
    p.r = s * cchi * sinlon;
46
0
    p.i = s * (Q->cchio * schi - Q->schio * cchi * coslon);
47
0
    p = pj_zpoly1(p, Q->zcoeff, Q->n);
48
0
    xy.x = p.r;
49
0
    xy.y = p.i;
50
51
0
    return xy;
52
0
}
53
54
0
static PJ_LP mod_ster_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */
55
0
    PJ_LP lp = {0.0, 0.0};
56
0
    struct pj_mod_ster_data *Q =
57
0
        static_cast<struct pj_mod_ster_data *>(P->opaque);
58
0
    int nn;
59
0
    COMPLEX p, fxy, fpxy, dp;
60
0
    double den, rh = 0.0, z, sinz = 0.0, cosz = 0.0, chi, phi = 0.0, esphi;
61
62
0
    p.r = xy.x;
63
0
    p.i = xy.y;
64
0
    for (nn = 20; nn; --nn) {
65
0
        fxy = pj_zpolyd1(p, Q->zcoeff, Q->n, &fpxy);
66
0
        fxy.r -= xy.x;
67
0
        fxy.i -= xy.y;
68
0
        den = fpxy.r * fpxy.r + fpxy.i * fpxy.i;
69
0
        dp.r = -(fxy.r * fpxy.r + fxy.i * fpxy.i) / den;
70
0
        dp.i = -(fxy.i * fpxy.r - fxy.r * fpxy.i) / den;
71
0
        p.r += dp.r;
72
0
        p.i += dp.i;
73
0
        if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN)
74
0
            break;
75
0
    }
76
0
    if (nn) {
77
0
        rh = hypot(p.r, p.i);
78
0
        z = 2. * atan(.5 * rh);
79
0
        sinz = sin(z);
80
0
        cosz = cos(z);
81
0
        if (fabs(rh) <= EPSLN) {
82
            /* if we end up here input coordinates were (0,0).
83
             * pj_inv() adds P->lam0 to lp.lam, this way we are
84
             * sure to get the correct offset */
85
0
            lp.lam = 0.0;
86
0
            lp.phi = P->phi0;
87
0
            return lp;
88
0
        }
89
0
        chi = aasin(P->ctx, cosz * Q->schio + p.i * sinz * Q->cchio / rh);
90
0
        phi = chi;
91
0
        for (nn = 20; nn; --nn) {
92
0
            double dphi;
93
0
            esphi = P->e * sin(phi);
94
0
            dphi = 2. * atan(tan((M_HALFPI + chi) * .5) *
95
0
                             pow((1. + esphi) / (1. - esphi), P->e * .5)) -
96
0
                   M_HALFPI - phi;
97
0
            phi += dphi;
98
0
            if (fabs(dphi) <= EPSLN)
99
0
                break;
100
0
        }
101
0
    }
102
0
    if (nn) {
103
0
        lp.phi = phi;
104
0
        lp.lam =
105
0
            atan2(p.r * sinz, rh * Q->cchio * cosz - p.i * Q->schio * sinz);
106
0
    } else
107
0
        lp.lam = lp.phi = HUGE_VAL;
108
0
    return lp;
109
0
}
110
111
571
static PJ *mod_ster_setup(PJ *P) { /* general initialization */
112
571
    struct pj_mod_ster_data *Q =
113
571
        static_cast<struct pj_mod_ster_data *>(P->opaque);
114
571
    double esphi, chio;
115
116
571
    if (P->es != 0.0) {
117
195
        esphi = P->e * sin(P->phi0);
118
195
        chio = 2. * atan(tan((M_HALFPI + P->phi0) * .5) *
119
195
                         pow((1. - esphi) / (1. + esphi), P->e * .5)) -
120
195
               M_HALFPI;
121
195
    } else
122
376
        chio = P->phi0;
123
571
    Q->schio = sin(chio);
124
571
    Q->cchio = cos(chio);
125
571
    P->inv = mod_ster_e_inverse;
126
571
    P->fwd = mod_ster_e_forward;
127
128
571
    return P;
129
571
}
130
131
/* Miller Oblated Stereographic */
132
217
PJ *PJ_PROJECTION(mil_os) {
133
217
    static const COMPLEX AB[] = {{0.924500, 0.}, {0., 0.}, {0.019430, 0.}};
134
135
217
    struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>(
136
217
        calloc(1, sizeof(struct pj_mod_ster_data)));
137
217
    if (nullptr == Q)
138
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
139
217
    P->opaque = Q;
140
141
217
    Q->n = 2;
142
217
    P->lam0 = DEG_TO_RAD * 20.;
143
217
    P->phi0 = DEG_TO_RAD * 18.;
144
217
    Q->zcoeff = AB;
145
217
    P->es = 0.;
146
147
217
    return mod_ster_setup(P);
148
217
}
149
150
/* Lee Oblated Stereographic */
151
10
PJ *PJ_PROJECTION(lee_os) {
152
10
    static const COMPLEX AB[] = {
153
10
        {0.721316, 0.}, {0., 0.}, {-0.0088162, -0.00617325}};
154
155
10
    struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>(
156
10
        calloc(1, sizeof(struct pj_mod_ster_data)));
157
10
    if (nullptr == Q)
158
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
159
10
    P->opaque = Q;
160
161
10
    Q->n = 2;
162
10
    P->lam0 = DEG_TO_RAD * -165.;
163
10
    P->phi0 = DEG_TO_RAD * -10.;
164
10
    Q->zcoeff = AB;
165
10
    P->es = 0.;
166
167
10
    return mod_ster_setup(P);
168
10
}
169
170
19
PJ *PJ_PROJECTION(gs48) {
171
19
    static const COMPLEX /* 48 United States */
172
19
        AB[] = {
173
19
            {0.98879, 0.}, {0., 0.}, {-0.050909, 0.}, {0., 0.}, {0.075528, 0.}};
174
175
19
    struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>(
176
19
        calloc(1, sizeof(struct pj_mod_ster_data)));
177
19
    if (nullptr == Q)
178
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
179
19
    P->opaque = Q;
180
181
19
    Q->n = 4;
182
19
    P->lam0 = DEG_TO_RAD * -96.;
183
19
    P->phi0 = DEG_TO_RAD * 39.;
184
19
    Q->zcoeff = AB;
185
19
    P->es = 0.;
186
19
    P->a = 6370997.;
187
188
19
    return mod_ster_setup(P);
189
19
}
190
191
174
PJ *PJ_PROJECTION(alsk) {
192
174
    static const COMPLEX ABe[] = {
193
        /* Alaska ellipsoid */
194
174
        {.9945303, 0.},         {.0052083, -.0027404}, {.0072721, .0048181},
195
174
        {-.0151089, -.1932526}, {.0642675, -.1381226}, {.3582802, -.2884586},
196
174
    };
197
198
174
    static const COMPLEX ABs[] = {/* Alaska sphere */
199
174
                                  {.9972523, 0.},        {.0052513, -.0041175},
200
174
                                  {.0074606, .0048125},  {-.0153783, -.1968253},
201
174
                                  {.0636871, -.1408027}, {.3660976, -.2937382}};
202
203
174
    struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>(
204
174
        calloc(1, sizeof(struct pj_mod_ster_data)));
205
174
    if (nullptr == Q)
206
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
207
174
    P->opaque = Q;
208
209
174
    Q->n = 5;
210
174
    P->lam0 = DEG_TO_RAD * -152.;
211
174
    P->phi0 = DEG_TO_RAD * 64.;
212
174
    if (P->es != 0.0) { /* fixed ellipsoid/sphere */
213
105
        Q->zcoeff = ABe;
214
105
        P->a = 6378206.4;
215
105
        P->e = sqrt(P->es = 0.00676866);
216
105
    } else {
217
69
        Q->zcoeff = ABs;
218
69
        P->a = 6370997.;
219
69
    }
220
221
174
    return mod_ster_setup(P);
222
174
}
223
224
151
PJ *PJ_PROJECTION(gs50) {
225
151
    static const COMPLEX ABe[] = {
226
        /* GS50 ellipsoid */
227
151
        {.9827497, 0.},         {.0210669, .0053804},  {-.1031415, -.0571664},
228
151
        {-.0323337, -.0322847}, {.0502303, .1211983},  {.0251805, .0895678},
229
151
        {-.0012315, -.1416121}, {.0072202, -.1317091}, {-.0194029, .0759677},
230
151
        {-.0210072, .0834037}};
231
232
151
    static const COMPLEX ABs[] = {
233
        /* GS50 sphere */
234
151
        {.9842990, 0.},         {.0211642, .0037608},  {-.1036018, -.0575102},
235
151
        {-.0329095, -.0320119}, {.0499471, .1223335},  {.0260460, .0899805},
236
151
        {.0007388, -.1435792},  {.0075848, -.1334108}, {-.0216473, .0776645},
237
151
        {-.0225161, .0853673}};
238
239
151
    struct pj_mod_ster_data *Q = static_cast<struct pj_mod_ster_data *>(
240
151
        calloc(1, sizeof(struct pj_mod_ster_data)));
241
151
    if (nullptr == Q)
242
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
243
151
    P->opaque = Q;
244
245
151
    Q->n = 9;
246
151
    P->lam0 = DEG_TO_RAD * -120.;
247
151
    P->phi0 = DEG_TO_RAD * 45.;
248
151
    if (P->es != 0.0) { /* fixed ellipsoid/sphere */
249
90
        Q->zcoeff = ABe;
250
90
        P->a = 6378206.4;
251
90
        P->e = sqrt(P->es = 0.00676866);
252
90
    } else {
253
61
        Q->zcoeff = ABs;
254
61
        P->a = 6370997.;
255
61
    }
256
257
151
    return mod_ster_setup(P);
258
151
}
259
260
#undef EPSLN