Coverage Report

Created: 2026-09-26 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/proj/src/create.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  PROJ
3
 * Purpose:  pj_create_internal() related stuff
4
 *
5
 * Author:   Thomas Knudsen,  thokn@sdfe.dk,  2016-06-09/2016-11-06
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2016, 2017 Thomas Knudsen/SDFE
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 *****************************************************************************/
28
29
#define FROM_PROJ_CPP
30
31
#include "proj.h"
32
#include "proj_internal.h"
33
#include <math.h>
34
35
/*************************************************************************************/
36
1.17M
static PJ *skip_prep_fin(PJ *P) {
37
    /**************************************************************************************
38
    Skip prepare and finalize function for the various "helper operations" added
39
    to P when in cs2cs compatibility mode.
40
    **************************************************************************************/
41
1.17M
    P->skip_fwd_prepare = 1;
42
1.17M
    P->skip_fwd_finalize = 1;
43
1.17M
    P->skip_inv_prepare = 1;
44
1.17M
    P->skip_inv_finalize = 1;
45
1.17M
    return P;
46
1.17M
}
47
48
/*************************************************************************************/
49
6.00M
static int cs2cs_emulation_setup(PJ *P) {
50
    /**************************************************************************************
51
    If any cs2cs style modifiers are given (axis=..., towgs84=..., ) create the
52
    4D API equivalent operations, so the preparation and finalization steps in
53
    the pj_inv/pj_fwd invocators can emulate the behavior of pj_transform and
54
    the cs2cs app.
55
56
    Returns 1 on success, 0 on failure
57
    **************************************************************************************/
58
6.00M
    PJ *Q;
59
6.00M
    paralist *p;
60
6.00M
    int do_cart = 0;
61
6.00M
    if (nullptr == P)
62
541k
        return 0;
63
64
    /* Don't recurse when calling proj_create (which calls us back) */
65
5.46M
    if (pj_param_exists(P->params, "break_cs2cs_recursion"))
66
1.17M
        return 1;
67
68
    /* Swap axes? */
69
4.29M
    p = pj_param_exists(P->params, "axis");
70
71
4.29M
    const bool disable_grid_presence_check =
72
4.29M
        pj_param_exists(P->params, "disable_grid_presence_check") != nullptr;
73
74
    /* Don't axisswap if data are already in "enu" order */
75
4.29M
    if (p && (0 != strcmp("enu", p->param))) {
76
120k
        size_t def_size = 100 + strlen(P->axis);
77
120k
        char *def = static_cast<char *>(malloc(def_size));
78
120k
        if (nullptr == def)
79
0
            return 0;
80
120k
        snprintf(def, def_size,
81
120k
                 "break_cs2cs_recursion     proj=axisswap  axis=%s", P->axis);
82
120k
        Q = pj_create_internal(P->ctx, def);
83
120k
        free(def);
84
120k
        if (nullptr == Q)
85
1.65k
            return 0;
86
118k
        P->axisswap = skip_prep_fin(Q);
87
118k
    }
88
89
    /* Geoid grid(s) given? */
90
4.29M
    p = pj_param_exists(P->params, "geoidgrids");
91
4.29M
    if (!disable_grid_presence_check && p &&
92
116k
        strlen(p->param) > strlen("geoidgrids=")) {
93
62.8k
        char *gridnames = p->param + strlen("geoidgrids=");
94
62.8k
        size_t def_size = 100 + 2 * strlen(gridnames);
95
62.8k
        char *def = static_cast<char *>(malloc(def_size));
96
62.8k
        if (nullptr == def)
97
0
            return 0;
98
62.8k
        snprintf(def, def_size,
99
62.8k
                 "break_cs2cs_recursion     proj=vgridshift  grids=%s",
100
62.8k
                 pj_double_quote_string_param_if_needed(gridnames).c_str());
101
62.8k
        Q = pj_create_internal(P->ctx, def);
102
62.8k
        free(def);
103
62.8k
        if (nullptr == Q)
104
11.1k
            return 0;
105
51.6k
        P->vgridshift = skip_prep_fin(Q);
106
51.6k
    }
107
108
    /* Datum shift grid(s) given? */
109
4.28M
    p = pj_param_exists(P->params, "nadgrids");
110
4.28M
    if (!disable_grid_presence_check && p &&
111
145k
        strlen(p->param) > strlen("nadgrids=")) {
112
112k
        char *gridnames = p->param + strlen("nadgrids=");
113
112k
        size_t def_size = 100 + 2 * strlen(gridnames);
114
112k
        char *def = static_cast<char *>(malloc(def_size));
115
112k
        if (nullptr == def)
116
0
            return 0;
117
112k
        snprintf(def, def_size,
118
112k
                 "break_cs2cs_recursion     proj=hgridshift  grids=%s",
119
112k
                 pj_double_quote_string_param_if_needed(gridnames).c_str());
120
112k
        Q = pj_create_internal(P->ctx, def);
121
112k
        free(def);
122
112k
        if (nullptr == Q)
123
13.1k
            return 0;
124
98.9k
        P->hgridshift = skip_prep_fin(Q);
125
98.9k
    }
126
127
    /* We ignore helmert if we have grid shift */
128
4.27M
    p = P->hgridshift ? nullptr : pj_param_exists(P->params, "towgs84");
129
4.27M
    while (p) {
130
390k
        const char *const s = p->param;
131
390k
        const double *const d = P->datum_params;
132
133
        /* We ignore null helmert shifts (common in auto-translated resource
134
         * files, e.g. epsg) */
135
390k
        if (0 == d[0] && 0 == d[1] && 0 == d[2] && 0 == d[3] && 0 == d[4] &&
136
265k
            0 == d[5] && 0 == d[6]) {
137
            /* If the current ellipsoid is not WGS84, then make sure the */
138
            /* change in ellipsoid is still done. */
139
186k
            if (!(fabs(P->a_orig - 6378137.0) < 1e-8 &&
140
159k
                  fabs(P->es_orig - 0.0066943799901413) < 1e-15)) {
141
135k
                do_cart = 1;
142
135k
            }
143
186k
            break;
144
186k
        }
145
146
203k
        const size_t n = strlen(s);
147
203k
        if (n <= 8) /* 8==strlen ("towgs84=") */
148
0
            return 0;
149
150
203k
        const size_t def_max_size = 100 + n;
151
203k
        std::string def;
152
203k
        def.reserve(def_max_size);
153
203k
        def += "break_cs2cs_recursion     proj=helmert exact ";
154
203k
        def += s;
155
203k
        def += " convention=position_vector";
156
203k
        Q = pj_create_internal(P->ctx, def.c_str());
157
203k
        if (nullptr == Q)
158
647
            return 0;
159
203k
        pj_inherit_ellipsoid_def(P, Q);
160
203k
        P->helmert = skip_prep_fin(Q);
161
162
203k
        break;
163
203k
    }
164
165
    /* We also need cartesian/geographical transformations if we are working in
166
     */
167
    /* geocentric/cartesian space or we need to do a Helmert transform. */
168
4.27M
    if (P->is_geocent || P->helmert || do_cart) {
169
364k
        char def[150];
170
364k
        snprintf(def, sizeof(def),
171
364k
                 "break_cs2cs_recursion     proj=cart   a=%40.20g  es=%40.20g",
172
364k
                 P->a_orig, P->es_orig);
173
364k
        {
174
            /* In case the current locale does not use dot but comma as decimal
175
             */
176
            /* separator, replace it with dot, so that proj_atof() behaves */
177
            /* correctly. */
178
            /* TODO later: use C++ ostringstream with
179
             * imbue(std::locale::classic()) */
180
            /* to be locale unaware */
181
364k
            char *next_pos;
182
364k
            for (next_pos = def; (next_pos = strchr(next_pos, ',')) != nullptr;
183
364k
                 next_pos++) {
184
0
                *next_pos = '.';
185
0
            }
186
364k
        }
187
364k
        Q = pj_create_internal(P->ctx, def);
188
364k
        if (nullptr == Q)
189
13
            return 0;
190
364k
        P->cart = skip_prep_fin(Q);
191
192
364k
        if (!P->is_geocent) {
193
333k
            snprintf(def, sizeof(def),
194
333k
                     "break_cs2cs_recursion     proj=cart  ellps=WGS84");
195
333k
            Q = pj_create_internal(P->ctx, def);
196
333k
            if (nullptr == Q)
197
0
                return 0;
198
333k
            P->cart_wgs84 = skip_prep_fin(Q);
199
333k
        }
200
364k
    }
201
202
4.27M
    return 1;
203
4.27M
}
204
205
/*************************************************************************************/
206
2.63M
PJ *pj_create_internal(PJ_CONTEXT *ctx, const char *definition) {
207
    /*************************************************************************************/
208
209
    /**************************************************************************************
210
        Create a new PJ object in the context ctx, using the given definition.
211
    If ctx==0, the default context is used, if definition==0, or invalid, a
212
    null-pointer is returned. The definition may use '+' as argument start
213
    indicator, as in
214
        "+proj=utm +zone=32", or leave it out, as in "proj=utm zone=32".
215
216
        It may even use free formatting "proj  =  utm;  zone  =32  ellps=
217
    GRS80". Note that the semicolon separator is allowed, but not required.
218
    **************************************************************************************/
219
2.63M
    char *args, **argv;
220
2.63M
    size_t argc, n;
221
222
2.63M
    if (nullptr == ctx)
223
0
        ctx = pj_get_default_ctx();
224
225
    /* Make a copy that we can manipulate */
226
2.63M
    n = strlen(definition);
227
2.63M
    args = (char *)malloc(n + 1);
228
2.63M
    if (nullptr == args) {
229
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/);
230
0
        return nullptr;
231
0
    }
