Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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
15.8k
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
15.8k
    P->skip_fwd_prepare = 1;
42
15.8k
    P->skip_fwd_finalize = 1;
43
15.8k
    P->skip_inv_prepare = 1;
44
15.8k
    P->skip_inv_finalize = 1;
45
15.8k
    return P;
46
15.8k
}
47
48
/*************************************************************************************/
49
88.0k
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
88.0k
    PJ *Q;
59
88.0k
    paralist *p;
60
88.0k
    int do_cart = 0;
61
88.0k
    if (nullptr == P)
62
5.97k
        return 0;
63
64
    /* Don't recurse when calling proj_create (which calls us back) */
65
82.0k
    if (pj_param_exists(P->params, "break_cs2cs_recursion"))
66
15.8k
        return 1;
67
68
    /* Swap axes? */
69
66.2k
    p = pj_param_exists(P->params, "axis");
70
71
66.2k
    const bool disable_grid_presence_check =
72
66.2k
        pj_param_exists(P->params, "disable_grid_presence_check") != nullptr;
73
74
    /* Don't axisswap if data are already in "enu" order */
75
66.2k
    if (p && (0 != strcmp("enu", p->param))) {
76
631
        size_t def_size = 100 + strlen(P->axis);
77
631
        char *def = static_cast<char *>(malloc(def_size));
78
631
        if (nullptr == def)
79
0
            return 0;
80
631
        snprintf(def, def_size,
81
631
                 "break_cs2cs_recursion     proj=axisswap  axis=%s", P->axis);
82
631
        Q = pj_create_internal(P->ctx, def);
83
631
        free(def);
84
631
        if (nullptr == Q)
85
6
            return 0;
86
625
        P->axisswap = skip_prep_fin(Q);
87
625
    }
88
89
    /* Geoid grid(s) given? */
90
66.2k
    p = pj_param_exists(P->params, "geoidgrids");
91
66.2k
    if (!disable_grid_presence_check && p &&
92
662
        strlen(p->param) > strlen("geoidgrids=")) {
93
485
        char *gridnames = p->param + strlen("geoidgrids=");
94
485
        size_t def_size = 100 + 2 * strlen(gridnames);
95
485
        char *def = static_cast<char *>(malloc(def_size));
96
485
        if (nullptr == def)
97
0
            return 0;
98
485
        snprintf(def, def_size,
99
485
                 "break_cs2cs_recursion     proj=vgridshift  grids=%s",
100
485
                 pj_double_quote_string_param_if_needed(gridnames).c_str());
101
485
        Q = pj_create_internal(P->ctx, def);
102
485
        free(def);
103
485
        if (nullptr == Q)
104
249
            return 0;
105
236
        P->vgridshift = skip_prep_fin(Q);
106
236
    }
107
108
    /* Datum shift grid(s) given? */
109
66.0k
    p = pj_param_exists(P->params, "nadgrids");
110
66.0k
    if (!disable_grid_presence_check && p &&
111
1.95k
        strlen(p->param) > strlen("nadgrids=")) {
112
1.77k
        char *gridnames = p->param + strlen("nadgrids=");
113
1.77k
        size_t def_size = 100 + 2 * strlen(gridnames);
114
1.77k
        char *def = static_cast<char *>(malloc(def_size));
115
1.77k
        if (nullptr == def)
116
0
            return 0;
117
1.77k
        snprintf(def, def_size,
118
1.77k
                 "break_cs2cs_recursion     proj=hgridshift  grids=%s",
119
1.77k
                 pj_double_quote_string_param_if_needed(gridnames).c_str());
120
1.77k
        Q = pj_create_internal(P->ctx, def);
121
1.77k
        free(def);
122
1.77k
        if (nullptr == Q)
123
227
            return 0;
124
1.54k
        P->hgridshift = skip_prep_fin(Q);
125
1.54k
    }
126
127
    /* We ignore helmert if we have grid shift */
128
65.7k
    p = P->hgridshift ? nullptr : pj_param_exists(P->params, "towgs84");
