Coverage Report

Created: 2025-07-11 06:33

/src/PROJ/src/projections/poly.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(poly, "Polyconic (American)")
10
"\n\tConic, Sph&Ell";
11
12
namespace { // anonymous namespace
13
struct pj_poly_data {
14
    double ml0;
15
    double *en;
16
};
17
} // anonymous namespace
18
19
14.2k
#define TOL 1e-10
20
0
#define CONV 1e-10
21
0
#define N_ITER 10
22
0
#define I_ITER 20
23
0
#define ITOL 1.e-12
24
25
7.14k
static PJ_XY poly_e_forward(PJ_LP lp, PJ *P) { /* Ellipsoidal, forward */
26
7.14k
    PJ_XY xy = {0.0, 0.0};
27
7.14k
    struct pj_poly_data *Q = static_cast<struct pj_poly_data *>(P->opaque);
28
7.14k
    double ms, sp, cp;
29
30
7.14k
    if (fabs(lp.phi) <= TOL) {
31
0
        xy.x = lp.lam;
32
0
        xy.y = -Q->ml0;
33
7.14k
    } else {
34
7.14k
        sp = sin(lp.phi);
35
7.14k
        cp = cos(lp.phi);
36
7.14k
        ms = fabs(cp) > TOL ? pj_msfn(sp, cp, P->es) / sp : 0.;
37
7.14k
        lp.lam *= sp;
38
7.14k
        xy.x = ms * sin(lp.lam);
39
7.14k
        xy.y =
40
7.14k
            (pj_mlfn(lp.phi, sp, cp, Q->en) - Q->ml0) + ms * (1. - cos(lp.lam));
41
7.14k
    }
42
43
7.14k
    return xy;
44
7.14k
}
45
46
0
static PJ_XY poly_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
47
0
    PJ_XY xy = {0.0, 0.0};
48
0
    struct pj_poly_data *Q = static_cast<struct pj_poly_data *>(P->opaque);
49
50
0
    if (fabs(lp.phi) <= TOL) {
51
0
        xy.x = lp.lam;
52
0
        xy.y = Q->ml0;
53
0
    } else {
54
0
        const double cot = 1. / tan(lp.phi);
55
0
        const double E = lp.lam * sin(lp.phi);
56
0
        xy.x = sin(E) * cot;
57
0
        xy.y = lp.phi - P->phi0 + cot * (1. - cos(E));
58
0
    }
59
60
0
    return xy;
61
0
}
62
63
0
static PJ_LP poly_e_inverse(PJ_XY xy, PJ *P) { /* Ellipsoidal, inverse */
64
0
    PJ_LP lp = {0.0, 0.0};
65
0
    struct pj_poly_data *Q = static_cast<struct pj_poly_data *>(P->opaque);
66
67
0
    xy.y += Q->ml0;
68
0
    if (fabs(xy.y) <= TOL) {
69
0
        lp.lam = xy.x;
70
0
        lp.phi = 0.;
71
0
    } else {
72
0
        int i;
73
74
0
        const double r = xy.y * xy.y + xy.x * xy.x;
75
0
        lp.phi = xy.y;
76
0
        for (i = I_ITER; i; --i) {
77
0
            const double sp = sin(lp.phi);
78
0
            const double cp = cos(lp.phi);
79
0
            const double s2ph = sp * cp;
80
0
            if (fabs(cp) < ITOL) {
81
0
                proj_errno_set(
82
0
                    P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
83
0
                return lp;
84
0
            }
85
0
            double mlp = sqrt(1. - P->es * sp * sp);
86
0
            const double c = sp * mlp / cp;
87
0
            const double ml = pj_mlfn(lp.phi, sp, cp, Q->en);
88
0
            const double mlb = ml * ml + r;
89
0
            mlp = P->one_es / (mlp * mlp * mlp);
90
0
            const double dPhi =
91
0
                (ml + ml + c * mlb - 2. * xy.y * (c * ml + 1.)) /
92
0
                (P->es * s2ph * (mlb - 2. * xy.y * ml) / c +
93
0
                 2. * (xy.y - ml) * (c * mlp - 1. / s2ph) - mlp - mlp);
94
0
            lp.phi += dPhi;
95
0
            if (fabs(dPhi) <= ITOL)
96
0
                break;
97
0
        }
98
0
        if (!i) {
99
0
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
100
0
            return lp;
101
0
        }
102
0
        const double c = sin(lp.phi);
103
0
        lp.lam =
104
0
            asin(xy.x * tan(lp.phi) * sqrt(1. - P->es * c * c)) / sin(lp.phi);
105
0
    }
106
107
0
    return lp;
108
0
}
109
110
0
static PJ_LP poly_s_inverse(PJ_XY xy, PJ *P) { /* Spheroidal, inverse */
111
0
    PJ_LP lp = {0.0, 0.0};
112
113
0
    if (fabs(xy.y = P->phi0 + xy.y) <= TOL) {
114
0
        lp.lam = xy.x;
115
0
        lp.phi = 0.;
116
0
    } else {
117
0
        lp.phi = xy.y;
118
0
        const double B = xy.x * xy.x + xy.y * xy.y;
119
0
        int i = N_ITER;
120
0
        while (true) {
121
0
            const double tp = tan(lp.phi);
122
0
            const double dphi = (xy.y * (lp.phi * tp + 1.) - lp.phi -
123
0
                                 .5 * (lp.phi * lp.phi + B) * tp) /
124
0
                                ((lp.phi - xy.y) / tp - 1.);
125
0
            lp.phi -= dphi;
126
0
            if (!(fabs(dphi) > CONV))
127
0
                break;
128
0
            --i;
129
0
            if (i == 0) {
130
0
                proj_errno_set(
131
0
                    P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
132
0
                return lp;
133
0
            }
134
0
        }
135
0
        lp.lam = asin(xy.x * tan(lp.phi)) / sin(lp.phi);
136
0
    }
137
138
0
    return lp;
139
0
}
140
141
131
static PJ *pj_poly_destructor(PJ *P, int errlev) {
142
131
    if (nullptr == P)
143
0
        return nullptr;
144
145
131
    if (nullptr == P->opaque)
146
0
        return pj_default_destructor(P, errlev);
147
148
131
    if (static_cast<struct pj_poly_data *>(P->opaque)->en)
149
121
        free(static_cast<struct pj_poly_data *>(P->opaque)->en);
150
151
131
    return pj_default_destructor(P, errlev);
152
131
}
153
154
131
PJ *PJ_PROJECTION(poly) {
155
131
    struct pj_poly_data *Q = static_cast<struct pj_poly_data *>(
156
131
        calloc(1, sizeof(struct pj_poly_data)));
157
131
    if (nullptr == Q)
158
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
159
160
131
    P->opaque = Q;
161
131
    P->destructor = pj_poly_destructor;
162
163
131
    if (P->es != 0.0) {
164
121
        if (!(Q->en = pj_enfn(P->n)))
165
0
            return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
166
121
        Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en);
167
121
        P->inv = poly_e_inverse;
168
121
        P->fwd = poly_e_forward;
169
121
    } else {
170
10
        Q->ml0 = -P->phi0;
171
10
        P->inv = poly_s_inverse;
172
10
        P->fwd = poly_s_forward;
173
10
    }
174
175
131
    return P;
176
131
}
177
178
#undef TOL
179
#undef CONV
180
#undef N_ITER
181
#undef I_ITER
182
#undef ITOL