Coverage Report

Created: 2025-07-18 07:18

/src/PROJ/src/fwd.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Project:  PROJ.4
3
 * Purpose:  Forward operation invocation
4
 * Author:   Thomas Knudsen,  thokn@sdfe.dk,  2018-01-02
5
 *           Based on material from Gerald Evenden (original pj_fwd)
6
 *           and Piyush Agram (original pj_fwd3d)
7
 *
8
 ******************************************************************************
9
 * Copyright (c) 2000, Frank Warmerdam
10
 * Copyright (c) 2018, Thomas Knudsen / SDFE
11
 *
12
 * Permission is hereby granted, free of charge, to any person obtaining a
13
 * copy of this software and associated documentation files (the "Software"),
14
 * to deal in the Software without restriction, including without limitation
15
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16
 * and/or sell copies of the Software, and to permit persons to whom the
17
 * Software is furnished to do so, subject to the following conditions:
18
 *
19
 * The above copyright notice and this permission notice shall be included
20
 * in all copies or substantial portions of the Software.
21
 *
22
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
 * DEALINGS IN THE SOFTWARE.
29
 *****************************************************************************/
30
31
#include <errno.h>
32
#include <math.h>
33
34
#include "proj_internal.h"
35
#include <math.h>
36
37
5.38M
#define INPUT_UNITS P->left
38
5.13M
#define OUTPUT_UNITS P->right
39
40
3.85M
static void fwd_prepare(PJ *P, PJ_COORD &coo) {
41
3.85M
    if (HUGE_VAL == coo.v[0] || HUGE_VAL == coo.v[1] || HUGE_VAL == coo.v[2]) {
42
9
        coo = proj_coord_error();
43
9
        return;
44
9
    }
45
46
    /* The helmert datum shift will choke unless it gets a sensible 4D
47
     * coordinate
48
     */
49
3.85M
    if (HUGE_VAL == coo.v[2] && P->helmert)
50
0
        coo.v[2] = 0.0;
51
3.85M
    if (HUGE_VAL == coo.v[3] && P->helmert)
52
86.3k
        coo.v[3] = 0.0;
53
54
    /* Check validity of angular input coordinates */
55
3.85M
    if (INPUT_UNITS == PJ_IO_UNITS_RADIANS) {
56
2.31M
        double t;
57
58
        /* check for latitude or longitude over-range */
59
2.31M
        t = (coo.lp.phi < 0 ? -coo.lp.phi : coo.lp.phi) - M_HALFPI;
60
2.31M
        if (t > PJ_EPS_LAT) {
61
11.3k
            proj_log_error(P, _("Invalid latitude"));
62
11.3k
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_INVALID_COORD);
63
11.3k
            coo = proj_coord_error();
64
11.3k
            return;
65
11.3k
        }
66
2.30M
        if (coo.lp.lam > 10 || coo.lp.lam < -10) {
67
0
            proj_log_error(P, _("Invalid longitude"));
68
0
            proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_INVALID_COORD);
69
0
            coo = proj_coord_error();
70
0
            return;
71
0
        }
72
73
        /* Clamp latitude to -90..90 degree range */
74
2.30M
        if (coo.lp.phi > M_HALFPI)
75
0
            coo.lp.phi = M_HALFPI;
76
2.30M
        if (coo.lp.phi < -M_HALFPI)
77
0
            coo.lp.phi = -M_HALFPI;
78
79
        /* If input latitude is geocentrical, convert to geographical */
80
2.30M
        if (P->geoc)
81
0
            coo = pj_geocentric_latitude(P, PJ_INV, coo);
82
83
        /* Ensure longitude is in the -pi:pi range */
84
2.30M
        if (0 == P->over)
85
2.30M
            coo.lp.lam = adjlon(coo.lp.lam);
86
87
2.30M
        if (P->hgridshift)
88
63.9k
            coo = proj_trans(P->hgridshift, PJ_INV, coo);
89
2.23M
        else if (P->helmert ||
90
2.23M
                 (P->cart_wgs84 != nullptr && P->cart != nullptr)) {
91
275k
            coo = proj_trans(P->cart_wgs84, PJ_FWD,
92
275k
                             coo); /* Go cartesian in WGS84 frame */
93
275k
            if (P->helmert)
94
261k
                coo = proj_trans(P->helmert, PJ_INV,
95
261k
                                 coo); /* Step into local frame */
96
275k
            coo = proj_trans(P->cart, PJ_INV,
97
275k
                             coo); /* Go back to angular using local ellps */
98
275k
        }
