Coverage Report

Created: 2025-10-30 06:17

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