232
2.63M
    strcpy(args, definition);
233
234
2.63M
    argc = pj_trim_argc(args);
235
2.63M
    if (argc == 0) {
236
638
        free(args);
237
638
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
238
638
        return nullptr;
239
638
    }
240
241
2.63M
    argv = pj_trim_argv(argc, args);
242
2.63M
    if (!argv) {
243
0
        free(args);
244
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/);
245
0
        return nullptr;
246
0
    }
247
248
2.63M
    PJ *P = pj_create_argv_internal(ctx, (int)argc, argv);
249
250
2.63M
    free(argv);
251
2.63M
    free(args);
252
253
2.63M
    return P;
254
2.63M
}
255
256
/*************************************************************************************/
257
0
PJ *proj_create_argv(PJ_CONTEXT *ctx, int argc, char **argv) {
258
    /**************************************************************************************
259
    Create a new PJ object in the context ctx, using the given definition
260
    argument array argv. If ctx==0, the default context is used, if
261
    definition==0, or invalid, a null-pointer is returned. The definition
262
    arguments may use '+' as argument start indicator, as in {"+proj=utm",
263
    "+zone=32"}, or leave it out, as in {"proj=utm", "zone=32"}.
264
    **************************************************************************************/
265
266
0
    if (nullptr == ctx)
267
0
        ctx = pj_get_default_ctx();
268
0
    if (nullptr == argv) {
269
0
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
270
0
        return nullptr;
271
0
    }
272
273
    /* We assume that free format is used, and build a full proj_create
274
     * compatible string */
275
0
    char *c = pj_make_args(argc, argv);
276
0
    if (nullptr == c) {
277
0
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP /* ENOMEM */);
278
0
        return nullptr;
279
0
    }
280
281
0
    PJ *P = proj_create(ctx, c);
282
283
0
    free((char *)c);
284
0
    return P;
285
0
}
286
287
/*************************************************************************************/
288
6.00M
PJ *pj_create_argv_internal(PJ_CONTEXT *ctx, int argc, char **argv) {
289
    /**************************************************************************************
290
    For use by pipeline init function.
291
    **************************************************************************************/
292
6.00M
    if (nullptr == ctx)
293
0
        ctx = pj_get_default_ctx();
294
6.00M
    if (nullptr == argv) {
295
0
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
296
0
        return nullptr;
297
0
    }
298
299
    /* ...and let pj_init_ctx do the hard work */
300
    /* New interface: forbid init=epsg:XXXX syntax by default */
301
6.00M
    const int allow_init_epsg =
302
6.00M
        proj_context_get_use_proj4_init_rules(ctx, FALSE);
303
6.00M
    PJ *P = pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg);
304
305
    /* Support cs2cs-style modifiers */
306
6.00M
    int ret = cs2cs_emulation_setup(P);
307
6.00M
    if (0 == ret)
308
567k
        return proj_destroy(P);
309
310
5.44M
    return P;
311
6.00M
}