Coverage Report

Created: 2026-02-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/init.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  PROJ.4
3
 * Purpose:  Initialize projection object from string definition.  Includes
4
 *           pj_init(), and pj_init_plus() function.
5
 * Author:   Gerald Evenden, Frank Warmerdam <warmerdam@pobox.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1995, Gerald Evenden
9
 * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com>
10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a
12
 * copy of this software and associated documentation files (the "Software"),
13
 * to deal in the Software without restriction, including without limitation
14
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15
 * and/or sell copies of the Software, and to permit persons to whom the
16
 * Software is furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included
19
 * in all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27
 * DEALINGS IN THE SOFTWARE.
28
 *****************************************************************************/
29
30
#include <ctype.h>
31
#include <math.h>
32
#include <stddef.h>
33
#include <stdio.h>
34
#include <string.h>
35
36
#include "filemanager.hpp"
37
#include "geodesic.h"
38
#include "proj.h"
39
#include "proj_internal.h"
40
#include <math.h>
41
42
/**************************************************************************************/
43
263
static paralist *string_to_paralist(PJ_CONTEXT *ctx, char *definition) {
44
    /***************************************************************************************
45
        Convert a string (presumably originating from get_init_string) to a
46
    paralist.
47
    ***************************************************************************************/
48
263
    const char *c = definition;
49
263
    paralist *first = nullptr, *last = nullptr;
50
51
2.44k
    while (*c) {
52
        /* Keep a handle to the start of the list, so we have something to
53
         * return */
54
2.18k
        auto param = pj_mkparam_ws(c, &c);
55
2.18k
        if (nullptr == param) {
56
0
            free_params(ctx, first, PROJ_ERR_OTHER /*ENOMEM*/);
57
0
            return nullptr;
58
0
        }
59
2.18k
        if (nullptr == last) {
60
263
            first = param;
61
1.92k
        } else {
62
1.92k
            last->next = param;
63
1.92k
        }
64
2.18k
        last = param;
65
2.18k
    }
66
263
    return first;
67
263
}
68
69
/**************************************************************************************/
70
399
static char *get_init_string(PJ_CONTEXT *ctx, const char *name) {
71
/***************************************************************************************
72
    Read a section of an init file. Return its contents as a plain character
73
string. It is the duty of the caller to free the memory allocated for the
74
string.
75
***************************************************************************************/
76
6.22k
#define MAX_LINE_LENGTH 1000
77
399
    size_t current_buffer_size = 5 * (MAX_LINE_LENGTH + 1);
78
399
    char *fname, *section;
79
399
    const char *key;
80
399
    char *buffer = nullptr;
81
399
    size_t n;
82
83
399
    fname = static_cast<char *>(malloc(MAX_PATH_FILENAME + ID_TAG_MAX + 3));
84
399
    if (nullptr == fname) {
85
0
        return nullptr;
86
0
    }
87
88
    /* Support "init=file:section", "+init=file:section", and "file:section"
89
     * format */
90
399
    key = strstr(name, "init=");
91
399
    if (nullptr == key)
92
345
        key = name;
93
54
    else
94
54
        key += 5;
95
399
    if (MAX_PATH_FILENAME + ID_TAG_MAX + 2 < strlen(key)) {
96
17
        free(fname);
97
17
        return nullptr;
98
17
    }
99
382
    memmove(fname, key, strlen(key) + 1);
100
101
    /* Locate the name of the section we search for */
102
382
    section = strrchr(fname, ':');
103
382
    if (nullptr == section) {
104
108
        pj_log(ctx, PJ_LOG_ERROR, _("Missing colon in +init"));
105
108
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
106
108
        free(fname);
107
108
        return nullptr;
108
108
    }
109
274
    *section = 0;
110
274
    section++;
111
274
    n = strlen(section);
112
274
    pj_log(ctx, PJ_LOG_TRACE,
113
274
           "get_init_string: searching for section [%s] in init file [%s]",
114
274
           section, fname);
115
116
274
    auto file = NS_PROJ::FileManager::open_resource_file(ctx, fname);
117
274
    if (nullptr == file) {
118
261
        pj_log(ctx, PJ_LOG_ERROR, _("Cannot open %s"), fname);
119
261
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
120
261
        free(fname);
121
261
        return nullptr;
122
261
    }
123
124
    /* Search for section in init file */
125
13
    std::string line;
126
5.82k
    for (;;) {
127
128
5.82k
        bool eofReached = false;
129
5.82k
        bool maxLenReached = false;
130
5.82k
        line = file->read_line(MAX_LINE_LENGTH, maxLenReached, eofReached);
131
        /* End of file? */
132
5.82k
        if (maxLenReached || eofReached) {
133
13
            pj_log(ctx, PJ_LOG_ERROR, _("Invalid content for %s"), fname);
134
13
            proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
135
13
            free(fname);
136
13
            return nullptr;
137
13
        }
138
139
        /* At start of right section? */
140
5.80k
        pj_chomp(&line[0]);
141
5.80k
        if ('<' != line[0])
142
4.83k
            continue;
143
975
        if (strlen(line.c_str()) < n + 2)
144
147
            continue;
145
828
        if (line[n + 1] != '>')
146
795
            continue;
147
33
        if (0 == strncmp(line.data() + 1, section, n))
148
0
            break;
149
33
    }
150
151
    /* We're at the first line of the right section - copy line to buffer */
152
0
    buffer = static_cast<char *>(malloc(current_buffer_size));
153
0
    if (nullptr == buffer) {
154
0
        free(fname);
155
0
        return nullptr;
156
0
    }
157
158
    /* Skip the "<section>" indicator, and copy the rest of the line over */
159
0
    strcpy(buffer, line.data() + strlen(section) + 2);
160
161
    /* Copy the remaining lines of the section to buffer */
162
0
    for (;;) {
163
0
        char *end_i_cator;
164
0
        size_t next_length, buffer_length;
165
166
        /* Did the section end somewhere in the most recently read line? */
167
0
        end_i_cator = strchr(buffer, '<');
168
0
        if (end_i_cator) {
169
0
            *end_i_cator = 0;
170
0
            break;
171
0
        }
172
173
0
        bool eofReached = false;
174
0
        bool maxLenReached = false;
175
0
        line = file->read_line(MAX_LINE_LENGTH, maxLenReached, eofReached);
176
        /* End of file? - done! */
177
0
        if (maxLenReached || eofReached)
178
0
            break;
179
180
        /* Otherwise, handle the line. It MAY be the start of the next section,
181
         */
182
        /* but that will be handled at the start of next trip through the loop
183
         */
184
0
        buffer_length = strlen(buffer);
185
0
        pj_chomp(&line[0]); /* Remove '#' style comments */
186
0
        next_length = strlen(line.data()) + buffer_length + 2;
187
0
        if (next_length > current_buffer_size) {
188
0
            char *b = static_cast<char *>(malloc(2 * current_buffer_size));
189
0
            if (nullptr == b) {
190
0
                free(buffer);
191
0
                buffer = nullptr;
192
0
                break;
193
0
            }
194
0
            strcpy(b, buffer);
195
0
            current_buffer_size *= 2;
196
0
            free(buffer);
197
0
            buffer = b;
198
0
        }
199
0
        buffer[buffer_length] = ' ';
200
0
        strcpy(buffer + buffer_length + 1, line.data());
201
0
    }
202
203
0
    free(fname);
204
0
    if (nullptr == buffer)
205
0
        return nullptr;
206
0
    pj_shrink(buffer);
207
0
    pj_log(ctx, PJ_LOG_TRACE, "key=%s, value: [%s]", key, buffer);
208
0
    return buffer;
209
0
}
210
211
/************************************************************************/
212
static paralist *get_init(PJ_CONTEXT *ctx, const char *key,
213
15.4k
                          int allow_init_epsg) {
214
    /*************************************************************************
215
    Expand key from buffer or (if not in buffer) from init file
216
    *************************************************************************/
217
15.4k
    const char *xkey;
218
15.4k
    char *definition = nullptr;
219
15.4k
    paralist *init_items = nullptr;
220
221
15.4k
    if (!ctx) {
222
0
        ctx = pj_get_default_ctx();
223
0
    }
224
225
    /* support "init=file:section", "+init=file:section", and "file:section"
226
     * format */
227
15.4k
    xkey = strstr(key, "init=");
228
15.4k
    if (nullptr == xkey)
229
4
        xkey = key;
230
15.4k
    else
231
15.4k
        xkey += 5;
232
15.4k
    pj_log(ctx, PJ_LOG_TRACE, "get_init: searching cache for key: [%s]", xkey);
233
234
    /* Is file/key pair already in cache? */
235
15.4k
    init_items = pj_search_initcache(xkey);
236
15.4k
    if (init_items)
237
14.2k
        return init_items;
238
239
1.16k
    if ((strncmp(xkey, "epsg:", 5) == 0 || strncmp(xkey, "IGNF:", 5) == 0)) {
240
761
        char unused[256];
241
761
        char initname[5];
242
761
        int exists;
243
244
761
        strncpy(initname, xkey, 4);
245
761
        initname[4] = 0;
246
247
761
        if (strncmp(xkey, "epsg:", 5) == 0) {
248
446
            exists = ctx->epsg_file_exists;
249
446
            if (exists < 0) {
250
83
                exists = pj_find_file(ctx, initname, unused, sizeof(unused));
251
83
                ctx->epsg_file_exists = exists;
252
83
            }
253
446
        } else {
254
315
            exists = pj_find_file(ctx, initname, unused, sizeof(unused));
255
315
        }
256
257
761
        if (!exists) {
258
761
            char szInitStr[7 + 64];
259
761
            PJ *src;
260
761
            const char *proj_string;
261
262
761
            proj_context_errno_set(ctx, 0);
263
264
761
            if (!allow_init_epsg) {
265
0
                pj_log(ctx, PJ_LOG_TRACE, "%s expansion disallowed", xkey);
266
0
                return nullptr;
267
0
            }
268
761
            if (strlen(xkey) > 64) {
269
177
                return nullptr;
270
177
            }
271
584
            strcpy(szInitStr, "+init=");
272
584
            strcat(szInitStr, xkey);
273
274
584
            auto old_proj4_init_rules = ctx->use_proj4_init_rules;
275
584
            ctx->use_proj4_init_rules = true;
276
584
            src = proj_create(ctx, szInitStr);
277
584
            ctx->use_proj4_init_rules = old_proj4_init_rules;
278
584
            if (!src) {
279
296
                return nullptr;
280
296
            }
281
282
288
            proj_string = proj_as_proj_string(ctx, src, PJ_PROJ_4, nullptr);
283
288
            if (!proj_string) {
284
25
                proj_destroy(src);
285
25
                return nullptr;
286
25
            }
287
263
            definition = (char *)calloc(1, strlen(proj_string) + 1);
288
263
            if (definition) {
289
263
                strcpy(definition, proj_string);
290
263
            }
291
292
263
            proj_destroy(src);
293
263
        }
294
761
    }
295
296
662
    if (!definition) {
297
        /* If not, we must read it from file */
298
399
        pj_log(ctx, PJ_LOG_TRACE,
299
399
               "get_init: searching on in init files for [%s]", xkey);
300
399
        definition = get_init_string(ctx, xkey);
301
399
    }
302
303
662
    if (nullptr == definition)
304
399
        return nullptr;
305
263
    init_items = string_to_paralist(ctx, definition);
306
263
    if (init_items)
307
263
        pj_log(ctx, PJ_LOG_TRACE, "get_init: got [%s], paralist[0,1]: [%s,%s]",
308
263
               definition, init_items->param,
309
263
               init_items->next ? init_items->next->param : "(empty)");
310
263
    free(definition);
311
263
    if (nullptr == init_items)
312
0
        return nullptr;
313
314
    /* We found it in file - now insert into the cache, before returning */
315
263
    pj_insert_initcache(xkey, init_items);
316
263
    return init_items;
317
263
}
318
319
97.0k
static void append_default_ellipsoid_to_paralist(paralist *start) {
320
97.0k
    if (nullptr == start)
321
0
        return;
322
323
    /* Set defaults, unless inhibited (either explicitly through a "no_defs"
324
     * token
325
     */
326
    /* or implicitly, because we are initializing a pipeline) */
327
97.0k
    if (pj_param_exists(start, "no_defs"))
328
14.7k
        return;
329
82.2k
    auto proj = pj_param_exists(start, "proj");
330
82.2k
    if (nullptr == proj)
331
0
        return;
332
82.2k
    if (strlen(proj->param) < 6)
333
0
        return;
334
82.2k
    if (0 == strcmp("pipeline", proj->param + 5))
335
6.86k
        return;
336
337
    /* Don't default ellipse if datum, ellps or any ellipsoid information is set
338
     */
339
75.4k
    if (pj_param_exists(start, "datum"))
340
590
        return;
341
74.8k
    if (pj_param_exists(start, "ellps"))
342
19.9k
        return;
343
54.8k
    if (pj_param_exists(start, "a"))
344
9.47k
        return;
345
45.3k
    if (pj_param_exists(start, "b"))
346
49
        return;
347
45.3k
    if (pj_param_exists(start, "rf"))
348
21
        return;
349
45.3k
    if (pj_param_exists(start, "f"))
350
351
        return;
351
44.9k
    if (pj_param_exists(start, "e"))
352
347
        return;
353
44.6k
    if (pj_param_exists(start, "es"))
354
331
        return;
355
356
    /* Locate end of start-list */
357
44.2k
    paralist *last = nullptr;
358
629k
    for (last = start; last->next; last = last->next)
359
585k
        ;
360
361
    /* If we're here, it's OK to append the current default item */
362
44.2k
    last->next = pj_mkparam("ellps=GRS80");
363
44.2k
}
364
365
/*****************************************************************************/
366
static paralist *pj_expand_init_internal(PJ_CONTEXT *ctx, paralist *init,
367
15.4k
                                         int allow_init_epsg) {
368
    /******************************************************************************
369
    Append expansion of <key> to the paralist <init>. The expansion is appended,
370
    rather than inserted at <init>'s place, since <init> may contain
371
    overrides to the expansion. These must take precedence, and hence come first
372
    in the expanded list.
373
374
    Consider e.g. the key 'foo:bar' which (hypothetically) expands to 'proj=utm
375
    zone=32 ellps=GRS80', i.e. a UTM projection on the GRS80 ellipsoid.
376
377
    The expression 'init=foo:bar ellps=intl' will then expand to:
378
379
               'init=foo:bar ellps=intl proj=utm zone=32 ellps=GRS80',
380
381
    where 'ellps=intl' precedes 'ellps=GRS80', and hence takes precedence,
382
    turning the expansion into an UTM projection on the Hayford ellipsoid.
383
384
    Note that 'init=foo:bar' stays in the list. It is ignored after expansion.
385
386
    ******************************************************************************/
387
15.4k
    paralist *last;
388
15.4k
    paralist *expn;
389
390
    /* Nowhere to start? */
391
15.4k
    if (nullptr == init)
392
0
        return nullptr;
393
394
15.4k
    expn = get_init(ctx, init->param, allow_init_epsg);
395
396
    /* Nothing in expansion? */
397
15.4k
    if (nullptr == expn)
398
897
        return nullptr;
399
400
    /* Locate  the end of the list */
401
78.4k
    for (last = init; last && last->next; last = last->next)
402
63.9k
        ;
403
404
    /* Then append and return */
405
14.5k
    last->next = expn;
406
14.5k
    return init;
407
15.4k
}
408
409
1.72k
paralist *pj_expand_init(PJ_CONTEXT *ctx, paralist *init) {
410
1.72k
    return pj_expand_init_internal(ctx, init, TRUE);
411
1.72k
}
412
413
/************************************************************************/
414
/*                              pj_init()                               */
415
/*                                                                      */
416
/*      Main entry point for initialing a PJ projections                */
417
/*      definition.  Note that the projection specific function is      */
418
/*      called to do the initial allocation so it can be created        */
419
/*      large enough to hold projection specific parameters.            */
420
/************************************************************************/
421
422
97.4k
static PJ_CONSTRUCTOR locate_constructor(const char *name) {
423
97.4k
    int i;
424
97.4k
    const char *s;
425
97.4k
    const PJ_OPERATIONS *operations;
426
97.4k
    operations = proj_list_operations();
427
8.81M
    for (i = 0; (s = operations[i].id) && strcmp(name, s); ++i)
428
8.71M
        ;
429
97.4k
    if (nullptr == s)
430
461
        return nullptr;
431
97.0k
    return (PJ_CONSTRUCTOR)operations[i].proj;
432
97.4k
}
433
434
PJ *pj_init_ctx_with_allow_init_epsg(PJ_CONTEXT *ctx, int argc, char **argv,
435
98.0k
                                     int allow_init_epsg) {
436
98.0k
    const char *s;
437
98.0k
    char *name;
438
98.0k
    PJ_CONSTRUCTOR proj;
439
98.0k
    paralist *curr, *init, *start;
440
98.0k
    int i;
441
98.0k
    int err;
442
98.0k
    PJ *PIN = nullptr;
443
98.0k
    int n_pipelines = 0;
444
98.0k
    int n_inits = 0;
445
98.0k
    const PJ_UNITS *units;
446
98.0k
    const PJ_PRIME_MERIDIANS *prime_meridians;
447
448
98.0k
    if (nullptr == ctx)
449
0
        ctx = pj_get_default_ctx();
450
451
98.0k
    ctx->last_errno = 0;
452
453
98.0k
    if (argc <= 0) {
454
5
        pj_log(ctx, PJ_LOG_ERROR, _("No arguments"));
455
5
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_MISSING_ARG);
456
5
        return nullptr;
457
5
    }
