Coverage Report

Created: 2024-02-25 06:14

/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
4
#define INPUT_UNITS P->right
37
4
#define OUTPUT_UNITS P->left
38
39
4
static void inv_prepare(PJ *P, PJ_COORD &coo) {
40
4
    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
4
    if (HUGE_VAL == coo.v[2] && P->helmert)
50
0
        coo.v[2] = 0.0;
51
4
    if (HUGE_VAL == coo.v[3] && P->helmert)
52
0
        coo.v[3] = 0.0;
53
54
4
    if (P->axisswap)
55
0
        coo = proj_trans(P->axisswap, PJ_INV, coo);
56
57
    /* Handle remaining possible input types */
58
4
    switch (INPUT_UNITS) {
59
0
    case PJ_IO_UNITS_WHATEVER:
60
0
        break;
61
62
0
    case PJ_IO_UNITS_DEGREES:
63
0
        break;
64
65
    /* de-scale and de-offset */
66
4
    case PJ_IO_UNITS_CARTESIAN:
67
4
        coo.xyz.x *= P->to_meter;
68
4
        coo.xyz.y *= P->to_meter;
69
4
        coo.xyz.z *= P->to_meter;
70
4
        if (P->is_geocent) {
71
0
            coo = proj_trans(P->cart, PJ_INV, coo);
72
0
        }
73
4
        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
0
    case PJ_IO_UNITS_RADIANS:
97
0
        coo.lpz.z = P->vto_meter * coo.lpz.z - P->z0;
98
0
        break;
99
4
    }
100
4
}
101
102
4
static void inv_finalize(PJ *P, PJ_COORD &coo) {
103
4
    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
4
    if (OUTPUT_UNITS == PJ_IO_UNITS_RADIANS) {
109
110
        /* Distance from central meridian, taking system zero meridian into
111
         * account
112
         */
113
4
        coo.lp.lam = coo.lp.lam + P->from_greenwich + P->lam0;
114
115
        /* adjust longitude to central meridian */
116
4
        if (0 == P->over)
117
4
            coo.lpz.lam = adjlon(coo.lpz.lam);
118
119
4
        if (P->vgridshift)
120
0
            coo = proj_trans(P->vgridshift, PJ_INV,
121
0
                             coo); /* Go geometric from orthometric */
122
4
        if (coo.lp.lam == HUGE_VAL)
123
0
            return;
124
4
        if (P->hgridshift)
125
0
            coo = proj_trans(P->hgridshift, PJ_FWD, coo);
126
4
        else if (P->helmert ||
127
4
                 (P->cart_wgs84 != nullptr && P->cart != nullptr)) {
128
0
            coo = proj_trans(P->cart, PJ_FWD,
129
0
                             coo); /* Go cartesian in local frame */
130
0
            if (P->helmert)
131
0
                coo = proj_trans(P->helmert, PJ_FWD, coo); /* Step into WGS84 */
132
0
            coo = proj_trans(P->cart_wgs84, PJ_INV,
133
0
                             coo); /* Go back to angular using WGS84 ellps */
134
0
        }
135
4
        if (coo.lp.lam == HUGE_VAL)
136
0
            return;
137
138
        /* If input latitude was geocentrical, convert back to geocentrical */
139
4
        if (P->geoc)
140
0
            coo = pj_geocentric_latitude(P, PJ_FWD, coo);
141
4
    }
142
4
}
143
144
4
static inline PJ_COORD error_or_coord(PJ *P, PJ_COORD coord, int last_errno) {
145
4
    if (P->ctx->last_errno)
146
0
        return proj_coord_error();
147
148
4
    P->ctx->last_errno = last_errno;
149
150
4
    return coord;
151
4
}
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
4
PJ_LPZ pj_inv3d(PJ_XYZ xyz, PJ *P) {
189
4
    PJ_COORD coo = {{0, 0, 0, 0}};
190
4
    coo.xyz = xyz;
191
192
4
    const int last_errno = P->ctx->last_errno;
193
4
    P->ctx->last_errno = 0;
194
195
4
    if (!P->skip_inv_prepare)
196
4
        inv_prepare(P, coo);
197
4
    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
4
    if (P->inv3d) {
203
4
        const auto lpz = P->inv3d(coo.xyz, P);
204
4
        coo.lpz = lpz;
205
4
    } 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
4
    if (HUGE_VAL == coo.v[0])
215
0
        return proj_coord_error().lpz;
216
217
4
    if (!P->skip_inv_finalize)
218
4
        inv_finalize(P, coo);
219
220
4
    return error_or_coord(P, coo, last_errno).lpz;
221
4
}
222
223
0
bool pj_inv4d(PJ_COORD &coo, PJ *P) {
224
225
0
    const int last_errno = P->ctx->last_errno;
226
0
    P->ctx->last_errno = 0;
227
228
0
    if (!P->skip_inv_prepare)
229
0
        inv_prepare(P, coo);
230
0
    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
0
    if (P->inv4d)
237
0
        P->inv4d(coo, P);
238
0
    else if (P->inv3d) {
239
0
        const auto lpz = P->inv3d(coo.xyz, P);
240
0
        coo.lpz = lpz;
241
0
    } else if (P->inv) {
242
0
        const auto lp = P->inv(coo.xy, P);
243
0
        coo.lp = lp;
244
0
    } 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
0
    if (HUGE_VAL == coo.v[0]) {
250
0
        coo = proj_coord_error();
251
0
        return false;
252
0
    }
253
254
0
    if (!P->skip_inv_finalize)
255
0
        inv_finalize(P, coo);
256
257
0
    if (P->ctx->last_errno) {
258
0
        coo = proj_coord_error();
259
0
        return false;
260
0
    }
261
262
0
    P->ctx->last_errno = last_errno;
263
0
    return true;
264
0
}