Coverage Report

Created: 2025-07-12 06:32

/src/PROJ/src/inv.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 * Project:  PROJ.4
3
 * Purpose:  Inverse operation invocation
4
 * Author:   Thomas Knudsen,  thokn@sdfe.dk,  2018-01-02
5
 *           Based on material from Gerald Evenden (original pj_inv)
6
 *           and Piyush Agram (original pj_inv3d)
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
#include <errno.h>
31
#include <math.h>
32
33
#include "proj_internal.h"
34
#include <math.h>
35
36
210k
#define INPUT_UNITS P->right
37
210k
#define OUTPUT_UNITS P->left
38
39
210k
static void inv_prepare(PJ *P, PJ_COORD &coo) {
40
210k
    if (coo.v[0] == HUGE_VAL || coo.v[1] == HUGE_VAL || coo.v[2] == HUGE_VAL) {
41
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
42
0
        coo = proj_coord_error();
43
0
        return;
44
0
    }
45
46
    /* The helmert datum shift will choke unless it gets a sensible 4D
47
     * coordinate
48
     */
49
210k
    if (HUGE_VAL == coo.v[2] && P->helmert)
50
0
        coo.v[2] = 0.0;
51
210k
    if (HUGE_VAL == coo.v[3] && P->helmert)
52
168
        coo.v[3] = 0.0;
53
54
210k
    if (P->axisswap)
55
0
        coo = proj_trans(P->axisswap, PJ_INV, coo);
56
57
    /* Handle remaining possible input types */
58
210k
    switch (INPUT_UNITS) {
59
66.4k
    case PJ_IO_UNITS_WHATEVER:
60
66.4k
        break;
61
62
0
    case PJ_IO_UNITS_DEGREES:
63
0
        break;
64
65
    /* de-scale and de-offset */
66
592
    case PJ_IO_UNITS_CARTESIAN:
67
592
        coo.xyz.x *= P->to_meter;
68
592
        coo.xyz.y *= P->to_meter;
69
592
        coo.xyz.z *= P->to_meter;
70
592
        if (P->is_geocent) {
71
0
            coo = proj_trans(P->cart, PJ_INV, coo);
72
0
        }
73
592
        break;
74
75
0
    case PJ_IO_UNITS_PROJECTED:
76
0
    case PJ_IO_UNITS_CLASSIC:
77
0
        coo.xyz.x = P->to_meter * coo.xyz.x - P->x0;
78
0
        coo.xyz.y = P->to_meter * coo.xyz.y - P->y0;
79
0
        coo.xyz.z = P->vto_meter * coo.xyz.z - P->z0;
80
0
        if (INPUT_UNITS == PJ_IO_UNITS_PROJECTED)
81
0
            return;
82
83
        /* Classic proj.4 functions expect plane coordinates in units of the
84
         * semimajor axis  */
85
        /* Multiplying by ra, rather than dividing by a because the CalCOFI
86
         * projection       */
87
        /* stomps on a and hence (apparently) depends on this to roundtrip
88
         * correctly
89
         */
90
        /* (CalCOFI avoids further scaling by stomping - but a better solution
91
         * is possible)  */
92
0
        coo.xyz.x *= P->ra;
93
0
        coo.xyz.y *= P->ra;
94
0
        break;
95
96
143k
    case PJ_IO_UNITS_RADIANS:
97
143k
        coo.lpz.z = P->vto_meter * coo.lpz.z - P->z0;
98
143k
        break;
99
210k
    }
100
210k
}
101
102
210k
static void inv_finalize(PJ *P, PJ_COORD &coo) {
103
210k
    if (coo.xyz.x == HUGE_VAL) {
104
0
        proj_errno_set(P, PROJ_ERR_COORD_TRANSFM_OUTSIDE_PROJECTION_DOMAIN);
105
0
        coo = proj_coord_error();
106
0
    }
107
108
210k
    if (OUTPUT_UNITS == PJ_IO_UNITS_RADIANS) {
109
110
        /* Distance from central meridian, taking system zero meridian into
111
         * account
112
         */
113
210k
        coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0;
114
115
        /* adjust longitude to central meridian */
116
210k
        if (0 == P->over)
117
210k
            coo.lpz.lam = adjlon(coo.lpz.lam);
118
119
210k
        if (P->vgridshift)
120
0
            coo = proj_trans(P->vgridshift, PJ_INV,
121
0
                             coo); /* Go geometric from orthometric */
122
210k
        if (coo.lp.lam == HUGE_VAL)
123
0
            return;
124
210k
        if (P->hgridshift)
125
20.1k
            coo = proj_trans(P->hgridshift, PJ_FWD, coo);
126
190k
        else if (P->helmert ||
127
190k
                 (P->cart_wgs84 != nullptr && P->cart != nullptr)) {
128
106k
            coo = proj_trans(P->cart, PJ_FWD,
129
106k
                             coo); /* Go cartesian in local frame */
130
106k
            if (P->helmert)
131
102k
                coo = proj_trans(P->helmert, PJ_FWD, coo); /* Step into WGS84 */
132
106k
            coo = proj_trans(P->cart_wgs84, PJ_INV,
133
106k
                             coo); /* Go back to angular using WGS84 ellps */
134
106k
        }
135
210k
        if (coo.lp.lam == HUGE_VAL)
136
0
            return;
137
138
        /* If input latitude was geocentrical, convert back to geocentrical */
139
210k
        if (P->geoc)
140
0
            coo = pj_geocentric_latitude(P, PJ_FWD, coo);
141
210k
    }
142
210k
}
143
144
592
static inline PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) {
145
592
    if (P->ctx->last_errno)
146
0
        return proj_coord_error();
147
148
592
    P->ctx->last_errno = last_errno;
149
150
592
    return coord;
151
592
}
152
153
0
PJ_LP pj_inv(PJ_XY xy, PJ *P) {
154
0
    PJ_COORD coo = {{0, 0, 0, 0}};
155
0
    coo.xy = xy;
156
157
0
    const int last_errno = P->ctx->last_errno;
158
0
    P->ctx->last_errno = 0;
159
160
0
    if (!P->skip_inv_prepare)
161
0
        inv_prepare(P, coo);
162
0
    if (HUGE_VAL == coo.v[0])
163
0
        return proj_coord_error().lp;
164
165
    /* Do the transformation, using the lowest dimensional transformer available
166
     */
167
0
    if (P->inv) {
168
0
        const auto lp = P->inv(coo.xy, P);
169
0
        coo.lp = lp;
170
0
    } else if (P->inv3d) {
171
0
        const auto lpz = P->inv3d(coo.xyz, P);
172
0
        coo.lpz = lpz;
173
0
    } else if (P->inv4d)
174
0
        P->inv4d(coo, P);
175
0
    else {
176
0
        proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP);
177
0
        return proj_coord_error().lp;
178
0
    }