458
459
    /* count occurrences of pipelines and inits */
460
1.35M
    for (i = 0; i < argc; ++i) {
461
1.25M
        if (!strcmp(argv[i], "+proj=pipeline") ||
462
1.25M
            !strcmp(argv[i], "proj=pipeline"))
463
10.7k
            n_pipelines++;
464
1.25M
        if (!strncmp(argv[i], "+init=", 6) || !strncmp(argv[i], "init=", 5))
465
20.0k
            n_inits++;
466
1.25M
    }
467
468
    /* can't have nested pipelines directly */
469
98.0k
    if (n_pipelines > 1) {
470
24
        pj_log(ctx, PJ_LOG_ERROR, _("Nested pipelines are not supported"));
471
24
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_WRONG_SYNTAX);
472
24
        return nullptr;
473
24
    }
474
475
    /* don't allow more than one +init in non-pipeline operations */
476
98.0k
    if (n_pipelines == 0 && n_inits > 1) {
477
24
        pj_log(ctx, PJ_LOG_ERROR, _("Too many inits"));
478
24
        proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_WRONG_SYNTAX);
479
24
        return nullptr;
480
24
    }
481
482
    /* put arguments into internal linked list */
483
98.0k
    start = curr = pj_mkparam(argv[0]);
484
98.0k
    if (!curr) {
485
0
        free_params(ctx, start, PROJ_ERR_OTHER /*ENOMEM*/);
486
0
        return nullptr;
487
0
    }