129
65.7k
    while (p) {
130
7.23k
        const char *const s = p->param;
131
7.23k
        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
7.23k
        if (0 == d[0] && 0 == d[1] && 0 == d[2] && 0 == d[3] && 0 == d[4] &&
136
4.15k
            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
3.82k
            if (!(fabs(P->a_orig - 6378137.0) < 1e-8 &&
140
3.45k
                  fabs(P->es_orig - 0.0066943799901413) < 1e-15)) {
141
1.08k
                do_cart = 1;
142
1.08k
            }
143
3.82k
            break;
144
3.82k
        }
145
146
3.40k
        const size_t n = strlen(s);
147
3.40k
        if (n <= 8) /* 8==strlen ("towgs84=") */
148
0
            return 0;
149
150
3.40k
        const size_t def_max_size = 100 + n;
151
3.40k
        std::string def;
152
3.40k
        def.reserve(def_max_size);
153
3.40k
        def += "break_cs2cs_recursion     proj=helmert exact ";
154
3.40k
        def += s;
155
3.40k
        def += " convention=position_vector";
156
3.40k
        Q = pj_create_internal(P->ctx, def.c_str());
157
3.40k
        if (nullptr == Q)
158
2
            return 0;
159
3.40k
        pj_inherit_ellipsoid_def(P, Q);
160
3.40k
        P->helmert = skip_prep_fin(Q);
161
162
3.40k
        break;
163
3.40k
    }
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
65.7k
    if (P->is_geocent || P->helmert || do_cart) {
169
5.72k
        char def[150];
170
5.72k
        snprintf(def, sizeof(def),
171
5.72k
                 "break_cs2cs_recursion     proj=cart   a=%40.20g  es=%40.20g",
172
5.72k
                 P->a_orig, P->es_orig);
173
5.72k
        {
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
5.72k
            char *next_pos;
182
5.72k
            for (next_pos = def; (next_pos = strchr(next_pos, ',')) != nullptr;
183
5.72k
                 next_pos++) {
184
0
                *next_pos = '.';
185
0
            }
186
5.72k
        }
187
5.72k
        Q = pj_create_internal(P->ctx, def);
188
5.72k
        if (nullptr == Q)
189
0
            return 0;
190
5.72k
        P->cart = skip_prep_fin(Q);
191
192
5.72k
        if (!P->is_geocent) {
193
4.28k
            snprintf(def, sizeof(def),
194
4.28k
                     "break_cs2cs_recursion     proj=cart  ellps=WGS84");
195
4.28k
            Q = pj_create_internal(P->ctx, def);
196
4.28k
            if (nullptr == Q)
197
0
                return 0;
198
4.28k
            P->cart_wgs84 = skip_prep_fin(Q);
199
4.28k
        }
200
5.72k
    }
201
202
65.7k
    return 1;
203
65.7k
}
204
205
/*************************************************************************************/
206
68.8k
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
68.8k
    char *args, **argv;
220
68.8k
    size_t argc, n;
221
222
68.8k
    if (nullptr == ctx)
223
0
        ctx = pj_get_default_ctx();
224
225
    /* Make a copy that we can manipulate */
226
68.8k
    n = strlen(definition);
227
68.8k
    args = (char *)malloc(n + 1);
228
68.8k
    if (nullptr == args) {
229
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER /*ENOMEM*/);
230
0
        return nullptr;
231
0
    }
232
68.8k
    strcpy(args, definition);
233
234
68.8k
    argc = pj_trim_argc(args);
235
68.8k
    if (argc == 0) {
236
0
        free(args);
237
0
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
238
0
        return nullptr;
239
0
    }
240
241
68.8k
    argv = pj_trim_argv(argc, args);
242
68.8k
    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
68.8k
    PJ *P = pj_create_argv_internal(ctx, (int)argc, argv);
249
250
68.8k
    free(argv);
251
68.8k
    free(args);
252
253
68.8k
    return P;
254
68.8k
}
255
256
/*************************************************************************************/
257
43.1k
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
43.1k
    if (nullptr == ctx)
267
0
        ctx = pj_get_default_ctx();
268
43.1k
    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
43.1k
    char *c = pj_make_args(argc, argv);
276
43.1k
    if (nullptr == c) {
277
0
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP /* ENOMEM */);
278
0
        return nullptr;
279
0
    }
280
281
43.1k
    PJ *P = proj_create(ctx, c);
282
283
43.1k
    free((char *)c);
284
43.1k
    return P;
285
43.1k
}
286
287
/*************************************************************************************/
288
88.0k
PJ *pj_create_argv_internal(PJ_CONTEXT *ctx, int argc, char **argv) {
289
    /**************************************************************************************
290
    For use by pipeline init function.
291
    **************************************************************************************/
292
88.0k
    if (nullptr == ctx)
293
0
        ctx = pj_get_default_ctx();
294
88.0k
    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
88.0k
    const int allow_init_epsg =
302
88.0k
        proj_context_get_use_proj4_init_rules(ctx, FALSE);
303
88.0k
    PJ *P = pj_init_ctx_with_allow_init_epsg(ctx, argc, argv, allow_init_epsg);
304
305
    /* Support cs2cs-style modifiers */
306
88.0k
    int ret = cs2cs_emulation_setup(P);
307
88.0k
    if (0 == ret)
308
6.45k
        return proj_destroy(P);
309
310
81.6k
    return P;
311
88.0k
}