99
2.30M
        if (coo.lp.lam == HUGE_VAL)
100
0
            return;
101
2.30M
        if (P->vgridshift)
102
19.7k
            coo = proj_trans(P->vgridshift, PJ_FWD,
103
19.7k
                             coo); /* Go orthometric from geometric */
104
105
        /* Distance from central meridian, taking system zero meridian into
106
         * account
107
         */
108
2.30M
        coo.lp.lam = (coo.lp.lam - P->from_greenwich) - P->lam0;
109
110
        /* Ensure longitude is in the -pi:pi range */
111
2.30M
        if (0 == P->over)
112
2.30M
            coo.lp.lam = adjlon(coo.lp.lam);
113
114
2.30M
        return;
115
2.30M
    }
116
117
    /* We do not support gridshifts on cartesian input */
118
1.53M
    if (INPUT_UNITS == PJ_IO_UNITS_CARTESIAN && P->helmert)
119
0
        coo = proj_trans(P->helmert, PJ_INV, coo);
120
1.53M
    return;
121
3.85M
}
122
123
5.13M
static void fwd_finalize(PJ *P, PJ_COORD &coo) {
124
125
5.13M
    switch (OUTPUT_UNITS) {
126
127
    /* Handle false eastings/northings and non-metric linear units */
128
41.7k
    case PJ_IO_UNITS_CARTESIAN:
129
130
41.7k
        if (P->is_geocent) {
131
0
            coo = proj_trans(P->cart, PJ_FWD, coo);
132
0
        }
133
41.7k
        coo.xyz.x *= P->fr_meter;
134
41.7k
        coo.xyz.y *= P->fr_meter;
135
41.7k
        coo.xyz.z *= P->fr_meter;
136
137
41.7k
        break;
138
139
    /* Classic proj.4 functions return plane coordinates in units of the
140
     * semimajor axis */
141
1.61M
    case PJ_IO_UNITS_CLASSIC:
142
1.61M
        coo.xy.x *= P->a;
143
1.61M
        coo.xy.y *= P->a;
144
1.61M
        PROJ_FALLTHROUGH;
145
146
    /* to continue processing in common with PJ_IO_UNITS_PROJECTED */
147
1.64M
    case PJ_IO_UNITS_PROJECTED:
148
1.64M
        coo.xyz.x = P->fr_meter * (coo.xyz.x + P->x0);
149
1.64M
        coo.xyz.y = P->fr_meter * (coo.xyz.y + P->y0);
150
1.64M
        coo.xyz.z = P->vfr_meter * (coo.xyz.z + P->z0);
151
1.64M
        break;
152
153
769k
    case PJ_IO_UNITS_WHATEVER:
154
769k
        break;
155
156
269k
    case PJ_IO_UNITS_DEGREES:
157
269k
        break;
158
159
2.40M
    case PJ_IO_UNITS_RADIANS:
160
2.40M
        coo.lpz.z = P->vfr_meter * (coo.lpz.z + P->z0);
161
162
2.40M
        if (P->is_long_wrap_set) {
163
0
            if (coo.lpz.lam != HUGE_VAL) {
164
0
                coo.lpz.lam = P->long_wrap_center +
165
0
                              adjlon(coo.lpz.lam - P->long_wrap_center);
166
0
            }
167
0
        }
168
169
2.40M
        break;
170
5.13M
    }
171
172
5.13M
    if (P->axisswap)
173
11.4k
        coo = proj_trans(P->axisswap, PJ_FWD, coo);
174
5.13M
}
175
176
13
static inline PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) {
177
13
    if (P->ctx->last_errno)
178
0
        return proj_coord_error();
179
180
13
    P->ctx->last_errno = last_errno;
181
182
13
    return coord;
183
13
}
184
185
0
PJ_XY pj_fwd(PJ_LP lp, PJ *P) {
186
0
    PJ_COORD coo = {{0, 0, 0, 0}};
187
0
    coo.lp = lp;
188
189
0
    const int last_errno = P->ctx->last_errno;
190
0
    P->ctx->last_errno = 0;
191
192
0
    if (!P->skip_fwd_prepare)
193
0
        fwd_prepare(P, coo);
194
0
    if (HUGE_VAL == coo.v[0] || HUGE_VAL == coo.v[1])
195
0
        return proj_coord_error().xy;
196
197
    /* Do the transformation, using the lowest dimensional transformer available
198
     */
199
0
    if (P->fwd) {
200
0
        const auto xy = P->fwd(coo.lp, P);
201
0
        coo.xy = xy;
202
0
    } else if (P->fwd3d) {
203
0
        const auto xyz = P->fwd3d(coo.lpz, P);
204
0
        coo.xyz = xyz;
205
0
    } else if (P->fwd4d)
206
0
        P->fwd4d(coo, P);
207
0
    else {
208
0
        proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP);
209
0
        return proj_coord_error().xy;
210
0
    }