488
489
1.25M
    for (i = 1; i < argc; ++i) {
490
1.15M
        curr->next = pj_mkparam(argv[i]);
491
1.15M
        if (!curr->next) {
492
0
            free_params(ctx, start, PROJ_ERR_OTHER /*ENOMEM*/);
493
0
            return nullptr;
494
0
        }
495
1.15M
        curr = curr->next;
496
1.15M
    }
497
498
    /* Only expand '+init's in non-pipeline operations. '+init's in pipelines
499
     * are
500
     */
501
    /* expanded in the individual pipeline steps during pipeline initialization.
502
     */
503
    /* Potentially this leads to many nested pipelines, which shouldn't be a */
504
    /* problem when '+init's are expanded as late as possible. */
505
98.0k
    init = pj_param_exists(start, "init");
506
98.0k
    if (init && n_pipelines == 0) {
507
13.6k
        init = pj_expand_init_internal(ctx, init, allow_init_epsg);
508
13.6k
        if (!init) {
509
290
            free_params(ctx, start, PROJ_ERR_INVALID_OP_WRONG_SYNTAX);
510
290
            return nullptr;
511
290
        }
512
13.6k
    }
513
97.7k
    if (ctx->last_errno) {
514
1
        free_params(ctx, start, ctx->last_errno);
515
1
        return nullptr;
516
1
    }
