Coverage Report

Created: 2026-01-09 07:03

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