Coverage Report

Created: 2025-07-12 06:32

/src/PROJ/src/projections/sconics.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "proj.h"
3
#include "proj_internal.h"
4
#include <errno.h>
5
#include <math.h>
6
7
namespace pj_sconics_ns {
8
enum Type {
9
    EULER = 0,
10
    MURD1 = 1,
11
    MURD2 = 2,
12
    MURD3 = 3,
13
    PCONIC = 4,
14
    TISSOT = 5,
15
    VITK1 = 6
16
};
17
}
18
19
namespace { // anonymous namespace
20
struct pj_sconics_data {
21
    double n;
22
    double rho_c;
23
    double rho_0;
24
    double sig;
25
    double c1, c2;
26
    enum pj_sconics_ns::Type type;
27
};
28
} // anonymous namespace
29
30
8
#define EPS10 1.e-10
31
143
#define EPS 1e-10
32
#define LINE2 "\n\tConic, Sph\n\tlat_1= and lat_2="
33
34
PROJ_HEAD(euler, "Euler") LINE2;
35
PROJ_HEAD(murd1, "Murdoch I") LINE2;
36
PROJ_HEAD(murd2, "Murdoch II") LINE2;
37
PROJ_HEAD(murd3, "Murdoch III") LINE2;
38
PROJ_HEAD(pconic, "Perspective Conic") LINE2;
39
PROJ_HEAD(tissot, "Tissot") LINE2;
40
PROJ_HEAD(vitk1, "Vitkovsky I") LINE2;
41
42
/* get common factors for simple conics */
43
64
static int phi12(PJ *P, double *del) {
44
64
    double p1, p2;
45
64
    int err = 0;
46
47
64
    if (!pj_param(P->ctx, P->params, "tlat_1").i) {
48
11
        proj_log_error(P, _("Missing parameter: lat_1 should be specified"));
49
11
        err = PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE;
50
53
    } else if (!pj_param(P->ctx, P->params, "tlat_2").i) {
51
5
        proj_log_error(P, _("Missing parameter: lat_2 should be specified"));
52
5
        err = PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE;
53
48
    } else {
54
48
        p1 = pj_param(P->ctx, P->params, "rlat_1").f;
55
48
        p2 = pj_param(P->ctx, P->params, "rlat_2").f;
56
48
        *del = 0.5 * (p2 - p1);
57
48
        const double sig = 0.5 * (p2 + p1);
58
48
        static_cast<struct pj_sconics_data *>(P->opaque)->sig = sig;
59
48
        err = (fabs(*del) < EPS || fabs(sig) < EPS)
60
48
                  ? PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE
61
48
                  : 0;
62
48
        if (err) {
63
1
            proj_log_error(
64
1
                P, _("Illegal value for lat_1 and lat_2: |lat_1 - lat_2| "
65
1
                     "and |lat_1 + lat_2| should be > 0"));
66
1
        }
67
48
    }
68
64
    return err;
69
64
}
70
71
0
static PJ_XY sconics_s_forward(PJ_LP lp, PJ *P) { /* Spheroidal, forward */
72
0
    PJ_XY xy = {0.0, 0.0};
73
0
    struct pj_sconics_data *Q =
74
0
        static_cast<struct pj_sconics_data *>(P->opaque);
75
0
    double rho;
76
77
0
    switch (Q->type) {
78
0
    case pj_sconics_ns::MURD2:
79
0
        rho = Q->rho_c + tan(Q->sig - lp.phi);
80
0
        break;
81
0
    case pj_sconics_ns::PCONIC:
82
0
        rho = Q->c2 * (Q->c1 - tan(lp.phi - Q->sig));
83
0
        break;
84
0
    default:
85
0
        rho = Q->rho_c - lp.phi;
86
0
        break;
87
0
    }
88
89
0
    xy.x = rho * sin(lp.lam *= Q->n);
90
0
    xy.y = Q->rho_0 - rho * cos(lp.lam);
91
0
    return xy;
92
0
}
93
94
static PJ_LP
95
sconics_s_inverse(PJ_XY xy,
96
0
                  PJ *P) { /* Spheroidal, (and ellipsoidal?) inverse */
97
0
    PJ_LP lp = {0.0, 0.0};
98
0
    struct pj_sconics_data *Q =
99
0
        static_cast<struct pj_sconics_data *>(P->opaque);
100
0
    double rho;
101
102
0
    xy.y = Q->rho_0 - xy.y;
103
0
    rho = hypot(xy.x, xy.y);
104
0
    if (Q->n < 0.) {
105
0
        rho = -rho;
106
0
        xy.x = -xy.x;
107
0
        xy.y = -xy.y;
108
0
    }
109
110
0
    lp.lam = atan2(xy.x, xy.y) / Q->n;
111
112
0
    switch (Q->type) {
113
0
    case pj_sconics_ns::PCONIC:
114
0
        lp.phi = atan(Q->c1 - rho / Q->c2) + Q->sig;
115
0
        break;
116
0
    case pj_sconics_ns::MURD2:
117
0
        lp.phi = Q->sig - atan(rho - Q->rho_c);
118
0
        break;
119
0
    default:
120
0
        lp.phi = Q->rho_c - rho;
121
0
    }
122
0
    return lp;
123
0
}
124
125
64
static PJ *pj_sconics_setup(PJ *P, enum pj_sconics_ns::Type type) {
126
64
    double del, cs;
127
64
    int err;
128
64
    struct pj_sconics_data *Q = static_cast<struct pj_sconics_data *>(
129
64
        calloc(1, sizeof(struct pj_sconics_data)));
130
64
    if (nullptr == Q)
131
0
        return pj_default_destructor(P, PROJ_ERR_OTHER /*ENOMEM*/);
132
64
    P->opaque = Q;
133
64
    Q->type = type;
134
135
64
    err = phi12(P, &del);
136
64
    if (err)
137
17
        return pj_default_destructor(P, err);
138
139
47
    switch (Q->type) {
140
141
0
    case pj_sconics_ns::TISSOT:
142
0
        Q->n = sin(Q->sig);
143
0
        cs = cos(del);
144
0
        Q->rho_c = Q->n / cs + cs / Q->n;
145
0
        Q->rho_0 = sqrt((Q->rho_c - 2 * sin(P->phi0)) / Q->n);
146
0
        break;
147
148
3
    case pj_sconics_ns::MURD1:
149
3
        Q->rho_c = sin(del) / (del * tan(Q->sig)) + Q->sig;
150
3
        Q->rho_0 = Q->rho_c - P->phi0;
151
3
        Q->n = sin(Q->sig);
152
3
        break;
153
154
12
    case pj_sconics_ns::MURD2:
155
12
        Q->rho_c = (cs = sqrt(cos(del))) / tan(Q->sig);
156
12
        Q->rho_0 = Q->rho_c + tan(Q->sig - P->phi0);
157
12
        Q->n = sin(Q->sig) * cs;
158
12
        break;
159
160
11
    case pj_sconics_ns::MURD3:
161
11
        Q->rho_c = del / (tan(Q->sig) * tan(del)) + Q->sig;
162
11
        Q->rho_0 = Q->rho_c - P->phi0;
163
11
        Q->n = sin(Q->sig) * sin(del) * tan(del) / (del * del);
164
11
        break;
165
166
9
    case pj_sconics_ns::EULER:
167
9
        Q->n = sin(Q->sig) * sin(del) / del;
168
9
        del *= 0.5;
169
9
        Q->rho_c = del / (tan(del) * tan(Q->sig)) + Q->sig;
170
9
        Q->rho_0 = Q->rho_c - P->phi0;
171
9
        break;
172
173
8
    case pj_sconics_ns::PCONIC:
174
8
        Q->n = sin(Q->sig);
175
8
        Q->c2 = cos(del);
176
8
        Q->c1 = 1. / tan(Q->sig);
177
8
        del = P->phi0 - Q->sig;
178
8
        if (fabs(del) - EPS10 >= M_HALFPI) {
179
2
            proj_log_error(
180
2
                P, _("Invalid value for lat_0/lat_1/lat_2: |lat_0 - 0.5 * "
181
2
                     "(lat_1 + lat_2)| should be < 90°"));
182
2
            return pj_default_destructor(P,
183
2
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
184
2
        }
185
6
        Q->rho_0 = Q->c2 * (Q->c1 - tan(del));
186
6
        break;
187
188
4
    case pj_sconics_ns::VITK1:
189
4
        cs = tan(del);
190
4
        Q->n = cs * sin(Q->sig) / del;
191
4
        Q->rho_c = del / (cs * tan(Q->sig)) + Q->sig;
192
4
        Q->rho_0 = Q->rho_c - P->phi0;
193
4
        break;
194
47
    }
195
196
45
    P->inv = sconics_s_inverse;
197
45
    P->fwd = sconics_s_forward;
198
45
    P->es = 0;
199
45
    return (P);
200
47
}
201
202
10
PJ *PJ_PROJECTION(euler) { return pj_sconics_setup(P, pj_sconics_ns::EULER); }
203
204
3
PJ *PJ_PROJECTION(tissot) { return pj_sconics_setup(P, pj_sconics_ns::TISSOT); }
205
206
3
PJ *PJ_PROJECTION(murd1) { return pj_sconics_setup(P, pj_sconics_ns::MURD1); }
207
208
15
PJ *PJ_PROJECTION(murd2) { return pj_sconics_setup(P, pj_sconics_ns::MURD2); }
209
210
13
PJ *PJ_PROJECTION(murd3) { return pj_sconics_setup(P, pj_sconics_ns::MURD3); }
211
212
12
PJ *PJ_PROJECTION(pconic) { return pj_sconics_setup(P, pj_sconics_ns::PCONIC); }
213
214
8
PJ *PJ_PROJECTION(vitk1) { return pj_sconics_setup(P, pj_sconics_ns::VITK1); }
215
216
#undef EPS10
217
#undef EPS
218
#undef LINE2