517
518
    /* Find projection selection */
519
97.7k
    curr = pj_param_exists(start, "proj");
520
97.7k
    if (nullptr == curr) {
521
222
        pj_log(ctx, PJ_LOG_ERROR, _("Missing proj"));
522
222
        free_params(ctx, start, PROJ_ERR_INVALID_OP_MISSING_ARG);
523
222
        return nullptr;
524
222
    }
525
97.5k
    name = curr->param;
526
97.5k
    if (strlen(name) < 6) {
527
10
        pj_log(ctx, PJ_LOG_ERROR, _("Invalid value for proj"));
528
10
        free_params(ctx, start, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
529
10
        return nullptr;
530
10
    }
531
97.4k
    name += 5;
532
533
97.4k
    proj = locate_constructor(name);
534
97.4k
    if (nullptr == proj) {
535
461
        pj_log(ctx, PJ_LOG_ERROR, _("Unknown projection"));
536
461
        free_params(ctx, start, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
537
461
        return nullptr;
538
461
    }
539
540
97.0k
    append_default_ellipsoid_to_paralist(start);
541
542
    /* Allocate projection structure */
543
97.0k
    PIN = proj(nullptr);
544
97.0k
    if (nullptr == PIN) {
545
0
        free_params(ctx, start, PROJ_ERR_OTHER /*ENOMEM*/);
546
0
        return nullptr;
547
0
    }
548
549
97.0k
    PIN->ctx = ctx;
550
97.0k
    PIN->params = start;
551
97.0k
    PIN->is_latlong = 0;
552
97.0k
    PIN->is_geocent = 0;
553
97.0k
    PIN->is_long_wrap_set = 0;
554
97.0k
    PIN->long_wrap_center = 0.0;
555
97.0k
    strcpy(PIN->axis, "enu");
556
557
    /* Set datum parameters. Similarly to +init parameters we want to expand */
558
    /* +datum parameters as late as possible when dealing with pipelines. */
559
    /* otherwise only the first occurrence of +datum will be expanded and that
560
     */
561
97.0k
    if (n_pipelines == 0) {
562
86.3k
        if (pj_datum_set(ctx, start, PIN))
563
38
            return pj_default_destructor(PIN, proj_errno(PIN));
564
86.3k
    }
565
566
96.9k
    err = pj_ellipsoid(PIN);
567
568
96.9k
    if (err) {
569
        /* Didn't get an ellps, but doesn't need one: Get a free WGS84 */
570
10.3k
        if (PIN->need_ellps) {
571
124
            pj_log(ctx, PJ_LOG_ERROR,
572
124
                   _("pj_init_ctx: Must specify ellipsoid or sphere"));
573
124
            return pj_default_destructor(PIN, proj_errno(PIN));
574
10.2k
        } else {
575
10.2k
            if (PIN->a == 0)
576
10.1k
                proj_errno_reset(PIN);
577
10.2k
            PIN->f = 1.0 / 298.257223563;
578
10.2k
            PIN->a = 6378137.0;
579
10.2k
            PIN->es = PIN->f * (2 - PIN->f);
580
10.2k
        }
581
10.3k
    }
582
96.8k
    PIN->a_orig = PIN->a;
583
96.8k
    PIN->es_orig = PIN->es;
584
96.8k
    if (pj_calc_ellipsoid_params(PIN, PIN->a, PIN->es))
585
0
        return pj_default_destructor(PIN,
586
0
                                     PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
587
588
    /* Now that we have ellipse information check for WGS84 datum */
589
96.8k
    if (PIN->datum_type == PJD_3PARAM && PIN->datum_params[0] == 0.0 &&
590
5.66k
        PIN->datum_params[1] == 0.0 && PIN->datum_params[2] == 0.0 &&
591
4.72k
        PIN->a == 6378137.0 &&
592
4.29k
        ABS(PIN->es - 0.006694379990) < 0.000000000050) /*WGS84/GRS80*/
593
3.96k
    {
594
3.96k
        PIN->datum_type = PJD_WGS84;
595
3.96k
    }
596
597
    /* Set PIN->geoc coordinate system */
598
96.8k
    PIN->geoc = (PIN->es != 0.0 && pj_param(ctx, start, "bgeoc").i);
599
600
    /* Over-ranging flag */
601
96.8k
    PIN->over = pj_param(ctx, start, "bover").i;
602
96.8k
    if (ctx->forceOver) {
603
0
        PIN->over = ctx->forceOver;
604
0
    }
605
606
    /* Vertical datum geoid grids */
607
96.8k
    PIN->has_geoid_vgrids = pj_param(ctx, start, "tgeoidgrids").i;
608
96.8k
    if (PIN->has_geoid_vgrids) /* we need to mark it as used. */
609
3.98k
        pj_param(ctx, start, "sgeoidgrids");
610
611
    /* Longitude center for wrapping */
612
96.8k
    PIN->is_long_wrap_set = pj_param(ctx, start, "tlon_wrap").i;
613
96.8k
    if (PIN->is_long_wrap_set) {
614
8
        PIN->long_wrap_center = pj_param(ctx, start, "rlon_wrap").f;
615
        /* Don't accept excessive values otherwise we might perform badly */
616
        /* when correcting longitudes around it */
617
        /* The test is written this way to error on long_wrap_center "=" NaN */
618
8
        if (!(fabs(PIN->long_wrap_center) < 10 * M_TWOPI)) {
619
0
            proj_log_error(PIN, _("Invalid value for lon_wrap"));
620
0
            return pj_default_destructor(PIN,
621
0
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
622
0
        }
623
8
    }
624
625
    /* Axis orientation */
626
96.8k
    if ((pj_param(ctx, start, "saxis").s) != nullptr) {
627
1.95k
        const char *axis_legal = "ewnsud";
628
1.95k
        const char *axis_arg = pj_param(ctx, start, "saxis").s;
629
1.95k
        if (strlen(axis_arg) != 3) {
630
7
            proj_log_error(PIN, _("Invalid value for axis"));
631
7
            return pj_default_destructor(PIN,
632
7
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
633
7
        }
634
635
1.94k
        if (strchr(axis_legal, axis_arg[0]) == nullptr ||
636
1.94k
            strchr(axis_legal, axis_arg[1]) == nullptr ||
637
1.93k
            strchr(axis_legal, axis_arg[2]) == nullptr) {
638
13
            proj_log_error(PIN, _("Invalid value for axis"));
639
13
            return pj_default_destructor(PIN,
640
13
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
641
13
        }
642
643
        /* TODO: it would be nice to validate we don't have on axis repeated */
644
1.93k
        strcpy(PIN->axis, axis_arg);
645
1.93k
    }
646
647
    /* Central meridian */
648
96.8k
    PIN->lam0 = pj_param(ctx, start, "rlon_0").f;
649
650
    /* Central latitude */
651
96.8k
    PIN->phi0 = pj_param(ctx, start, "rlat_0").f;
652
96.8k
    if (fabs(PIN->phi0) > M_HALFPI) {
653
8
        proj_log_error(PIN,
654
8
                       _("Invalid value for lat_0: |lat_0| should be <= 90°"));
655
8
        return pj_default_destructor(PIN,
656
8
                                     PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
657
8
    }
658
659
    /* False easting and northing */
660
96.8k
    PIN->x0 = pj_param(ctx, start, "dx_0").f;
661
96.8k
    PIN->y0 = pj_param(ctx, start, "dy_0").f;
662
96.8k
    PIN->z0 = pj_param(ctx, start, "dz_0").f;
663
96.8k
    PIN->t0 = pj_param(ctx, start, "dt_0").f;
664
665
    /* General scaling factor */
666
96.8k
    if (pj_param(ctx, start, "tk_0").i)
667
497
        PIN->k0 = pj_param(ctx, start, "dk_0").f;
668
96.3k
    else if (pj_param(ctx, start, "tk").i)
669
6.18k
        PIN->k0 = pj_param(ctx, start, "dk").f;
670
90.1k
    else
671
90.1k
        PIN->k0 = 1.;
672
96.8k
    if (PIN->k0 <= 0.) {
673
108
        proj_log_error(PIN, _("Invalid value for k/k_0: it should be > 0"));
674
108
        return pj_default_destructor(PIN,
675
108
                                     PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
676
108
    }
677
678
    /* Set units */
679
96.7k
    units = pj_list_linear_units();
680
96.7k
    s = nullptr;
681
96.7k
    if ((name = pj_param(ctx, start, "sunits").s) != nullptr) {
682
27.7k
        for (i = 0; (s = units[i].id) && strcmp(name, s); ++i)
683
15.3k
            ;
684
12.4k
        if (!s) {
685
15
            proj_log_error(PIN, _("Invalid value for units"));
686
15
            return pj_default_destructor(PIN,
687
15
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
688
15
        }
689
12.4k
        s = units[i].to_meter;
690
12.4k
    }
691
96.7k
    if (s || (s = pj_param(ctx, start, "sto_meter").s)) {
692
13.3k
        char *end_ptr = const_cast<char *>(s);
693
13.3k
        PIN->to_meter = pj_strtod(s, &end_ptr);
694
13.3k
        s = end_ptr;
695
13.3k
        if (*s == '/') { /* ratio number */
696
114
            ++s;
697
114
            double denom = pj_strtod(s, nullptr);
698
114
            if (denom == 0.0) {
699
0
                proj_log_error(PIN,
700
0
                               _("Invalid value for to_meter donominator"));
701
0
                return pj_default_destructor(
702
0
                    PIN, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
703
0
            }
704
114
            PIN->to_meter /= denom;
705
114
        }
706
13.3k
        if (PIN->to_meter <= 0.0) {
707
8
            proj_log_error(PIN, _("Invalid value for to_meter"));
708
8
            return pj_default_destructor(PIN,
709
8
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
710
8
        }
711
13.2k
        PIN->fr_meter = 1 / PIN->to_meter;
712
713
13.2k
    } else
714
83.4k
        PIN->to_meter = PIN->fr_meter = 1.;
715
716
    /* Set vertical units */
717
96.7k
    s = nullptr;
718
96.7k
    if ((name = pj_param(ctx, start, "svunits").s) != nullptr) {
719
12.3k
        for (i = 0; (s = units[i].id) && strcmp(name, s); ++i)
720
8.27k
            ;
721
4.08k
        if (!s) {
722
17
            proj_log_error(PIN, _("Invalid value for vunits"));
723
17
            return pj_default_destructor(PIN,
724
17
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
725
17
        }
726
4.07k
        s = units[i].to_meter;
727
4.07k
    }
728
96.6k
    if (s || (s = pj_param(ctx, start, "svto_meter").s)) {
729
4.25k
        char *end_ptr = const_cast<char *>(s);
730
4.25k
        PIN->vto_meter = pj_strtod(s, &end_ptr);
731
4.25k
        s = end_ptr;
732
4.25k
        if (*s == '/') { /* ratio number */
733
62
            ++s;
734
62
            double denom = pj_strtod(s, nullptr);
735
62
            if (denom == 0.0) {
736
7
                proj_log_error(PIN,
737
7
                               _("Invalid value for vto_meter donominator"));
738
7
                return pj_default_destructor(
739
7
                    PIN, PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
740
7
            }
741
55
            PIN->vto_meter /= denom;
742
55
        }
743
4.24k
        if (PIN->vto_meter <= 0.0) {
744
13
            proj_log_error(PIN, _("Invalid value for vto_meter"));
745
13
            return pj_default_destructor(PIN,
746
13
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
747
13
        }
748
4.23k
        PIN->vfr_meter = 1. / PIN->vto_meter;
749
92.4k
    } else {
750
92.4k
        PIN->vto_meter = PIN->to_meter;
751
92.4k
        PIN->vfr_meter = PIN->fr_meter;
752
92.4k
    }
753
754
    /* Prime meridian */
755
96.6k
    prime_meridians = proj_list_prime_meridians();
756
96.6k
    s = nullptr;
757
96.6k
    if ((name = pj_param(ctx, start, "spm").s) != nullptr) {
758
1.18k
        const char *value = nullptr;
759
1.18k
        char *next_str = nullptr;
760
761
14.7k
        for (i = 0; prime_meridians[i].id != nullptr; ++i) {
762
13.9k
            if (strcmp(name, prime_meridians[i].id) == 0) {
763
356
                value = prime_meridians[i].defn;
764
356
                break;
765
356
            }
766
13.9k
        }
767
768
1.18k
        if (value == nullptr &&
769
828
            (dmstor_ctx(ctx, name, &next_str) != 0.0 || *name == '0') &&
770
815
            *next_str == '\0')
771
814
            value = name;
772
773
1.18k
        if (!value) {
774
14
            proj_log_error(PIN, _("Invalid value for pm"));
775
14
            return pj_default_destructor(PIN,
776
14
                                         PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
777
14
        }
778
1.17k
        PIN->from_greenwich = dmstor_ctx(ctx, value, nullptr);
779
1.17k
    } else
780
95.4k
        PIN->from_greenwich = 0.0;
781
782
    /* Private object for the geodesic functions */
783
96.6k
    PIN->geod = static_cast<struct geod_geodesic *>(
784
96.6k
        calloc(1, sizeof(struct geod_geodesic)));
785
96.6k
    if (nullptr == PIN->geod)
786
0
        return pj_default_destructor(PIN, PROJ_ERR_OTHER /*ENOMEM*/);
787
96.6k
    geod_init(PIN->geod, PIN->a, PIN->f);
788
789
    /* Projection specific initialization */
790
96.6k
    err = proj_errno_reset(PIN);
791
96.6k
    PIN = proj(PIN);
792
96.6k
    if (proj_errno(PIN)) {
793
4.90k
        proj_destroy(PIN);
794
4.90k
        return nullptr;
795
4.90k
    }
796
91.7k
    proj_errno_restore(PIN, err);
797
91.7k
    return PIN;
798
96.6k
}