211
0
    if (HUGE_VAL == coo.v[0])
212
0
        return proj_coord_error().xy;
213
214
0
    if (!P->skip_fwd_finalize)
215
0
        fwd_finalize(P, coo);
216
217
0
    return error_or_coord(P, coo, last_errno).xy;
218
0
}
219
220
22
PJ_XYZ pj_fwd3d(PJ_LPZ lpz, PJ *P) {
221
22
    PJ_COORD coo = {{0, 0, 0, 0}};
222
22
    coo.lpz = lpz;
223
224
22
    const int last_errno = P->ctx->last_errno;
225
22
    P->ctx->last_errno = 0;
226
227
22
    if (!P->skip_fwd_prepare)
228
22
        fwd_prepare(P, coo);
229
22
    if (HUGE_VAL == coo.v[0])
230
9
        return proj_coord_error().xyz;
231
232
    /* Do the transformation, using the lowest dimensional transformer feasible
233
     */
234
13
    if (P->fwd3d) {
235
13
        const auto xyz = P->fwd3d(coo.lpz, P);
236
13
        coo.xyz = xyz;
237
13
    } else if (P->fwd4d)
238
0
        P->fwd4d(coo, P);
239
0
    else if (P->fwd) {
240
0
        const auto xy = P->fwd(coo.lp, P);
241
0
        coo.xy = xy;
242
0
    } else {
243
0
        proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP);
244
0
        return proj_coord_error().xyz;
245
0
    }
246
13
    if (HUGE_VAL == coo.v[0])
247
0
        return proj_coord_error().xyz;
248
249
13
    if (!P->skip_fwd_finalize)
250
13
        fwd_finalize(P, coo);
251
252
13
    return error_or_coord(P, coo, last_errno).xyz;
253
13
}
254
255
9.10M
bool pj_fwd4d(PJ_COORD &coo, PJ *P) {
256
257
9.10M
    const int last_errno = P->ctx->last_errno;
258
9.10M
    P->ctx->last_errno = 0;
259
260
9.10M
    if (!P->skip_fwd_prepare)
261
3.85M
        fwd_prepare(P, coo);
262
9.10M
    if (HUGE_VAL == coo.v[0]) {
263
11.3k
        coo = proj_coord_error();
264
11.3k
        return false;
265
11.3k
    }
266
267
    /* Call the highest dimensional converter available */
268
9.08M
    if (P->fwd4d)
269
5.51M
        P->fwd4d(coo, P);
270
3.57M
    else if (P->fwd3d) {
271
494k
        const auto xyz = P->fwd3d(coo.lpz, P);
272
494k
        coo.xyz = xyz;
273
3.08M
    } else if (P->fwd) {
274
2.01M
        const auto xy = P->fwd(coo.lp, P);
275
2.01M
        coo.xy = xy;
276
2.01M
    } else {
277
1.06M
        proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP);
278
1.06M
        coo = proj_coord_error();
279
1.06M
        return false;
280
1.06M
    }
281
8.02M
    if (HUGE_VAL == coo.v[0]) {
282
38.4k
        coo = proj_coord_error();
283
38.4k
        return false;
284
38.4k
    }
285
286
7.98M
    if (!P->skip_fwd_finalize)
287
5.13M
        fwd_finalize(P, coo);
288
289
7.98M
    if (P->ctx->last_errno) {
290
221
        coo = proj_coord_error();
291
221
        return false;
292
221
    }
293
294
7.98M
    P->ctx->last_errno = last_errno;
295
7.98M
    return true;
296
7.98M
}