179
0
    if (HUGE_VAL == coo.v[0])
180
0
        return proj_coord_error().lp;
181
182
0
    if (!P->skip_inv_finalize)
183
0
        inv_finalize(P, coo);
184
185
0
    return error_or_coord(P, coo, last_errno).lp;
186
0
}
187
188
592
PJ_LPZ pj_inv3d(PJ_XYZ xyz, PJ *P) {
189
592
    PJ_COORD coo = {{0, 0, 0, 0}};
190
592
    coo.xyz = xyz;
191
192
592
    const int last_errno = P->ctx->last_errno;
193
592
    P->ctx->last_errno = 0;
194
195
592
    if (!P->skip_inv_prepare)
196
592
        inv_prepare(P, coo);
197
592
    if (HUGE_VAL == coo.v[0])
198
0
        return proj_coord_error().lpz;
199
200
    /* Do the transformation, using the lowest dimensional transformer feasible
201
     */
202
592
    if (P->inv3d) {
203
592
        const auto lpz = P->inv3d(coo.xyz, P);
204
592
        coo.lpz = lpz;
205
592
    } else if (P->inv4d)
206
0
        P->inv4d(coo, P);
207
0
    else if (P->inv) {
208
0
        const auto lp = P->inv(coo.xy, P);
209
0
        coo.lp = lp;
210
0
    } else {
211
0
        proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP);
212
0
        return proj_coord_error().lpz;
213
0
    }
214
592
    if (HUGE_VAL == coo.v[0])
215
0
        return proj_coord_error().lpz;
216
217
592
    if (!P->skip_inv_finalize)
218
592
        inv_finalize(P, coo);
219
220
592
    return error_or_coord(P, coo, last_errno).lpz;
221
592
}
222
223
697k
bool pj_inv4d(PJ_COORD &coo, PJ *P) {
224
225
697k
    const int last_errno = P->ctx->last_errno;
226
697k
    P->ctx->last_errno = 0;
227
228
697k
    if (!P->skip_inv_prepare)
229
210k
        inv_prepare(P, coo);
230
697k
    if (HUGE_VAL == coo.v[0]) {
231
0
        coo = proj_coord_error();
232
0
        return false;
233
0
    }
234
235
    /* Call the highest dimensional converter available */
236
697k
    if (P->inv4d)
237
347k
        P->inv4d(coo, P);
238
349k
    else if (P->inv3d) {
239
283k
        const auto lpz = P->inv3d(coo.xyz, P);
240
283k
        coo.lpz = lpz;
241
283k
    } else if (P->inv) {
242
66.4k
        const auto lp = P->inv(coo.xy, P);
243
66.4k
        coo.lp = lp;
244
66.4k
    } else {
245
0
        proj_errno_set(P, PROJ_ERR_OTHER_NO_INVERSE_OP);
246
0
        coo = proj_coord_error();
247
0
        return false;
248
0
    }
249
697k
    if (HUGE_VAL == coo.v[0]) {
250
672
        coo = proj_coord_error();
251
672
        return false;
252
672
    }
253
254
696k
    if (!P->skip_inv_finalize)
255
210k
        inv_finalize(P, coo);
256
257
696k
    if (P->ctx->last_errno) {
258
0
        coo = proj_coord_error();
259
0
        return false;
260
0
    }
261
262
696k
    P->ctx->last_errno = last_errno;
263
696k
    return true;
264
696k
}