Coverage Report

Created: 2025-06-09 07:29

/src/gdal/ogr/ogrspatialreference.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRSpatialReference class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999,  Les Technologies SoftMap Inc.
9
 * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_spatialref.h"
16
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <limits>
23
#include <string>
24
#include <mutex>
25
#include <set>
26
#include <vector>
27
28
#include "cpl_atomic_ops.h"
29
#include "cpl_conv.h"
30
#include "cpl_csv.h"
31
#include "cpl_error.h"
32
#include "cpl_error_internal.h"
33
#include "cpl_http.h"
34
#include "cpl_json.h"
35
#include "cpl_multiproc.h"
36
#include "cpl_string.h"
37
#include "cpl_vsi.h"
38
#include "ogr_core.h"
39
#include "ogr_p.h"
40
#include "ogr_proj_p.h"
41
#include "ogr_srs_api.h"
42
#include "ogrmitabspatialref.h"
43
44
#include "proj.h"
45
#include "proj_experimental.h"
46
#include "proj_constants.h"
47
48
// Exists since 8.0.1
49
#ifndef PROJ_AT_LEAST_VERSION
50
#define PROJ_COMPUTE_VERSION(maj, min, patch)                                  \
51
    ((maj)*10000 + (min)*100 + (patch))
52
#define PROJ_VERSION_NUMBER                                                    \
53
    PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR,               \
54
                         PROJ_VERSION_PATCH)
55
#define PROJ_AT_LEAST_VERSION(maj, min, patch)                                 \
56
    (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
57
#endif
58
59
6.37k
#define STRINGIFY(s) #s
60
6.37k
#define XSTRINGIFY(s) STRINGIFY(s)
61
62
struct OGRSpatialReference::Private
63
{
64
    struct Listener : public OGR_SRSNode::Listener
65
    {
66
        OGRSpatialReference::Private *m_poObj = nullptr;
67
68
329k
        explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
69
329k
        {
70
329k
        }
71
72
        Listener(const Listener &) = delete;
73
        Listener &operator=(const Listener &) = delete;
74
75
        void notifyChange(OGR_SRSNode *) override;
76
    };
77
78
    OGRSpatialReference *m_poSelf = nullptr;
79
    PJ *m_pj_crs = nullptr;
80
81
    // Temporary state used for object construction
82
    PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
83
    CPLString m_osPrimeMeridianName{};
84
    CPLString m_osAngularUnits{};
85
    CPLString m_osLinearUnits{};
86
    CPLString m_osAxisName[3]{};
87
88
    std::vector<std::string> m_wktImportWarnings{};
89
    std::vector<std::string> m_wktImportErrors{};
90
    CPLString m_osAreaName{};
91
92
    bool m_bIsThreadSafe = false;
93
    bool m_bNodesChanged = false;
94
    bool m_bNodesWKT2 = false;
95
    OGR_SRSNode *m_poRoot = nullptr;
96
97
    double dfFromGreenwich = 0.0;
98
    double dfToMeter = 0.0;
99
    double dfToDegrees = 0.0;
100
    double m_dfAngularUnitToRadian = 0.0;
101
102
    int nRefCount = 1;
103
    int bNormInfoSet = FALSE;
104
105
    PJ *m_pj_geod_base_crs_temp = nullptr;
106
    PJ *m_pj_proj_crs_cs_temp = nullptr;
107
108
    bool m_pj_crs_modified_during_demote = false;
109
    PJ *m_pj_bound_crs_target = nullptr;
110
    PJ *m_pj_bound_crs_co = nullptr;
111
    PJ *m_pj_crs_backup = nullptr;
112
    OGR_SRSNode *m_poRootBackup = nullptr;
113
114
    bool m_bMorphToESRI = false;
115
    bool m_bHasCenterLong = false;
116
117
    std::shared_ptr<Listener> m_poListener{};
118
119
    std::recursive_mutex m_mutex{};
120
121
    OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
122
    std::vector<int> m_axisMapping{1, 2, 3};
123
124
    double m_coordinateEpoch = 0;  // as decimal year
125
126
    explicit Private(OGRSpatialReference *poSelf);
127
    ~Private();
128
    Private(const Private &) = delete;
129
    Private &operator=(const Private &) = delete;
130
131
    void SetThreadSafe()
132
0
    {
133
0
        m_bIsThreadSafe = true;
134
0
    }
135
136
    void clear();
137
    void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
138
    void setRoot(OGR_SRSNode *poRoot);
139
    void refreshProjObj();
140
    void nodesChanged();
141
    void refreshRootFromProjObj();
142
    void invalidateNodes();
143
144
    void setMorphToESRI(bool b);
145
146
    PJ *getGeodBaseCRS();
147
    PJ *getProjCRSCoordSys();
148
149
    const char *getProjCRSName();
150
    OGRErr replaceConversionAndUnref(PJ *conv);
151
152
    void demoteFromBoundCRS();
153
    void undoDemoteFromBoundCRS();
154
155
    PJ_CONTEXT *getPROJContext()
156
2.42M
    {
157
2.42M
        return OSRGetProjTLSContext();
158
2.42M
    }
159
160
    const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
161
162
    void refreshAxisMapping();
163
164
    // This structures enables locking during calls to OGRSpatialReference
165
    // public methods. Locking is only needed for instances of
166
    // OGRSpatialReference that have been asked to be thread-safe at
167
    // construction.
168
    // The lock is not just for a single call to OGRSpatialReference::Private,
169
    // but for the series of calls done by a OGRSpatialReference method.
170
    // We need a recursive mutex, because some OGRSpatialReference methods
171
    // may call other ones.
172
    struct OptionalLockGuard
173
    {
174
        Private &m_private;
175
176
1.43M
        explicit OptionalLockGuard(Private *p) : m_private(*p)
177
1.43M
        {
178
1.43M
            if (m_private.m_bIsThreadSafe)
179
0
                m_private.m_mutex.lock();
180
1.43M
        }
181
182
        ~OptionalLockGuard()
183
1.43M
        {
184
1.43M
            if (m_private.m_bIsThreadSafe)
185
0
                m_private.m_mutex.unlock();
186
1.43M
        }
187
    };
188
189
    inline OptionalLockGuard GetOptionalLockGuard()
190
1.43M
    {
191
1.43M
        return OptionalLockGuard(this);
192
1.43M
    }
193
};
194
195
void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
196
6.37M
{
197
6.37M
    m_poObj->nodesChanged();
198
6.37M
}
199
200
#define TAKE_OPTIONAL_LOCK()                                                   \
201
1.43M
    auto lock = d->GetOptionalLockGuard();                                     \
202
1.43M
    CPL_IGNORE_RET_VAL(lock)
203
204
static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
205
329k
{
206
329k
    const char *pszDefaultAMS =
207
329k
        CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
208
329k
    if (pszDefaultAMS)
209
0
    {
210
0
        if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
211
0
            return OAMS_AUTHORITY_COMPLIANT;
212
0
        else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
213
0
            return OAMS_TRADITIONAL_GIS_ORDER;
214
0
        else
215
0
        {
216
0
            CPLError(CE_Failure, CPLE_AppDefined,
217
0
                     "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
218
0
                     pszDefaultAMS);
219
0
        }
220
0
    }
221
329k
    return OAMS_AUTHORITY_COMPLIANT;
222
329k
}
223
224
OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
225
329k
    : m_poSelf(poSelf),
226
329k
      m_poListener(std::shared_ptr<Listener>(new Listener(this)))
227
329k
{
228
    // Get the default value for m_axisMappingStrategy from the
229
    // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
230
329k
    m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
231
329k
}
232
233
OGRSpatialReference::Private::~Private()
234
329k
{
235
    // In case we destroy the object not in the thread that created it,
236
    // we need to reassign the PROJ context. Having the context bundled inside
237
    // PJ* deeply sucks...
238
329k
    auto ctxt = getPROJContext();
239
240
329k
    proj_assign_context(m_pj_crs, ctxt);
241
329k
    proj_destroy(m_pj_crs);
242
243
329k
    proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
244
329k
    proj_destroy(m_pj_geod_base_crs_temp);
245
246
329k
    proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
247
329k
    proj_destroy(m_pj_proj_crs_cs_temp);
248
249
329k
    proj_assign_context(m_pj_bound_crs_target, ctxt);
250
329k
    proj_destroy(m_pj_bound_crs_target);
251
252
329k
    proj_assign_context(m_pj_bound_crs_co, ctxt);
253
329k
    proj_destroy(m_pj_bound_crs_co);
254
255
329k
    proj_assign_context(m_pj_crs_backup, ctxt);
256
329k
    proj_destroy(m_pj_crs_backup);
257
258
329k
    delete m_poRootBackup;
259
329k
    delete m_poRoot;
260
329k
}
261
262
void OGRSpatialReference::Private::clear()
263
252k
{
264
252k
    proj_assign_context(m_pj_crs, getPROJContext());
265
252k
    proj_destroy(m_pj_crs);
266
252k
    m_pj_crs = nullptr;
267
268
252k
    delete m_poRoot;
269
252k
    m_poRoot = nullptr;
270
252k
    m_bNodesChanged = false;
271
272
252k
    m_wktImportWarnings.clear();
273
252k
    m_wktImportErrors.clear();
274
275
252k
    m_pj_crs_modified_during_demote = false;
276
252k
    m_pjType = PJ_TYPE_UNKNOWN;
277
252k
    m_osPrimeMeridianName.clear();
278
252k
    m_osAngularUnits.clear();
279
252k
    m_osLinearUnits.clear();
280
281
252k
    bNormInfoSet = FALSE;
282
252k
    dfFromGreenwich = 1.0;
283
252k
    dfToMeter = 1.0;
284
252k
    dfToDegrees = 1.0;
285
252k
    m_dfAngularUnitToRadian = 0.0;
286
287
252k
    m_bMorphToESRI = false;
288
252k
    m_bHasCenterLong = false;
289
290
252k
    m_coordinateEpoch = 0.0;
291
252k
}
292
293
void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
294
88.3k
{
295
88.3k
    m_poRoot = poRoot;
296
88.3k
    if (m_poRoot)
297
88.3k
    {
298
88.3k
        m_poRoot->RegisterListener(m_poListener);
299
88.3k
    }
300
88.3k
    nodesChanged();
301
88.3k
}
302
303
void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
304
                                            bool doRefreshAxisMapping)
305
517k
{
306
517k
    auto ctxt = getPROJContext();
307
308
517k
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
309
517k
    if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
310
0
    {
311
0
        const double dfEpoch =
312
0
            proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
313
0
        if (!std::isnan(dfEpoch))
314
0
        {
315
0
            m_poSelf->SetCoordinateEpoch(dfEpoch);
316
0
        }
317
0
        auto crs = proj_get_source_crs(ctxt, pj_crsIn);
318
0
        proj_destroy(pj_crsIn);
319
0
        pj_crsIn = crs;
320
0
    }
321
517k
#endif
322
323
517k
    proj_assign_context(m_pj_crs, ctxt);
324
517k
    proj_destroy(m_pj_crs);
325
517k
    m_pj_crs = pj_crsIn;
326
517k
    if (m_pj_crs)
327
515k
    {
328
515k
        m_pjType = proj_get_type(m_pj_crs);
329
515k
    }
330
517k
    if (m_pj_crs_backup)
331
651
    {
332
651
        m_pj_crs_modified_during_demote = true;
333
651
    }
334
517k
    invalidateNodes();
335
517k
    if (doRefreshAxisMapping)
336
516k
    {
337
516k
        refreshAxisMapping();
338
516k
    }
339
517k
}
340
341
void OGRSpatialReference::Private::refreshProjObj()
342
819k
{
343
819k
    if (m_bNodesChanged && m_poRoot)
344
80.4k
    {
345
80.4k
        char *pszWKT = nullptr;
346
80.4k
        m_poRoot->exportToWkt(&pszWKT);
347
80.4k
        auto poRootBackup = m_poRoot;
348
80.4k
        m_poRoot = nullptr;
349
80.4k
        const double dfCoordinateEpochBackup = m_coordinateEpoch;
350
80.4k
        clear();
351
80.4k
        m_coordinateEpoch = dfCoordinateEpochBackup;
352
80.4k
        m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
353
354
80.4k
        const char *const options[] = {
355
80.4k
            "STRICT=NO",
356
80.4k
#if PROJ_AT_LEAST_VERSION(9, 1, 0)
357
80.4k
            "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
358
80.4k
#endif
359
80.4k
            nullptr
360
80.4k
        };
361
80.4k
        PROJ_STRING_LIST warnings = nullptr;
362
80.4k
        PROJ_STRING_LIST errors = nullptr;
363
80.4k
        setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
364
80.4k
                                      &warnings, &errors));
365
126k
        for (auto iter = warnings; iter && *iter; ++iter)
366
45.7k
        {
367
45.7k
            m_wktImportWarnings.push_back(*iter);
368
45.7k
        }
369
123k
        for (auto iter = errors; iter && *iter; ++iter)
370
42.8k
        {
371
42.8k
            m_wktImportErrors.push_back(*iter);
372
42.8k
        }
373
80.4k
        proj_string_list_destroy(warnings);
374
80.4k
        proj_string_list_destroy(errors);
375
376
80.4k
        CPLFree(pszWKT);
377
378
80.4k
        m_poRoot = poRootBackup;
379
80.4k
        m_bNodesChanged = false;
380
80.4k
    }
381
819k
}
382
383
void OGRSpatialReference::Private::refreshRootFromProjObj()
384
98.4k
{
385
98.4k
    CPLAssert(m_poRoot == nullptr);
386
387
98.4k
    if (m_pj_crs)
388
71.5k
    {
389
71.5k
        CPLStringList aosOptions;
390
71.5k
        if (!m_bMorphToESRI)
391
71.5k
        {
392
71.5k
            aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
393
71.5k
            aosOptions.SetNameValue("MULTILINE", "NO");
394
71.5k
        }
395
71.5k
        aosOptions.SetNameValue("STRICT", "NO");
396
397
71.5k
        const char *pszWKT;
398
71.5k
        {
399
71.5k
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
400
71.5k
            pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
401
71.5k
                                 m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
402
71.5k
                                 aosOptions.List());
403
71.5k
            m_bNodesWKT2 = false;
404
71.5k
        }
405
71.5k
        if (!m_bMorphToESRI && pszWKT == nullptr)
406
183
        {
407
183
            pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
408
183
                                 aosOptions.List());
409
183
            m_bNodesWKT2 = true;
410
183
        }
411
71.5k
        if (pszWKT)
412
71.5k
        {
413
71.5k
            auto root = new OGR_SRSNode();
414
71.5k
            setRoot(root);
415
71.5k
            root->importFromWkt(&pszWKT);
416
71.5k
            m_bNodesChanged = false;
417
71.5k
        }
418
71.5k
    }
419
98.4k
}
420
421
static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
422
518k
{
423
518k
    const char *pszName1 = nullptr;
424
518k
    const char *pszDirection1 = nullptr;
425
518k
    proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
426
518k
                          nullptr, nullptr, nullptr, nullptr);
427
518k
    const char *pszName2 = nullptr;
428
518k
    const char *pszDirection2 = nullptr;
429
518k
    proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
430
518k
                          nullptr, nullptr, nullptr, nullptr);
431
518k
    if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
432
518k
        EQUAL(pszDirection2, "east"))
433
96.3k
    {
434
96.3k
        return true;
435
96.3k
    }
436
421k
    if (pszDirection1 && pszDirection2 &&
437
421k
        ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
438
421k
         (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
439
421k
        pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
440
421k
        STARTS_WITH_CI(pszName2, "easting"))
441
22
    {
442
22
        return true;
443
22
    }
444
421k
    return false;
445
421k
}
446
447
void OGRSpatialReference::Private::refreshAxisMapping()
448
586k
{
449
586k
    if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
450
66.2k
        return;
451
452
519k
    bool doUndoDemote = false;
453
519k
    if (m_pj_crs_backup == nullptr)
454
519k
    {
455
519k
        doUndoDemote = true;
456
519k
        demoteFromBoundCRS();
457
519k
    }
458
519k
    const auto ctxt = getPROJContext();
459
519k
    PJ *horizCRS = nullptr;
460
519k
    int axisCount = 0;
461
519k
    if (m_pjType == PJ_TYPE_VERTICAL_CRS)
462
1.39k
    {
463
1.39k
        axisCount = 1;
464
1.39k
    }
465
518k
    else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
466
208k
    {
467
208k
        horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
468
208k
        if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
469
52
        {
470
52
            auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
471
52
            if (baseCRS)
472
52
            {
473
52
                proj_destroy(horizCRS);
474
52
                horizCRS = baseCRS;
475
52
            }
476
52
        }
477
478
208k
        auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
479
208k
        if (vertCRS)
480
208k
        {
481
208k
            if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
482
0
            {
483
0
                auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
484
0
                if (baseCRS)
485
0
                {
486
0
                    proj_destroy(vertCRS);
487
0
                    vertCRS = baseCRS;
488
0
                }
489
0
            }
490
491
208k
            auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
492
208k
            if (cs)
493
208k
            {
494
208k
                axisCount += proj_cs_get_axis_count(ctxt, cs);
495
208k
                proj_destroy(cs);
496
208k
            }
497
208k
            proj_destroy(vertCRS);
498
208k
        }
499
208k
    }
500
309k
    else
501
309k
    {
502
309k
        horizCRS = m_pj_crs;
503
309k
    }
504
505
519k
    bool bSwitchForGisFriendlyOrder = false;
506
519k
    if (horizCRS)
507
518k
    {
508
518k
        auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
509
518k
        if (cs)
510
518k
        {
511
518k
            int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
512
518k
            axisCount += nHorizCSAxisCount;
513
518k
            if (nHorizCSAxisCount >= 2)
514
518k
            {
515
518k
                bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
516
518k
            }
517
518k
            proj_destroy(cs);
518
518k
        }
519
518k
    }
520
519k
    if (horizCRS != m_pj_crs)
521
210k
    {
522
210k
        proj_destroy(horizCRS);
523
210k
    }
524
519k
    if (doUndoDemote)
525
519k
    {
526
519k
        undoDemoteFromBoundCRS();
527
519k
    }
528
529
519k
    m_axisMapping.resize(axisCount);
530
519k
    if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
531
519k
        !bSwitchForGisFriendlyOrder)
532
511k
    {
533
1.76M
        for (int i = 0; i < axisCount; i++)
534
1.25M
        {
535
1.25M
            m_axisMapping[i] = i + 1;
536
1.25M
        }
537
511k
    }
538
8.14k
    else
539
8.14k
    {
540
8.14k
        m_axisMapping[0] = 2;
541
8.14k
        m_axisMapping[1] = 1;
542
8.14k
        if (axisCount == 3)
543
2.08k
        {
544
2.08k
            m_axisMapping[2] = 3;
545
2.08k
        }
546
8.14k
    }
547
519k
}
548
549
void OGRSpatialReference::Private::nodesChanged()
550
6.46M
{
551
6.46M
    m_bNodesChanged = true;
552
6.46M
}
553
554
void OGRSpatialReference::Private::invalidateNodes()
555
517k
{
556
517k
    delete m_poRoot;
557
517k
    m_poRoot = nullptr;
558
517k
    m_bNodesChanged = false;
559
517k
}
560
561
void OGRSpatialReference::Private::setMorphToESRI(bool b)
562
0
{
563
0
    invalidateNodes();
564
0
    m_bMorphToESRI = b;
565
0
}
566
567
void OGRSpatialReference::Private::demoteFromBoundCRS()
568
1.03M
{
569
1.03M
    CPLAssert(m_pj_bound_crs_target == nullptr);
570
1.03M
    CPLAssert(m_pj_bound_crs_co == nullptr);
571
1.03M
    CPLAssert(m_poRootBackup == nullptr);
572
1.03M
    CPLAssert(m_pj_crs_backup == nullptr);
573
574
1.03M
    m_pj_crs_modified_during_demote = false;
575
576
1.03M
    if (m_pjType == PJ_TYPE_BOUND_CRS)
577
6.30k
    {
578
6.30k
        auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
579
6.30k
        m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
580
6.30k
        m_pj_bound_crs_co =
581
6.30k
            proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
582
583
6.30k
        m_poRootBackup = m_poRoot;
584
6.30k
        m_poRoot = nullptr;
585
6.30k
        m_pj_crs_backup = m_pj_crs;
586
6.30k
        m_pj_crs = baseCRS;
587
6.30k
        m_pjType = proj_get_type(m_pj_crs);
588
6.30k
    }
589
1.03M
}
590
591
void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
592
1.03M
{
593
1.03M
    if (m_pj_bound_crs_target)
594
6.30k
    {
595
6.30k
        CPLAssert(m_poRoot == nullptr);
596
6.30k
        CPLAssert(m_pj_crs);
597
6.30k
        if (!m_pj_crs_modified_during_demote)
598
5.66k
        {
599
5.66k
            proj_destroy(m_pj_crs);
600
5.66k
            m_pj_crs = m_pj_crs_backup;
601
5.66k
            m_pjType = proj_get_type(m_pj_crs);
602
5.66k
            m_poRoot = m_poRootBackup;
603
5.66k
        }
604
645
        else
605
645
        {
606
645
            delete m_poRootBackup;
607
645
            m_poRootBackup = nullptr;
608
645
            proj_destroy(m_pj_crs_backup);
609
645
            m_pj_crs_backup = nullptr;
610
645
            setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
611
645
                                               m_pj_bound_crs_target,
612
645
                                               m_pj_bound_crs_co),
613
645
                     false);
614
645
        }
615
6.30k
    }
616
617
1.03M
    m_poRootBackup = nullptr;
618
1.03M
    m_pj_crs_backup = nullptr;
619
1.03M
    proj_destroy(m_pj_bound_crs_target);
620
1.03M
    m_pj_bound_crs_target = nullptr;
621
1.03M
    proj_destroy(m_pj_bound_crs_co);
622
1.03M
    m_pj_bound_crs_co = nullptr;
623
1.03M
    m_pj_crs_modified_during_demote = false;
624
1.03M
}
625
626
const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
627
    const char *pszTargetKey)
628
167k
{
629
167k
    if (pszTargetKey)
630
76.9k
    {
631
76.9k
        demoteFromBoundCRS();
632
76.9k
        if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
633
76.9k
             m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
634
76.9k
            EQUAL(pszTargetKey, "GEOGCS"))
635
189
        {
636
189
            pszTargetKey = nullptr;
637
189
        }
638
76.7k
        else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
639
76.7k
                 EQUAL(pszTargetKey, "GEOCCS"))
640
0
        {
641
0
            pszTargetKey = nullptr;
642
0
        }
643
76.7k
        else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
644
76.7k
                 EQUAL(pszTargetKey, "PROJCS"))
645
0
        {
646
0
            pszTargetKey = nullptr;
647
0
        }
648
76.7k
        else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
649
76.7k
                 EQUAL(pszTargetKey, "VERT_CS"))
650
0
        {
651
0
            pszTargetKey = nullptr;
652
0
        }
653
76.9k
        undoDemoteFromBoundCRS();
654
76.9k
    }
655
167k
    return pszTargetKey;
656
167k
}
657
658
PJ *OGRSpatialReference::Private::getGeodBaseCRS()
659
36.7k
{
660
36.7k
    if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
661
36.7k
        m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
662
18
    {
663
18
        return m_pj_crs;
664
18
    }
665
666
36.7k
    auto ctxt = getPROJContext();
667
36.7k
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
668
16.5k
    {
669
16.5k
        proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
670
16.5k
        proj_destroy(m_pj_geod_base_crs_temp);
671
16.5k
        m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
672
16.5k
        return m_pj_geod_base_crs_temp;
673
16.5k
    }
674
675
20.1k
    proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
676
20.1k
    proj_destroy(m_pj_geod_base_crs_temp);
677
20.1k
    auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
678
20.1k
                                            nullptr, 0);
679
20.1k
    m_pj_geod_base_crs_temp = proj_create_geographic_crs(
680
20.1k
        ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
681
20.1k
        SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
682
20.1k
        SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
683
20.1k
    proj_destroy(cs);
684
685
20.1k
    return m_pj_geod_base_crs_temp;
686
36.7k
}
687
688
PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
689
16.5k
{
690
16.5k
    auto ctxt = getPROJContext();
691
16.5k
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
692
16.4k
    {
693
16.4k
        proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
694
16.4k
        proj_destroy(m_pj_proj_crs_cs_temp);
695
16.4k
        m_pj_proj_crs_cs_temp =
696
16.4k
            proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
697
16.4k
        return m_pj_proj_crs_cs_temp;
698
16.4k
    }
699
700
35
    proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
701
35
    proj_destroy(m_pj_proj_crs_cs_temp);
702
35
    m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
703
35
        ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
704
35
    return m_pj_proj_crs_cs_temp;
705
16.5k
}
706
707
const char *OGRSpatialReference::Private::getProjCRSName()
708
16.6k
{
709
16.6k
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
710
16.5k
    {
711
16.5k
        return proj_get_name(m_pj_crs);
712
16.5k
    }
713
714
47
    return "unnamed";
715
16.6k
}
716
717
OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
718
5.57k
{
719
5.57k
    refreshProjObj();
720
721
5.57k
    demoteFromBoundCRS();
722
723
5.57k
    auto projCRS =
724
5.57k
        proj_create_projected_crs(getPROJContext(), getProjCRSName(),
725
5.57k
                                  getGeodBaseCRS(), conv, getProjCRSCoordSys());
726
5.57k
    proj_destroy(conv);
727
728
5.57k
    setPjCRS(projCRS);
729
730
5.57k
    undoDemoteFromBoundCRS();
731
5.57k
    return OGRERR_NONE;
732
5.57k
}
733
734
/************************************************************************/
735
/*                           ToPointer()                                */
736
/************************************************************************/
737
738
static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
739
151k
{
740
151k
    return OGRSpatialReference::FromHandle(hSRS);
741
151k
}
742
743
/************************************************************************/
744
/*                           ToHandle()                                 */
745
/************************************************************************/
746
747
static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
748
0
{
749
0
    return OGRSpatialReference::ToHandle(poSRS);
750
0
}
751
752
/************************************************************************/
753
/*                           OGRsnPrintDouble()                         */
754
/************************************************************************/
755
756
void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
757
758
void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
759
760
0
{
761
0
    CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
762
763
0
    const size_t nLen = strlen(pszStrBuf);
764
765
    // The following hack is intended to truncate some "precision" in cases
766
    // that appear to be roundoff error.
767
0
    if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
768
0
                      strcmp(pszStrBuf + nLen - 6, "000001") == 0))
769
0
    {
770
0
        CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
771
0
    }
772
773
    // Force to user periods regardless of locale.
774
0
    if (strchr(pszStrBuf, ',') != nullptr)
775
0
    {
776
0
        char *const pszDelim = strchr(pszStrBuf, ',');
777
0
        *pszDelim = '.';
778
0
    }
779
0
}
780
781
/************************************************************************/
782
/*                        OGRSpatialReference()                         */
783
/************************************************************************/
784
785
/**
786
 * \brief Constructor.
787
 *
788
 * This constructor takes an optional string argument which if passed
789
 * should be a WKT representation of an SRS.  Passing this is equivalent
790
 * to not passing it, and then calling importFromWkt() with the WKT string.
791
 *
792
 * Note that newly created objects are given a reference count of one.
793
 *
794
 * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
795
 * object are assumed to be in the order of the axis of the CRS definition
796
 (which
797
 * for example means latitude first, longitude second for geographic CRS
798
 belonging
799
 * to the EPSG authority). It is possible to define a data axis to CRS axis
800
 * mapping strategy with the SetAxisMappingStrategy() method.
801
 *
802
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
803
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
804
 later
805
 * being the default value when the option is not set) to control the value of
806
 the
807
 * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
808
 * created. Calling SetAxisMappingStrategy() will override this default value.
809
810
 * The C function OSRNewSpatialReference() does the same thing as this
811
 * constructor.
812
 *
813
 * @param pszWKT well known text definition to which the object should
814
 * be initialized, or NULL (the default).
815
 */
816
817
OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
818
320k
    : d(new Private(this))
819
320k
{
820
320k
    if (pszWKT != nullptr)
821
0
        importFromWkt(pszWKT);
822
320k
}
823
824
/************************************************************************/
825
/*                       OSRNewSpatialReference()                       */
826
/************************************************************************/
827
828
/**
829
 * \brief Constructor.
830
 *
831
 * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
832
 * object are assumed to be in the order of the axis of the CRS definition
833
 * (which for example means latitude first, longitude second for geographic CRS
834
 * belonging to the EPSG authority). It is possible to define a data axis to CRS
835
 * axis mapping strategy with the SetAxisMappingStrategy() method.
836
 *
837
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
838
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
839
 * later being the default value when the option is not set) to control the
840
 * value of the data axis to CRS axis mapping strategy when a
841
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
842
 * override this default value.
843
 *
844
 * This function is the same as OGRSpatialReference::OGRSpatialReference()
845
 */
846
OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
847
848
0
{
849
0
    OGRSpatialReference *poSRS = new OGRSpatialReference();
850
851
0
    if (pszWKT != nullptr && strlen(pszWKT) > 0)
852
0
    {
853
0
        if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
854
0
        {
855
0
            delete poSRS;
856
0
            poSRS = nullptr;
857
0
        }
858
0
    }
859
860
0
    return ToHandle(poSRS);
861
0
}
862
863
/************************************************************************/
864
/*                        OGRSpatialReference()                         */
865
/************************************************************************/
866
867
/** Copy constructor. See also Clone().
868
 * @param oOther other spatial reference
869
 */
870
OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
871
8.95k
    : d(new Private(this))
872
8.95k
{
873
8.95k
    *this = oOther;
874
8.95k
}
875
876
/************************************************************************/
877
/*                        OGRSpatialReference()                         */
878
/************************************************************************/
879
880
/** Move constructor.
881
 * @param oOther other spatial reference
882
 */
883
OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
884
0
    : d(std::move(oOther.d))
885
0
{
886
0
}
887
888
/************************************************************************/
889
/*                        ~OGRSpatialReference()                        */
890
/************************************************************************/
891
892
/**
893
 * \brief OGRSpatialReference destructor.
894
 *
895
 * The C function OSRDestroySpatialReference() does the same thing as this
896
 * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
897
 *
898
 * @deprecated
899
 */
900
901
OGRSpatialReference::~OGRSpatialReference()
902
903
329k
{
904
329k
}
905
906
/************************************************************************/
907
/*                      DestroySpatialReference()                       */
908
/************************************************************************/
909
910
/**
911
 * \brief OGRSpatialReference destructor.
912
 *
913
 * This static method will destroy a OGRSpatialReference.  It is
914
 * equivalent to calling delete on the object, but it ensures that the
915
 * deallocation is properly executed within the OGR libraries heap on
916
 * platforms where this can matter (win32).
917
 *
918
 * This function is the same as OSRDestroySpatialReference()
919
 *
920
 * @param poSRS the object to delete
921
 *
922
 * @since GDAL 1.7.0
923
 */
924
925
void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
926
0
{
927
0
    delete poSRS;
928
0
}
929
930
/************************************************************************/
931
/*                     OSRDestroySpatialReference()                     */
932
/************************************************************************/
933
934
/**
935
 * \brief OGRSpatialReference destructor.
936
 *
937
 * This function is the same as OGRSpatialReference::~OGRSpatialReference()
938
 * and OGRSpatialReference::DestroySpatialReference()
939
 *
940
 * @param hSRS the object to delete
941
 */
942
void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
943
944
29.7k
{
945
29.7k
    delete ToPointer(hSRS);
946
29.7k
}
947
948
/************************************************************************/
949
/*                               Clear()                                */
950
/************************************************************************/
951
952
/**
953
 * \brief Wipe current definition.
954
 *
955
 * Returns OGRSpatialReference to a state with no definition, as it
956
 * exists when first created.  It does not affect reference counts.
957
 */
958
959
void OGRSpatialReference::Clear()
960
961
172k
{
962
172k
    d->clear();
963
172k
}
964
965
/************************************************************************/
966
/*                             operator=()                              */
967
/************************************************************************/
968
969
/** Assignment operator.
970
 * @param oSource SRS to assign to *this
971
 * @return *this
972
 */
973
OGRSpatialReference &
974
OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
975
976
38.8k
{
977
38.8k
    if (&oSource != this)
978
38.8k
    {
979
38.8k
        Clear();
980
#ifdef CPPCHECK
981
        // Otherwise cppcheck would protest that nRefCount isn't modified
982
        d->nRefCount = (d->nRefCount + 1) - 1;
983
#endif
984
985
38.8k
        oSource.d->refreshProjObj();
986
38.8k
        if (oSource.d->m_pj_crs)
987
38.1k
            d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
988
38.8k
        if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
989
0
            SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
990
38.8k
        else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
991
0
            SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
992
993
38.8k
        d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
994
38.8k
    }
995
996
38.8k
    return *this;
997
38.8k
}
998
999
/************************************************************************/
1000
/*                             operator=()                              */
1001
/************************************************************************/
1002
1003
/** Move assignment operator.
1004
 * @param oSource SRS to assign to *this
1005
 * @return *this
1006
 */
1007
OGRSpatialReference &
1008
OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1009
1010
6.23k
{
1011
6.23k
    if (&oSource != this)
1012
6.23k
    {
1013
6.23k
        d = std::move(oSource.d);
1014
6.23k
    }
1015
1016
6.23k
    return *this;
1017
6.23k
}
1018
1019
/************************************************************************/
1020
/*                      AssignAndSetThreadSafe()                        */
1021
/************************************************************************/
1022
1023
/** Assignment method, with thread-safety.
1024
 *
1025
 * Same as an assignment operator, but asking also that the *this instance
1026
 * becomes thread-safe.
1027
 *
1028
 * @param oSource SRS to assign to *this
1029
 * @return *this
1030
 * @since 3.10
1031
 */
1032
1033
OGRSpatialReference &
1034
OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1035
0
{
1036
0
    *this = oSource;
1037
0
    d->SetThreadSafe();
1038
0
    return *this;
1039
0
}
1040
1041
/************************************************************************/
1042
/*                             Reference()                              */
1043
/************************************************************************/
1044
1045
/**
1046
 * \brief Increments the reference count by one.
1047
 *
1048
 * The reference count is used keep track of the number of OGRGeometry objects
1049
 * referencing this SRS.
1050
 *
1051
 * The method does the same thing as the C function OSRReference().
1052
 *
1053
 * @return the updated reference count.
1054
 */
1055
1056
int OGRSpatialReference::Reference()
1057
1058
0
{
1059
0
    return CPLAtomicInc(&d->nRefCount);
1060
0
}
1061
1062
/************************************************************************/
1063
/*                            OSRReference()                            */
1064
/************************************************************************/
1065
1066
/**
1067
 * \brief Increments the reference count by one.
1068
 *
1069
 * This function is the same as OGRSpatialReference::Reference()
1070
 */
1071
int OSRReference(OGRSpatialReferenceH hSRS)
1072
1073
0
{
1074
0
    VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1075
1076
0
    return ToPointer(hSRS)->Reference();
1077
0
}
1078
1079
/************************************************************************/
1080
/*                            Dereference()                             */
1081
/************************************************************************/
1082
1083
/**
1084
 * \brief Decrements the reference count by one.
1085
 *
1086
 * The method does the same thing as the C function OSRDereference().
1087
 *
1088
 * @return the updated reference count.
1089
 */
1090
1091
int OGRSpatialReference::Dereference()
1092
1093
151k
{
1094
151k
    if (d->nRefCount <= 0)
1095
0
        CPLDebug("OSR",
1096
0
                 "Dereference() called on an object with refcount %d,"
1097
0
                 "likely already destroyed!",
1098
0
                 d->nRefCount);
1099
151k
    return CPLAtomicDec(&d->nRefCount);
1100
151k
}
1101
1102
/************************************************************************/
1103
/*                           OSRDereference()                           */
1104
/************************************************************************/
1105
1106
/**
1107
 * \brief Decrements the reference count by one.
1108
 *
1109
 * This function is the same as OGRSpatialReference::Dereference()
1110
 */
1111
int OSRDereference(OGRSpatialReferenceH hSRS)
1112
1113
0
{
1114
0
    VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1115
1116
0
    return ToPointer(hSRS)->Dereference();
1117
0
}
1118
1119
/************************************************************************/
1120
/*                         GetReferenceCount()                          */
1121
/************************************************************************/
1122
1123
/**
1124
 * \brief Fetch current reference count.
1125
 *
1126
 * @return the current reference count.
1127
 */
1128
int OGRSpatialReference::GetReferenceCount() const
1129
0
{
1130
0
    return d->nRefCount;
1131
0
}
1132
1133
/************************************************************************/
1134
/*                              Release()                               */
1135
/************************************************************************/
1136
1137
/**
1138
 * \brief Decrements the reference count by one, and destroy if zero.
1139
 *
1140
 * The method does the same thing as the C function OSRRelease().
1141
 */
1142
1143
void OGRSpatialReference::Release()
1144
1145
151k
{
1146
151k
    if (Dereference() <= 0)
1147
151k
        delete this;
1148
151k
}
1149
1150
/************************************************************************/
1151
/*                             OSRRelease()                             */
1152
/************************************************************************/
1153
1154
/**
1155
 * \brief Decrements the reference count by one, and destroy if zero.
1156
 *
1157
 * This function is the same as OGRSpatialReference::Release()
1158
 */
1159
void OSRRelease(OGRSpatialReferenceH hSRS)
1160
1161
122k
{
1162
122k
    VALIDATE_POINTER0(hSRS, "OSRRelease");
1163
1164
122k
    ToPointer(hSRS)->Release();
1165
122k
}
1166
1167
OGR_SRSNode *OGRSpatialReference::GetRoot()
1168
422k
{
1169
422k
    TAKE_OPTIONAL_LOCK();
1170
1171
422k
    if (!d->m_poRoot)
1172
98.4k
    {
1173
98.4k
        d->refreshRootFromProjObj();
1174
98.4k
    }
1175
422k
    return d->m_poRoot;
1176
422k
}
1177
1178
const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1179
0
{
1180
0
    TAKE_OPTIONAL_LOCK();
1181
1182
0
    if (!d->m_poRoot)
1183
0
    {
1184
0
        d->refreshRootFromProjObj();
1185
0
    }
1186
0
    return d->m_poRoot;
1187
0
}
1188
1189
/************************************************************************/
1190
/*                              SetRoot()                               */
1191
/************************************************************************/
1192
1193
/**
1194
 * \brief Set the root SRS node.
1195
 *
1196
 * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1197
 * as part of assigning the new root.  Ownership of the passed OGR_SRSNode is
1198
 * is assumed by the OGRSpatialReference.
1199
 *
1200
 * @param poNewRoot object to assign as root.
1201
 */
1202
1203
void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1204
1205
14.0k
{
1206
14.0k
    if (d->m_poRoot != poNewRoot)
1207
14.0k
    {
1208
14.0k
        delete d->m_poRoot;
1209
14.0k
        d->setRoot(poNewRoot);
1210
14.0k
    }
1211
14.0k
}
1212
1213
/************************************************************************/
1214
/*                            GetAttrNode()                             */
1215
/************************************************************************/
1216
1217
/**
1218
 * \brief Find named node in tree.
1219
 *
1220
 * This method does a pre-order traversal of the node tree searching for
1221
 * a node with this exact value (case insensitive), and returns it.  Leaf
1222
 * nodes are not considered, under the assumption that they are just
1223
 * attribute value nodes.
1224
 *
1225
 * If a node appears more than once in the tree (such as UNIT for instance),
1226
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1227
 * more specific.
1228
 *
1229
 * @param pszNodePath the name of the node to search for.  May contain multiple
1230
 * components such as "GEOGCS|UNIT".
1231
 *
1232
 * @return a pointer to the node found, or NULL if none.
1233
 */
1234
1235
OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1236
1237
129k
{
1238
129k
    if (strchr(pszNodePath, '|') == nullptr)
1239
50.6k
    {
1240
        // Fast path
1241
50.6k
        OGR_SRSNode *poNode = GetRoot();
1242
50.6k
        if (poNode)
1243
48.1k
            poNode = poNode->GetNode(pszNodePath);
1244
50.6k
        return poNode;
1245
50.6k
    }
1246
1247
78.4k
    char **papszPathTokens =
1248
78.4k
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1249
1250
78.4k
    if (CSLCount(papszPathTokens) < 1)
1251
0
    {
1252
0
        CSLDestroy(papszPathTokens);
1253
0
        return nullptr;
1254
0
    }
1255
1256
78.4k
    OGR_SRSNode *poNode = GetRoot();
1257
299k
    for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1258
220k
    {
1259
220k
        poNode = poNode->GetNode(papszPathTokens[i]);
1260
220k
    }
1261
1262
78.4k
    CSLDestroy(papszPathTokens);
1263
1264
78.4k
    return poNode;
1265
78.4k
}
1266
1267
/**
1268
 * \brief Find named node in tree.
1269
 *
1270
 * This method does a pre-order traversal of the node tree searching for
1271
 * a node with this exact value (case insensitive), and returns it.  Leaf
1272
 * nodes are not considered, under the assumption that they are just
1273
 * attribute value nodes.
1274
 *
1275
 * If a node appears more than once in the tree (such as UNIT for instance),
1276
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1277
 * more specific.
1278
 *
1279
 * @param pszNodePath the name of the node to search for.  May contain multiple
1280
 * components such as "GEOGCS|UNIT".
1281
 *
1282
 * @return a pointer to the node found, or NULL if none.
1283
 */
1284
1285
const OGR_SRSNode *
1286
OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1287
1288
37.5k
{
1289
37.5k
    OGR_SRSNode *poNode =
1290
37.5k
        const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1291
1292
37.5k
    return poNode;
1293
37.5k
}
1294
1295
/************************************************************************/
1296
/*                            GetAttrValue()                            */
1297
/************************************************************************/
1298
1299
/**
1300
 * \brief Fetch indicated attribute of named node.
1301
 *
1302
 * This method uses GetAttrNode() to find the named node, and then extracts
1303
 * the value of the indicated child.  Thus a call to GetAttrValue("UNIT",1)
1304
 * would return the second child of the UNIT node, which is normally the
1305
 * length of the linear unit in meters.
1306
 *
1307
 * This method does the same thing as the C function OSRGetAttrValue().
1308
 *
1309
 * @param pszNodeName the tree node to look for (case insensitive).
1310
 * @param iAttr the child of the node to fetch (zero based).
1311
 *
1312
 * @return the requested value, or NULL if it fails for any reason.
1313
 */
1314
1315
const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1316
                                              int iAttr) const
1317
1318
23.0k
{
1319
23.0k
    const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1320
23.0k
    if (poNode == nullptr)
1321
2.53k
    {
1322
2.53k
        if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1323
0
        {
1324
0
            return GetAttrValue("METHOD", iAttr);
1325
0
        }
1326
2.53k
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1327
0
        {
1328
0
            return GetAttrValue("PROJCRS|METHOD", iAttr);
1329
0
        }
1330
2.53k
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1331
159
        {
1332
159
            return GetAttrValue("PROJCRS", iAttr);
1333
159
        }
1334
2.37k
        return nullptr;
1335
2.53k
    }
1336
1337
20.4k
    if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1338
0
        return nullptr;
1339
1340
20.4k
    return poNode->GetChild(iAttr)->GetValue();
1341
20.4k
}
1342
1343
/************************************************************************/
1344
/*                          OSRGetAttrValue()                           */
1345
/************************************************************************/
1346
1347
/**
1348
 * \brief Fetch indicated attribute of named node.
1349
 *
1350
 * This function is the same as OGRSpatialReference::GetAttrValue()
1351
 */
1352
const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1353
                                        const char *pszKey, int iChild)
1354
1355
0
{
1356
0
    VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1357
1358
0
    return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1359
0
}
1360
1361
/************************************************************************/
1362
/*                             GetName()                                */
1363
/************************************************************************/
1364
1365
/**
1366
 * \brief Return the CRS name.
1367
 *
1368
 * The returned value is only short lived and should not be used after other
1369
 * calls to methods on this object.
1370
 *
1371
 * @since GDAL 3.0
1372
 */
1373
1374
const char *OGRSpatialReference::GetName() const
1375
22.8k
{
1376
22.8k
    TAKE_OPTIONAL_LOCK();
1377
1378
22.8k
    d->refreshProjObj();
1379
22.8k
    if (!d->m_pj_crs)
1380
10
        return nullptr;
1381
22.8k
    const char *pszName = proj_get_name(d->m_pj_crs);
1382
#if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1383
    if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1384
    {
1385
        // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1386
        PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1387
        if (baseCRS)
1388
        {
1389
            pszName = proj_get_name(baseCRS);
1390
            // pszName still remains valid after proj_destroy(), since
1391
            // d->m_pj_crs keeps a reference to the base CRS C++ object.
1392
            proj_destroy(baseCRS);
1393
        }
1394
    }
1395
#endif
1396
22.8k
    return pszName;
1397
22.8k
}
1398
1399
/************************************************************************/
1400
/*                           OSRGetName()                               */
1401
/************************************************************************/
1402
1403
/**
1404
 * \brief Return the CRS name.
1405
 *
1406
 * The returned value is only short lived and should not be used after other
1407
 * calls to methods on this object.
1408
 *
1409
 * @since GDAL 3.0
1410
 */
1411
const char *OSRGetName(OGRSpatialReferenceH hSRS)
1412
1413
0
{
1414
0
    VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1415
1416
0
    return ToPointer(hSRS)->GetName();
1417
0
}
1418
1419
/************************************************************************/
1420
/*                               Clone()                                */
1421
/************************************************************************/
1422
1423
/**
1424
 * \brief Make a duplicate of this OGRSpatialReference.
1425
 *
1426
 * This method is the same as the C function OSRClone().
1427
 *
1428
 * @return a new SRS, which becomes the responsibility of the caller.
1429
 */
1430
1431
OGRSpatialReference *OGRSpatialReference::Clone() const
1432
1433
58.9k
{
1434
58.9k
    OGRSpatialReference *poNewRef = new OGRSpatialReference();
1435
1436
58.9k
    TAKE_OPTIONAL_LOCK();
1437
1438
58.9k
    d->refreshProjObj();
1439
58.9k
    if (d->m_pj_crs != nullptr)
1440
58.3k
        poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1441
58.9k
    if (d->m_bHasCenterLong && d->m_poRoot)
1442
1.38k
    {
1443
1.38k
        poNewRef->d->setRoot(d->m_poRoot->Clone());
1444
1.38k
    }
1445
58.9k
    poNewRef->d->m_axisMapping = d->m_axisMapping;
1446
58.9k
    poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1447
58.9k
    poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1448
58.9k
    return poNewRef;
1449
58.9k
}
1450
1451
/************************************************************************/
1452
/*                              OSRClone()                              */
1453
/************************************************************************/
1454
1455
/**
1456
 * \brief Make a duplicate of this OGRSpatialReference.
1457
 *
1458
 * This function is the same as OGRSpatialReference::Clone()
1459
 */
1460
OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1461
1462
0
{
1463
0
    VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1464
1465
0
    return ToHandle(ToPointer(hSRS)->Clone());
1466
0
}
1467
1468
/************************************************************************/
1469
/*                            dumpReadable()                            */
1470
/************************************************************************/
1471
1472
/** Dump pretty wkt to stdout, mostly for debugging.
1473
 */
1474
void OGRSpatialReference::dumpReadable()
1475
1476
0
{
1477
0
    char *pszPrettyWkt = nullptr;
1478
1479
0
    const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1480
0
    exportToWkt(&pszPrettyWkt, apszOptions);
1481
0
    printf("%s\n", pszPrettyWkt); /*ok*/
1482
0
    CPLFree(pszPrettyWkt);
1483
0
}
1484
1485
/************************************************************************/
1486
/*                         exportToPrettyWkt()                          */
1487
/************************************************************************/
1488
1489
/**
1490
 * Convert this SRS into a nicely formatted WKT 1 string for display to a
1491
 * person.
1492
 *
1493
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1494
 * Issues</a> page for implementation details of WKT 1 in OGR.
1495
 *
1496
 * Note that the returned WKT string should be freed with
1497
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1498
 *
1499
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1500
 * option. Valid values are the one of the FORMAT option of
1501
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1502
 *
1503
 * This method is the same as the C function OSRExportToPrettyWkt().
1504
 *
1505
 * @param ppszResult the resulting string is returned in this pointer.
1506
 * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1507
 *   stripped off.
1508
 *
1509
 * @return OGRERR_NONE if successful.
1510
 */
1511
1512
OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1513
                                              int bSimplify) const
1514
1515
0
{
1516
0
    CPLStringList aosOptions;
1517
0
    aosOptions.SetNameValue("MULTILINE", "YES");
1518
0
    if (bSimplify)
1519
0
    {
1520
0
        aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1521
0
    }
1522
0
    return exportToWkt(ppszResult, aosOptions.List());
1523
0
}
1524
1525
/************************************************************************/
1526
/*                        OSRExportToPrettyWkt()                        */
1527
/************************************************************************/
1528
1529
/**
1530
 * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1531
 * person.
1532
 *
1533
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1534
 * option. Valid values are the one of the FORMAT option of
1535
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1536
 *
1537
 * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1538
 */
1539
1540
OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1541
                                        char **ppszReturn, int bSimplify)
1542
1543
0
{
1544
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1545
1546
0
    *ppszReturn = nullptr;
1547
1548
0
    return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1549
0
}
1550
1551
/************************************************************************/
1552
/*                            exportToWkt()                             */
1553
/************************************************************************/
1554
1555
/**
1556
 * \brief Convert this SRS into WKT 1 format.
1557
 *
1558
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1559
 * Issues</a> page for implementation details of WKT 1 in OGR.
1560
 *
1561
 * Note that the returned WKT string should be freed with
1562
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1563
 *
1564
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1565
 * option. Valid values are the one of the FORMAT option of
1566
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1567
 *
1568
 * This method is the same as the C function OSRExportToWkt().
1569
 *
1570
 * @param ppszResult the resulting string is returned in this pointer.
1571
 *
1572
 * @return OGRERR_NONE if successful.
1573
 */
1574
1575
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1576
1577
29.1k
{
1578
29.1k
    return exportToWkt(ppszResult, nullptr);
1579
29.1k
}
1580
1581
/************************************************************************/
1582
/*                GDAL_proj_crs_create_bound_crs_to_WGS84()             */
1583
/************************************************************************/
1584
1585
static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1586
                                                   bool onlyIfEPSGCode,
1587
                                                   bool canModifyHorizPart)
1588
0
{
1589
0
    PJ *ret = nullptr;
1590
0
    if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1591
0
    {
1592
0
        auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1593
0
        auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1594
0
        if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1595
0
            vertCRS &&
1596
0
            (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1597
0
        {
1598
0
            auto boundHoriz =
1599
0
                canModifyHorizPart
1600
0
                    ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1601
0
                    : proj_clone(ctx, horizCRS);
1602
0
            auto boundVert =
1603
0
                proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1604
0
            if (boundHoriz && boundVert)
1605
0
            {
1606
0
                ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1607
0
                                               boundHoriz, boundVert);
1608
0
            }
1609
0
            proj_destroy(boundHoriz);
1610
0
            proj_destroy(boundVert);
1611
0
        }
1612
0
        proj_destroy(horizCRS);
1613
0
        proj_destroy(vertCRS);
1614
0
    }
1615
0
    else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1616
0
             (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1617
0
    {
1618
0
        ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1619
0
    }
1620
0
    return ret;
1621
0
}
1622
1623
/************************************************************************/
1624
/*                            exportToWkt()                             */
1625
/************************************************************************/
1626
1627
/**
1628
 * Convert this SRS into a WKT string.
1629
 *
1630
 * Note that the returned WKT string should be freed with
1631
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1632
 *
1633
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1634
 * Issues</a> page for implementation details of WKT 1 in OGR.
1635
 *
1636
 * @param ppszResult the resulting string is returned in this pointer.
1637
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1638
 * supported options are
1639
 * <ul>
1640
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1641
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1642
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1643
 *     node is returned.
1644
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1645
 *     node is returned.
1646
 *     WKT1 is an alias of WKT1_GDAL.
1647
 *     WKT2 will default to the latest revision implemented (currently
1648
 *     WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1649
 * </li>
1650
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1651
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1652
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1653
 * height (for example for use with LAS 1.4 WKT1).
1654
 * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1655
 * </ul>
1656
 *
1657
 * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1658
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1659
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1660
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1661
 * TOWGS84[] node may be added.
1662
 *
1663
 * @return OGRERR_NONE if successful.
1664
 * @since GDAL 3.0
1665
 */
1666
1667
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1668
                                        const char *const *papszOptions) const
1669
48.9k
{
1670
    // In the past calling this method was thread-safe, even if we never
1671
    // guaranteed it. Now proj_as_wkt() will cache the result internally,
1672
    // so this is no longer thread-safe.
1673
48.9k
    std::lock_guard oLock(d->m_mutex);
1674
1675
48.9k
    d->refreshProjObj();
1676
48.9k
    if (!d->m_pj_crs)
1677
91
    {
1678
91
        *ppszResult = CPLStrdup("");
1679
91
        return OGRERR_FAILURE;
1680
91
    }
1681
1682
48.8k
    if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1683
1.03k
    {
1684
1.03k
        return d->m_poRoot->exportToWkt(ppszResult);
1685
1.03k
    }
1686
1687
47.7k
    auto ctxt = d->getPROJContext();
1688
47.7k
    auto wktFormat = PJ_WKT1_GDAL;
1689
47.7k
    const char *pszFormat =
1690
47.7k
        CSLFetchNameValueDef(papszOptions, "FORMAT",
1691
47.7k
                             CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1692
47.7k
    if (EQUAL(pszFormat, "DEFAULT"))
1693
29.1k
        pszFormat = "";
1694
1695
47.7k
    if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1696
0
    {
1697
0
        wktFormat = PJ_WKT1_ESRI;
1698
0
    }
1699
47.7k
    else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1700
47.7k
             EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1701
18.6k
    {
1702
18.6k
        wktFormat = PJ_WKT1_GDAL;
1703
18.6k
    }
1704
29.1k
    else if (EQUAL(pszFormat, "WKT2_2015"))
1705
0
    {
1706
0
        wktFormat = PJ_WKT2_2015;
1707
0
    }
1708
29.1k
    else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1709
29.1k
             EQUAL(pszFormat, "WKT2_2019"))
1710
0
    {
1711
0
        wktFormat = PJ_WKT2_2018;
1712
0
    }
1713
29.1k
    else if (pszFormat[0] == '\0')
1714
29.1k
    {
1715
        // cppcheck-suppress knownConditionTrueFalse
1716
29.1k
        if (IsDerivedGeographic())
1717
0
        {
1718
0
            wktFormat = PJ_WKT2_2018;
1719
0
        }
1720
29.1k
        else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1721
29.1k
                 GetAxesCount() == 3)
1722
76
        {
1723
76
            wktFormat = PJ_WKT2_2018;
1724
76
        }
1725
29.1k
    }
1726
0
    else
1727
0
    {
1728
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1729
0
        *ppszResult = CPLStrdup("");
1730
0
        return OGRERR_FAILURE;
1731
0
    }
1732
1733
47.7k
    CPLStringList aosOptions;
1734
47.7k
    if (wktFormat != PJ_WKT1_ESRI)
1735
47.7k
    {
1736
47.7k
        aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1737
47.7k
    }
1738
47.7k
    aosOptions.SetNameValue(
1739
47.7k
        "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1740
1741
47.7k
    const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1742
47.7k
        papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1743
47.7k
    if (pszAllowEllpsHeightAsVertCS)
1744
0
    {
1745
0
        aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1746
0
                                pszAllowEllpsHeightAsVertCS);
1747
0
    }
1748
1749
47.7k
    PJ *boundCRS = nullptr;
1750
47.7k
    if (wktFormat == PJ_WKT1_GDAL &&
1751
47.7k
        CPLTestBool(CSLFetchNameValueDef(
1752
47.7k
            papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1753
47.7k
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1754
0
    {
1755
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1756
0
            d->getPROJContext(), d->m_pj_crs, true, true);
1757
0
    }
1758
1759
47.7k
    CPLErrorAccumulator oErrorAccumulator;
1760
47.7k
    const char *pszWKT;
1761
47.7k
    {
1762
47.7k
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1763
47.7k
        CPL_IGNORE_RET_VAL(oAccumulator);
1764
47.7k
        pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1765
47.7k
                             aosOptions.List());
1766
47.7k
    }
1767
47.7k
    for (const auto &oError : oErrorAccumulator.GetErrors())
1768
218
    {
1769
218
        if (pszFormat[0] == '\0' &&
1770
218
            (oError.msg.find("Unsupported conversion method") !=
1771
158
                 std::string::npos ||
1772
158
             oError.msg.find("can only be exported to WKT2") !=
1773
0
                 std::string::npos ||
1774
158
             oError.msg.find("can only be exported since WKT2:2019") !=
1775
0
                 std::string::npos))
1776
158
        {
1777
158
            CPLErrorReset();
1778
            // If we cannot export in the default mode (WKT1), retry with WKT2
1779
158
            pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1780
158
                                 PJ_WKT2_2018, aosOptions.List());
1781
158
            break;
1782
158
        }
1783
60
        CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1784
60
    }
1785
1786
47.7k
    if (!pszWKT)
1787
60
    {
1788
60
        *ppszResult = CPLStrdup("");
1789
60
        proj_destroy(boundCRS);
1790
60
        return OGRERR_FAILURE;
1791
60
    }
1792
1793
47.7k
    if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1794
0
    {
1795
0
        OGR_SRSNode oRoot;
1796
0
        oRoot.importFromWkt(&pszWKT);
1797
0
        oRoot.StripNodes("AXIS");
1798
0
        if (EQUAL(pszFormat, "SFSQL"))
1799
0
        {
1800
0
            oRoot.StripNodes("TOWGS84");
1801
0
        }
1802
0
        oRoot.StripNodes("AUTHORITY");
1803
0
        oRoot.StripNodes("EXTENSION");
1804
0
        OGRErr eErr;
1805
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1806
0
            eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1807
0
        else
1808
0
            eErr = oRoot.exportToWkt(ppszResult);
1809
0
        proj_destroy(boundCRS);
1810
0
        return eErr;
1811
0
    }
1812
1813
47.7k
    *ppszResult = CPLStrdup(pszWKT);
1814
1815
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1816
    if (wktFormat == PJ_WKT2_2018)
1817
    {
1818
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1819
        // related to a wrong EPSG code assigned to UTM South conversions
1820
        char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1821
        if (pszPtr)
1822
        {
1823
            pszPtr += strlen("CONVERSION[\"UTM zone ");
1824
            const int nZone = atoi(pszPtr);
1825
            while (*pszPtr >= '0' && *pszPtr <= '9')
1826
                ++pszPtr;
1827
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1828
                pszPtr[1] == '"' && pszPtr[2] == ',')
1829
            {
1830
                pszPtr += 3;
1831
                int nLevel = 0;
1832
                bool bInString = false;
1833
                // Find the ID node corresponding to this CONVERSION node
1834
                while (*pszPtr)
1835
                {
1836
                    if (bInString)
1837
                    {
1838
                        if (*pszPtr == '"' && pszPtr[1] == '"')
1839
                        {
1840
                            ++pszPtr;
1841
                        }
1842
                        else if (*pszPtr == '"')
1843
                        {
1844
                            bInString = false;
1845
                        }
1846
                    }
1847
                    else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1848
                    {
1849
                        if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1850
                                                              17000 + nZone)))
1851
                        {
1852
                            CPLAssert(pszPtr[11] == '7');
1853
                            CPLAssert(pszPtr[12] == '0');
1854
                            pszPtr[11] = '6';
1855
                            pszPtr[12] = '1';
1856
                        }
1857
                        break;
1858
                    }
1859
                    else if (*pszPtr == '"')
1860
                    {
1861
                        bInString = true;
1862
                    }
1863
                    else if (*pszPtr == '[')
1864
                    {
1865
                        ++nLevel;
1866
                    }
1867
                    else if (*pszPtr == ']')
1868
                    {
1869
                        --nLevel;
1870
                    }
1871
1872
                    ++pszPtr;
1873
                }
1874
            }
1875
        }
1876
    }
1877
#endif
1878
1879
47.7k
    proj_destroy(boundCRS);
1880
47.7k
    return OGRERR_NONE;
1881
47.7k
}
1882
1883
/************************************************************************/
1884
/*                            exportToWkt()                             */
1885
/************************************************************************/
1886
1887
/**
1888
 * Convert this SRS into a WKT string.
1889
 *
1890
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1891
 * Issues</a> page for implementation details of WKT 1 in OGR.
1892
 *
1893
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1894
 * supported options are
1895
 * <ul>
1896
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1897
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1898
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1899
 *     node is returned.
1900
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1901
 *     node is returned.
1902
 *     WKT1 is an alias of WKT1_GDAL.
1903
 *     WKT2 will default to the latest revision implemented (currently
1904
 *     WKT2_2019)
1905
 * </li>
1906
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1907
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1908
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1909
 * height (for example for use with LAS 1.4 WKT1).
1910
 * Requires PROJ 7.2.1.</li>
1911
 * </ul>
1912
 *
1913
 * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1914
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1915
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1916
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1917
 * TOWGS84[] node may be added.
1918
 *
1919
 * @return a non-empty string if successful.
1920
 * @since GDAL 3.9
1921
 */
1922
1923
std::string
1924
OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
1925
0
{
1926
0
    std::string osWKT;
1927
0
    char *pszWKT = nullptr;
1928
0
    if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
1929
0
        osWKT = pszWKT;
1930
0
    CPLFree(pszWKT);
1931
0
    return osWKT;
1932
0
}
1933
1934
/************************************************************************/
1935
/*                           OSRExportToWkt()                           */
1936
/************************************************************************/
1937
1938
/**
1939
 * \brief Convert this SRS into WKT 1 format.
1940
 *
1941
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1942
 * Issues</a> page for implementation details of WKT in OGR.
1943
 *
1944
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1945
 * option. Valid values are the one of the FORMAT option of
1946
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1947
 *
1948
 * This function is the same as OGRSpatialReference::exportToWkt().
1949
 */
1950
1951
OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
1952
1953
0
{
1954
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
1955
1956
0
    *ppszReturn = nullptr;
1957
1958
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn);
1959
0
}
1960
1961
/************************************************************************/
1962
/*                          OSRExportToWktEx()                          */
1963
/************************************************************************/
1964
1965
/**
1966
 * \brief Convert this SRS into WKT format.
1967
 *
1968
 * This function is the same as OGRSpatialReference::exportToWkt(char **
1969
 * ppszResult,const char* const* papszOptions ) const
1970
 *
1971
 * @since GDAL 3.0
1972
 */
1973
1974
OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
1975
                        const char *const *papszOptions)
1976
0
{
1977
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
1978
1979
0
    *ppszReturn = nullptr;
1980
1981
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
1982
0
}
1983
1984
/************************************************************************/
1985
/*                       exportToPROJJSON()                             */
1986
/************************************************************************/
1987
1988
/**
1989
 * Convert this SRS into a PROJJSON string.
1990
 *
1991
 * Note that the returned JSON string should be freed with
1992
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1993
 *
1994
 * @param ppszResult the resulting string is returned in this pointer.
1995
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1996
 * supported options are
1997
 * <ul>
1998
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
1999
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2000
 * on).</li>
2001
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2002
 * disable it.</li>
2003
 * </ul>
2004
 *
2005
 * @return OGRERR_NONE if successful.
2006
 * @since GDAL 3.1 and PROJ 6.2
2007
 */
2008
2009
OGRErr OGRSpatialReference::exportToPROJJSON(
2010
    char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2011
0
{
2012
0
    TAKE_OPTIONAL_LOCK();
2013
2014
0
    d->refreshProjObj();
2015
0
    if (!d->m_pj_crs)
2016
0
    {
2017
0
        *ppszResult = nullptr;
2018
0
        return OGRERR_FAILURE;
2019
0
    }
2020
2021
0
    const char *pszPROJJSON =
2022
0
        proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2023
2024
0
    if (!pszPROJJSON)
2025
0
    {
2026
0
        *ppszResult = CPLStrdup("");
2027
0
        return OGRERR_FAILURE;
2028
0
    }
2029
2030
0
    *ppszResult = CPLStrdup(pszPROJJSON);
2031
2032
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2033
    {
2034
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2035
        // related to a wrong EPSG code assigned to UTM South conversions
2036
        char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2037
        if (pszPtr)
2038
        {
2039
            pszPtr += strlen("\"name\": \"UTM zone ");
2040
            const int nZone = atoi(pszPtr);
2041
            while (*pszPtr >= '0' && *pszPtr <= '9')
2042
                ++pszPtr;
2043
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2044
            {
2045
                pszPtr += 2;
2046
                int nLevel = 0;
2047
                bool bInString = false;
2048
                // Find the id node corresponding to this conversion node
2049
                while (*pszPtr)
2050
                {
2051
                    if (bInString)
2052
                    {
2053
                        if (*pszPtr == '\\')
2054
                        {
2055
                            ++pszPtr;
2056
                        }
2057
                        else if (*pszPtr == '"')
2058
                        {
2059
                            bInString = false;
2060
                        }
2061
                    }
2062
                    else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2063
                    {
2064
                        const char *pszNextEndCurl = strchr(pszPtr, '}');
2065
                        const char *pszAuthEPSG =
2066
                            strstr(pszPtr, "\"authority\": \"EPSG\"");
2067
                        char *pszCode = strstr(
2068
                            pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2069
                        if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2070
                            pszNextEndCurl - pszAuthEPSG > 0 &&
2071
                            pszNextEndCurl - pszCode > 0)
2072
                        {
2073
                            CPLAssert(pszCode[9] == '7');
2074
                            CPLAssert(pszCode[10] == '0');
2075
                            pszCode[9] = '6';
2076
                            pszCode[10] = '1';
2077
                        }
2078
                        break;
2079
                    }
2080
                    else if (*pszPtr == '"')
2081
                    {
2082
                        bInString = true;
2083
                    }
2084
                    else if (*pszPtr == '{' || *pszPtr == '[')
2085
                    {
2086
                        ++nLevel;
2087
                    }
2088
                    else if (*pszPtr == '}' || *pszPtr == ']')
2089
                    {
2090
                        --nLevel;
2091
                    }
2092
2093
                    ++pszPtr;
2094
                }
2095
            }
2096
        }
2097
    }
2098
#endif
2099
2100
0
    return OGRERR_NONE;
2101
0
}
2102
2103
/************************************************************************/
2104
/*                          OSRExportToPROJJSON()                       */
2105
/************************************************************************/
2106
2107
/**
2108
 * \brief Convert this SRS into PROJJSON format.
2109
 *
2110
 * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2111
 *
2112
 * @since GDAL 3.1 and PROJ 6.2
2113
 */
2114
2115
OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2116
                           const char *const *papszOptions)
2117
0
{
2118
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2119
2120
0
    *ppszReturn = nullptr;
2121
2122
0
    return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2123
0
}
2124
2125
/************************************************************************/
2126
/*                           importFromWkt()                            */
2127
/************************************************************************/
2128
2129
/**
2130
 * \brief Import from WKT string.
2131
 *
2132
 * This method will wipe the existing SRS definition, and
2133
 * reassign it based on the contents of the passed WKT string.  Only as
2134
 * much of the input string as needed to construct this SRS is consumed from
2135
 * the input string, and the input string pointer
2136
 * is then updated to point to the remaining (unused) input.
2137
 *
2138
 * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2139
 * the CRS contained in it will be used to fill the OGRSpatialReference object,
2140
 * and the coordinate epoch potentially present used as the coordinate epoch
2141
 * property of the OGRSpatialReference object.
2142
 *
2143
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2144
 * Issues</a> page for implementation details of WKT in OGR.
2145
 *
2146
 * This method is the same as the C function OSRImportFromWkt().
2147
 *
2148
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2149
 * point to remaining unused input text.
2150
 *
2151
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2152
 * fails for any reason.
2153
 * @since GDAL 2.3
2154
 */
2155
2156
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2157
2158
268
{
2159
268
    return importFromWkt(ppszInput, nullptr);
2160
268
}
2161
2162
/************************************************************************/
2163
/*                           importFromWkt()                            */
2164
/************************************************************************/
2165
2166
/*! @cond Doxygen_Suppress */
2167
2168
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2169
                                          CSLConstList papszOptions)
2170
2171
19.6k
{
2172
19.6k
    return importFromWkt(&pszInput, papszOptions);
2173
19.6k
}
2174
2175
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2176
                                          CSLConstList papszOptions)
2177
2178
19.8k
{
2179
19.8k
    TAKE_OPTIONAL_LOCK();
2180
2181
19.8k
    if (!ppszInput || !*ppszInput)
2182
0
        return OGRERR_FAILURE;
2183
2184
19.8k
    if (strlen(*ppszInput) > 100 * 1000 &&
2185
19.8k
        CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2186
0
    {
2187
0
        CPLError(CE_Failure, CPLE_NotSupported,
2188
0
                 "Suspiciously large input for importFromWkt(). Rejecting it. "
2189
0
                 "You can remove this limitation by definition the "
2190
0
                 "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2191
0
        return OGRERR_FAILURE;
2192
0
    }
2193
2194
19.8k
    Clear();
2195
2196
19.8k
    bool canCache = false;
2197
19.8k
    auto tlsCache = OSRGetProjTLSCache();
2198
19.8k
    std::string osWkt;
2199
19.8k
    if (**ppszInput)
2200
19.8k
    {
2201
19.8k
        osWkt = *ppszInput;
2202
19.8k
        auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2203
19.8k
        if (cachedObj)
2204
12.2k
        {
2205
12.2k
            d->setPjCRS(cachedObj);
2206
12.2k
        }
2207
7.61k
        else
2208
7.61k
        {
2209
7.61k
            CPLStringList aosOptions(papszOptions);
2210
7.61k
            if (aosOptions.FetchNameValue("STRICT") == nullptr)
2211
7.61k
                aosOptions.SetNameValue("STRICT", "NO");
2212
7.61k
            PROJ_STRING_LIST warnings = nullptr;
2213
7.61k
            PROJ_STRING_LIST errors = nullptr;
2214
7.61k
            auto ctxt = d->getPROJContext();
2215
7.61k
            auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2216
7.61k
                                           &warnings, &errors);
2217
7.61k
            d->setPjCRS(pj);
2218
2219
7.61k
            for (auto iter = warnings; iter && *iter; ++iter)
2220
3
            {
2221
3
                d->m_wktImportWarnings.push_back(*iter);
2222
3
            }
2223
13.0k
            for (auto iter = errors; iter && *iter; ++iter)
2224
5.44k
            {
2225
5.44k
                d->m_wktImportErrors.push_back(*iter);
2226
5.44k
                if (!d->m_pj_crs)
2227
228
                {
2228
228
                    CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2229
228
                }
2230
5.44k
            }
2231
7.61k
            if (warnings == nullptr && errors == nullptr)
2232
2.16k
            {
2233
2.16k
                canCache = true;
2234
2.16k
            }
2235
7.61k
            proj_string_list_destroy(warnings);
2236
7.61k
            proj_string_list_destroy(errors);
2237
7.61k
        }
2238
19.8k
    }
2239
19.8k
    if (!d->m_pj_crs)
2240
229
        return OGRERR_CORRUPT_DATA;
2241
2242
    // Only accept CRS objects
2243
19.6k
    if (!proj_is_crs(d->m_pj_crs))
2244
105
    {
2245
105
        Clear();
2246
105
        return OGRERR_CORRUPT_DATA;
2247
105
    }
2248
2249
19.5k
    if (canCache)
2250
2.15k
    {
2251
2.15k
        tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2252
2.15k
    }
2253
2254
19.5k
    if (strstr(*ppszInput, "CENTER_LONG"))
2255
1.25k
    {
2256
1.25k
        auto poRoot = new OGR_SRSNode();
2257
1.25k
        d->setRoot(poRoot);
2258
1.25k
        const char *pszTmp = *ppszInput;
2259
1.25k
        poRoot->importFromWkt(&pszTmp);
2260
1.25k
        d->m_bHasCenterLong = true;
2261
1.25k
    }
2262
2263
    // TODO? we don't really update correctly since we assume that the
2264
    // passed string is only WKT.
2265
19.5k
    *ppszInput += strlen(*ppszInput);
2266
19.5k
    return OGRERR_NONE;
2267
2268
#if no_longer_implemented_for_now
2269
    /* -------------------------------------------------------------------- */
2270
    /*      The following seems to try and detect and unconsumed            */
2271
    /*      VERTCS[] coordinate system definition (ESRI style) and to       */
2272
    /*      import and attach it to the existing root.  Likely we will      */
2273
    /*      need to extend this somewhat to bring it into an acceptable     */
2274
    /*      OGRSpatialReference organization at some point.                 */
2275
    /* -------------------------------------------------------------------- */
2276
    if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2277
    {
2278
        if (((*ppszInput)[0]) == ',')
2279
            (*ppszInput)++;
2280
        OGR_SRSNode *poNewChild = new OGR_SRSNode();
2281
        poRoot->AddChild(poNewChild);
2282
        return poNewChild->importFromWkt(ppszInput);
2283
    }
2284
#endif
2285
19.6k
}
2286
2287
/*! @endcond */
2288
2289
/**
2290
 * \brief Import from WKT string.
2291
 *
2292
 * This method will wipe the existing SRS definition, and
2293
 * reassign it based on the contents of the passed WKT string.  Only as
2294
 * much of the input string as needed to construct this SRS is consumed from
2295
 * the input string, and the input string pointer
2296
 * is then updated to point to the remaining (unused) input.
2297
 *
2298
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2299
 * Issues</a> page for implementation details of WKT in OGR.
2300
 *
2301
 * This method is the same as the C function OSRImportFromWkt().
2302
 *
2303
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2304
 * point to remaining unused input text.
2305
 *
2306
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2307
 * fails for any reason.
2308
 * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2309
 * char*)
2310
 */
2311
2312
OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2313
2314
0
{
2315
0
    return importFromWkt(const_cast<const char **>(ppszInput));
2316
0
}
2317
2318
/**
2319
 * \brief Import from WKT string.
2320
 *
2321
 * This method will wipe the existing SRS definition, and
2322
 * reassign it based on the contents of the passed WKT string.  Only as
2323
 * much of the input string as needed to construct this SRS is consumed from
2324
 * the input string, and the input string pointer
2325
 * is then updated to point to the remaining (unused) input.
2326
 *
2327
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2328
 * Issues</a> page for implementation details of WKT in OGR.
2329
 *
2330
 * @param pszInput Input WKT
2331
 *
2332
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2333
 * fails for any reason.
2334
 * @since GDAL 2.3
2335
 */
2336
2337
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2338
268
{
2339
268
    return importFromWkt(&pszInput);
2340
268
}
2341
2342
/************************************************************************/
2343
/*                              Validate()                              */
2344
/************************************************************************/
2345
2346
/**
2347
 * \brief Validate CRS imported with importFromWkt() or with modified with
2348
 * direct node manipulations. Otherwise the CRS should be always valid.
2349
 *
2350
 * This method attempts to verify that the spatial reference system is
2351
 * well formed, and consists of known tokens.  The validation is not
2352
 * comprehensive.
2353
 *
2354
 * This method is the same as the C function OSRValidate().
2355
 *
2356
 * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2357
 * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2358
 * but contains non-standard PROJECTION[] values.
2359
 */
2360
2361
OGRErr OGRSpatialReference::Validate() const
2362
2363
0
{
2364
0
    TAKE_OPTIONAL_LOCK();
2365
2366
0
    for (const auto &str : d->m_wktImportErrors)
2367
0
    {
2368
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2369
0
    }
2370
0
    for (const auto &str : d->m_wktImportWarnings)
2371
0
    {
2372
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2373
0
    }
2374
0
    if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2375
0
    {
2376
0
        return OGRERR_CORRUPT_DATA;
2377
0
    }
2378
0
    if (!d->m_wktImportWarnings.empty())
2379
0
    {
2380
0
        return OGRERR_UNSUPPORTED_SRS;
2381
0
    }
2382
0
    return OGRERR_NONE;
2383
0
}
2384
2385
/************************************************************************/
2386
/*                            OSRValidate()                             */
2387
/************************************************************************/
2388
/**
2389
 * \brief Validate SRS tokens.
2390
 *
2391
 * This function is the same as the C++ method OGRSpatialReference::Validate().
2392
 */
2393
OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2394
2395
0
{
2396
0
    VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2397
2398
0
    return OGRSpatialReference::FromHandle(hSRS)->Validate();
2399
0
}
2400
2401
/************************************************************************/
2402
/*                          OSRImportFromWkt()                          */
2403
/************************************************************************/
2404
2405
/**
2406
 * \brief Import from WKT string.
2407
 *
2408
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2409
 * Issues</a> page for implementation details of WKT in OGR.
2410
 *
2411
 * This function is the same as OGRSpatialReference::importFromWkt().
2412
 */
2413
2414
OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2415
2416
0
{
2417
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2418
2419
0
    return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2420
0
}
2421
2422
/************************************************************************/
2423
/*                              SetNode()                               */
2424
/************************************************************************/
2425
2426
/**
2427
 * \brief Set attribute value in spatial reference.
2428
 *
2429
 * Missing intermediate nodes in the path will be created if not already
2430
 * in existence.  If the attribute has no children one will be created and
2431
 * assigned the value otherwise the zeroth child will be assigned the value.
2432
 *
2433
 * This method does the same as the C function OSRSetAttrValue().
2434
 *
2435
 * @param pszNodePath full path to attribute to be set.  For instance
2436
 * "PROJCS|GEOGCS|UNIT".
2437
 *
2438
 * @param pszNewNodeValue value to be assigned to node, such as "meter".
2439
 * This may be NULL if you just want to force creation of the intermediate
2440
 * path.
2441
 *
2442
 * @return OGRERR_NONE on success.
2443
 */
2444
2445
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2446
                                    const char *pszNewNodeValue)
2447
2448
83.0k
{
2449
83.0k
    TAKE_OPTIONAL_LOCK();
2450
2451
83.0k
    char **papszPathTokens =
2452
83.0k
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2453
2454
83.0k
    if (CSLCount(papszPathTokens) < 1)
2455
0
    {
2456
0
        CSLDestroy(papszPathTokens);
2457
0
        return OGRERR_FAILURE;
2458
0
    }
2459
2460
83.0k
    if (GetRoot() == nullptr ||
2461
83.0k
        !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2462
18.6k
    {
2463
18.6k
        if (EQUAL(papszPathTokens[0], "PROJCS") &&
2464
18.6k
            CSLCount(papszPathTokens) == 1)
2465
4.51k
        {
2466
4.51k
            CSLDestroy(papszPathTokens);
2467
4.51k
            return SetProjCS(pszNewNodeValue);
2468
4.51k
        }
2469
14.0k
        else
2470
14.0k
        {
2471
14.0k
            SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2472
14.0k
        }
2473
18.6k
    }
2474
2475
78.5k
    OGR_SRSNode *poNode = GetRoot();
2476
170k
    for (int i = 1; papszPathTokens[i] != nullptr; i++)
2477
92.2k
    {
2478
92.2k
        int j = 0;  // Used after for.
2479
2480
275k
        for (; j < poNode->GetChildCount(); j++)
2481
223k
        {
2482
223k
            if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2483
40.2k
            {
2484
40.2k
                poNode = poNode->GetChild(j);
2485
40.2k
                j = -1;
2486
40.2k
                break;
2487
40.2k
            }
2488
223k
        }
2489
2490
92.2k
        if (j != -1)
2491
52.0k
        {
2492
52.0k
            OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2493
52.0k
            poNode->AddChild(poNewNode);
2494
52.0k
            poNode = poNewNode;
2495
52.0k
        }
2496
92.2k
    }
2497
2498
78.5k
    CSLDestroy(papszPathTokens);
2499
2500
78.5k
    if (pszNewNodeValue != nullptr)
2501
78.5k
    {
2502
78.5k
        if (poNode->GetChildCount() > 0)
2503
12.4k
            poNode->GetChild(0)->SetValue(pszNewNodeValue);
2504
66.1k
        else
2505
66.1k
            poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2506
78.5k
    };
2507
78.5k
    return OGRERR_NONE;
2508
83.0k
}
2509
2510
/************************************************************************/
2511
/*                          OSRSetAttrValue()                           */
2512
/************************************************************************/
2513
2514
/**
2515
 * \brief Set attribute value in spatial reference.
2516
 *
2517
 * This function is the same as OGRSpatialReference::SetNode()
2518
 */
2519
OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2520
                                   const char *pszPath, const char *pszValue)
2521
2522
0
{
2523
0
    VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2524
2525
0
    return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2526
0
}
2527
2528
/************************************************************************/
2529
/*                              SetNode()                               */
2530
/************************************************************************/
2531
2532
/**
2533
 * \brief Set attribute value in spatial reference.
2534
 *
2535
 * Missing intermediate nodes in the path will be created if not already
2536
 * in existence.  If the attribute has no children one will be created and
2537
 * assigned the value otherwise the zeroth child will be assigned the value.
2538
 *
2539
 * This method does the same as the C function OSRSetAttrValue().
2540
 *
2541
 * @param pszNodePath full path to attribute to be set.  For instance
2542
 * "PROJCS|GEOGCS|UNIT".
2543
 *
2544
 * @param dfValue value to be assigned to node.
2545
 *
2546
 * @return OGRERR_NONE on success.
2547
 */
2548
2549
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2550
2551
0
{
2552
0
    char szValue[64] = {'\0'};
2553
2554
0
    if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2555
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2556
0
    else
2557
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2558
2559
0
    return SetNode(pszNodePath, szValue);
2560
0
}
2561
2562
/************************************************************************/
2563
/*                          SetAngularUnits()                           */
2564
/************************************************************************/
2565
2566
/**
2567
 * \brief Set the angular units for the geographic coordinate system.
2568
 *
2569
 * This method creates a UNIT subnode with the specified values as a
2570
 * child of the GEOGCS node.
2571
 *
2572
 * This method does the same as the C function OSRSetAngularUnits().
2573
 *
2574
 * @param pszUnitsName the units name to be used.  Some preferred units
2575
 * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2576
 *
2577
 * @param dfInRadians the value to multiple by an angle in the indicated
2578
 * units to transform to radians.  Some standard conversion factors can
2579
 * be found in ogr_srs_api.h.
2580
 *
2581
 * @return OGRERR_NONE on success.
2582
 */
2583
2584
OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2585
                                            double dfInRadians)
2586
2587
0
{
2588
0
    TAKE_OPTIONAL_LOCK();
2589
2590
0
    d->bNormInfoSet = FALSE;
2591
2592
0
    d->refreshProjObj();
2593
0
    if (!d->m_pj_crs)
2594
0
        return OGRERR_FAILURE;
2595
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2596
0
    if (!geodCRS)
2597
0
        return OGRERR_FAILURE;
2598
0
    proj_destroy(geodCRS);
2599
0
    d->demoteFromBoundCRS();
2600
0
    d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2601
0
                                               pszUnitsName, dfInRadians,
2602
0
                                               nullptr, nullptr));
2603
0
    d->undoDemoteFromBoundCRS();
2604
2605
0
    d->m_osAngularUnits = pszUnitsName;
2606
0
    d->m_dfAngularUnitToRadian = dfInRadians;
2607
2608
0
    return OGRERR_NONE;
2609
0
}
2610
2611
/************************************************************************/
2612
/*                         OSRSetAngularUnits()                         */
2613
/************************************************************************/
2614
2615
/**
2616
 * \brief Set the angular units for the geographic coordinate system.
2617
 *
2618
 * This function is the same as OGRSpatialReference::SetAngularUnits()
2619
 */
2620
OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2621
                          double dfInRadians)
2622
2623
0
{
2624
0
    VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2625
2626
0
    return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2627
0
}
2628
2629
/************************************************************************/
2630
/*                          GetAngularUnits()                           */
2631
/************************************************************************/
2632
2633
/**
2634
 * \brief Fetch angular geographic coordinate system units.
2635
 *
2636
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2637
 * will be assumed.  This method only checks directly under the GEOGCS node
2638
 * for units.
2639
 *
2640
 * This method does the same thing as the C function OSRGetAngularUnits().
2641
 *
2642
 * @param ppszName a pointer to be updated with the pointer to the units name.
2643
 * The returned value remains internal to the OGRSpatialReference and should
2644
 * not be freed, or modified.  It may be invalidated on the next
2645
 * OGRSpatialReference call.
2646
 *
2647
 * @return the value to multiply by angular distances to transform them to
2648
 * radians.
2649
 * @since GDAL 2.3.0
2650
 */
2651
2652
double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2653
2654
0
{
2655
0
    TAKE_OPTIONAL_LOCK();
2656
2657
0
    d->refreshProjObj();
2658
2659
0
    if (!d->m_osAngularUnits.empty())
2660
0
    {
2661
0
        if (ppszName != nullptr)
2662
0
            *ppszName = d->m_osAngularUnits.c_str();
2663
0
        return d->m_dfAngularUnitToRadian;
2664
0
    }
2665
2666
0
    do
2667
0
    {
2668
0
        if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2669
0
        {
2670
0
            break;
2671
0
        }
2672
2673
0
        auto geodCRS =
2674
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2675
0
        if (!geodCRS)
2676
0
        {
2677
0
            break;
2678
0
        }
2679
0
        auto coordSys =
2680
0
            proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2681
0
        proj_destroy(geodCRS);
2682
0
        if (!coordSys)
2683
0
        {
2684
0
            break;
2685
0
        }
2686
0
        if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2687
0
            PJ_CS_TYPE_ELLIPSOIDAL)
2688
0
        {
2689
0
            proj_destroy(coordSys);
2690
0
            break;
2691
0
        }
2692
2693
0
        double dfConvFactor = 0.0;
2694
0
        const char *pszUnitName = nullptr;
2695
0
        if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2696
0
                                   nullptr, nullptr, &dfConvFactor,
2697
0
                                   &pszUnitName, nullptr, nullptr))
2698
0
        {
2699
0
            proj_destroy(coordSys);
2700
0
            break;
2701
0
        }
2702
2703
0
        d->m_osAngularUnits = pszUnitName;
2704
2705
0
        proj_destroy(coordSys);
2706
0
        d->m_dfAngularUnitToRadian = dfConvFactor;
2707
0
    } while (false);
2708
2709
0
    if (d->m_osAngularUnits.empty())
2710
0
    {
2711
0
        d->m_osAngularUnits = "degree";
2712
0
        d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2713
0
    }
2714
2715
0
    if (ppszName != nullptr)
2716
0
        *ppszName = d->m_osAngularUnits.c_str();
2717
0
    return d->m_dfAngularUnitToRadian;
2718
0
}
2719
2720
/**
2721
 * \brief Fetch angular geographic coordinate system units.
2722
 *
2723
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2724
 * will be assumed.  This method only checks directly under the GEOGCS node
2725
 * for units.
2726
 *
2727
 * This method does the same thing as the C function OSRGetAngularUnits().
2728
 *
2729
 * @param ppszName a pointer to be updated with the pointer to the units name.
2730
 * The returned value remains internal to the OGRSpatialReference and should
2731
 * not be freed, or modified.  It may be invalidated on the next
2732
 * OGRSpatialReference call.
2733
 *
2734
 * @return the value to multiply by angular distances to transform them to
2735
 * radians.
2736
 * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2737
 */
2738
2739
double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2740
2741
0
{
2742
0
    return GetAngularUnits(const_cast<const char **>(ppszName));
2743
0
}
2744
2745
/************************************************************************/
2746
/*                         OSRGetAngularUnits()                         */
2747
/************************************************************************/
2748
2749
/**
2750
 * \brief Fetch angular geographic coordinate system units.
2751
 *
2752
 * This function is the same as OGRSpatialReference::GetAngularUnits()
2753
 */
2754
double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2755
2756
0
{
2757
0
    VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2758
2759
0
    return ToPointer(hSRS)->GetAngularUnits(
2760
0
        const_cast<const char **>(ppszName));
2761
0
}
2762
2763
/************************************************************************/
2764
/*                 SetLinearUnitsAndUpdateParameters()                  */
2765
/************************************************************************/
2766
2767
/**
2768
 * \brief Set the linear units for the projection.
2769
 *
2770
 * This method creates a UNIT subnode with the specified values as a
2771
 * child of the PROJCS or LOCAL_CS node.   It works the same as the
2772
 * SetLinearUnits() method, but it also updates all existing linear
2773
 * projection parameter values from the old units to the new units.
2774
 *
2775
 * @param pszName the units name to be used.  Some preferred units
2776
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2777
 * and SRS_UL_US_FOOT.
2778
 *
2779
 * @param dfInMeters the value to multiple by a length in the indicated
2780
 * units to transform to meters.  Some standard conversion factors can
2781
 * be found in ogr_srs_api.h.
2782
 *
2783
 * @param pszUnitAuthority Unit authority name. Or nullptr
2784
 *
2785
 * @param pszUnitCode Unit code. Or nullptr
2786
 *
2787
 * @return OGRERR_NONE on success.
2788
 */
2789
2790
OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2791
    const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2792
    const char *pszUnitCode)
2793
2794
280
{
2795
280
    TAKE_OPTIONAL_LOCK();
2796
2797
280
    if (dfInMeters <= 0.0)
2798
0
        return OGRERR_FAILURE;
2799
2800
280
    d->refreshProjObj();
2801
280
    if (!d->m_pj_crs)
2802
0
        return OGRERR_FAILURE;
2803
2804
280
    d->demoteFromBoundCRS();
2805
280
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2806
247
    {
2807
247
        d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2808
247
            d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2809
247
            pszUnitAuthority, pszUnitCode, true));
2810
247
    }
2811
280
    d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2812
280
                                              pszName, dfInMeters,
2813
280
                                              pszUnitAuthority, pszUnitCode));
2814
280
    d->undoDemoteFromBoundCRS();
2815
2816
280
    d->m_osLinearUnits = pszName;
2817
280
    d->dfToMeter = dfInMeters;
2818
2819
280
    return OGRERR_NONE;
2820
280
}
2821
2822
/************************************************************************/
2823
/*                OSRSetLinearUnitsAndUpdateParameters()                */
2824
/************************************************************************/
2825
2826
/**
2827
 * \brief Set the linear units for the projection.
2828
 *
2829
 * This function is the same as
2830
 *   OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2831
 */
2832
OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2833
                                            const char *pszUnits,
2834
                                            double dfInMeters)
2835
2836
0
{
2837
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2838
0
                      OGRERR_FAILURE);
2839
2840
0
    return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2841
0
                                                              dfInMeters);
2842
0
}
2843
2844
/************************************************************************/
2845
/*                           SetLinearUnits()                           */
2846
/************************************************************************/
2847
2848
/**
2849
 * \brief Set the linear units for the projection.
2850
 *
2851
 * This method creates a UNIT subnode with the specified values as a
2852
 * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2853
 * Geographic 3D CRS the vertical axis units will be set.
2854
 *
2855
 * This method does the same as the C function OSRSetLinearUnits().
2856
 *
2857
 * @param pszUnitsName the units name to be used.  Some preferred units
2858
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2859
 * and SRS_UL_US_FOOT.
2860
 *
2861
 * @param dfInMeters the value to multiple by a length in the indicated
2862
 * units to transform to meters.  Some standard conversion factors can
2863
 * be found in ogr_srs_api.h.
2864
 *
2865
 * @return OGRERR_NONE on success.
2866
 */
2867
2868
OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2869
                                           double dfInMeters)
2870
2871
33.3k
{
2872
33.3k
    return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2873
33.3k
}
2874
2875
/************************************************************************/
2876
/*                         OSRSetLinearUnits()                          */
2877
/************************************************************************/
2878
2879
/**
2880
 * \brief Set the linear units for the projection.
2881
 *
2882
 * This function is the same as OGRSpatialReference::SetLinearUnits()
2883
 */
2884
OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2885
                         double dfInMeters)
2886
2887
0
{
2888
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2889
2890
0
    return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2891
0
}
2892
2893
/************************************************************************/
2894
/*                        SetTargetLinearUnits()                        */
2895
/************************************************************************/
2896
2897
/**
2898
 * \brief Set the linear units for the projection.
2899
 *
2900
 * This method creates a UNIT subnode with the specified values as a
2901
 * child of the target node.
2902
 *
2903
 * This method does the same as the C function OSRSetTargetLinearUnits().
2904
 *
2905
 * @param pszTargetKey the keyword to set the linear units for.
2906
 * i.e. "PROJCS" or "VERT_CS"
2907
 *
2908
 * @param pszUnitsName the units name to be used.  Some preferred units
2909
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2910
 * and SRS_UL_US_FOOT.
2911
 *
2912
 * @param dfInMeters the value to multiple by a length in the indicated
2913
 * units to transform to meters.  Some standard conversion factors can
2914
 * be found in ogr_srs_api.h.
2915
 *
2916
 * @param pszUnitAuthority Unit authority name. Or nullptr
2917
 *
2918
 * @param pszUnitCode Unit code. Or nullptr
2919
 *
2920
 * @return OGRERR_NONE on success.
2921
 *
2922
 * @since OGR 1.9.0
2923
 */
2924
2925
OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
2926
                                                 const char *pszUnitsName,
2927
                                                 double dfInMeters,
2928
                                                 const char *pszUnitAuthority,
2929
                                                 const char *pszUnitCode)
2930
2931
44.0k
{
2932
44.0k
    TAKE_OPTIONAL_LOCK();
2933
2934
44.0k
    if (dfInMeters <= 0.0)
2935
814
        return OGRERR_FAILURE;
2936
2937
43.2k
    d->refreshProjObj();
2938
43.2k
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2939
43.2k
    if (pszTargetKey == nullptr)
2940
43.2k
    {
2941
43.2k
        if (!d->m_pj_crs)
2942
127
            return OGRERR_FAILURE;
2943
2944
43.1k
        d->demoteFromBoundCRS();
2945
43.1k
        if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2946
39.2k
        {
2947
39.2k
            d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2948
39.2k
                d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2949
39.2k
                pszUnitAuthority, pszUnitCode, false));
2950
39.2k
        }
2951
43.1k
        d->setPjCRS(proj_crs_alter_cs_linear_unit(
2952
43.1k
            d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2953
43.1k
            pszUnitAuthority, pszUnitCode));
2954
43.1k
        d->undoDemoteFromBoundCRS();
2955
2956
43.1k
        d->m_osLinearUnits = pszUnitsName;
2957
43.1k
        d->dfToMeter = dfInMeters;
2958
2959
43.1k
        return OGRERR_NONE;
2960
43.2k
    }
2961
2962
0
    OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
2963
2964
0
    if (poCS == nullptr)
2965
0
        return OGRERR_FAILURE;
2966
2967
0
    char szValue[128] = {'\0'};
2968
0
    if (dfInMeters < std::numeric_limits<int>::max() &&
2969
0
        dfInMeters > std::numeric_limits<int>::min() &&
2970
0
        dfInMeters == static_cast<int>(dfInMeters))
2971
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
2972
0
    else
2973
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
2974
2975
0
    OGR_SRSNode *poUnits = nullptr;
2976
0
    if (poCS->FindChild("UNIT") >= 0)
2977
0
    {
2978
0
        poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
2979
0
        if (poUnits->GetChildCount() < 2)
2980
0
            return OGRERR_FAILURE;
2981
0
        poUnits->GetChild(0)->SetValue(pszUnitsName);
2982
0
        poUnits->GetChild(1)->SetValue(szValue);
2983
0
        if (poUnits->FindChild("AUTHORITY") != -1)
2984
0
            poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
2985
0
    }
2986
0
    else
2987
0
    {
2988
0
        poUnits = new OGR_SRSNode("UNIT");
2989
0
        poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
2990
0
        poUnits->AddChild(new OGR_SRSNode(szValue));
2991
2992
0
        poCS->AddChild(poUnits);
2993
0
    }
2994
2995
0
    return OGRERR_NONE;
2996
0
}
2997
2998
/************************************************************************/
2999
/*                         OSRSetLinearUnits()                          */
3000
/************************************************************************/
3001
3002
/**
3003
 * \brief Set the linear units for the target node.
3004
 *
3005
 * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3006
 *
3007
 * @since OGR 1.9.0
3008
 */
3009
OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3010
                               const char *pszTargetKey, const char *pszUnits,
3011
                               double dfInMeters)
3012
3013
0
{
3014
0
    VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3015
3016
0
    return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3017
0
                                                 dfInMeters);
3018
0
}
3019
3020
/************************************************************************/
3021
/*                           GetLinearUnits()                           */
3022
/************************************************************************/
3023
3024
/**
3025
 * \brief Fetch linear projection units.
3026
 *
3027
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3028
 * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3029
 * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3030
 * axis units will be returned.
3031
 *
3032
 * This method does the same thing as the C function OSRGetLinearUnits()
3033
 *
3034
 * @param ppszName a pointer to be updated with the pointer to the units name.
3035
 * The returned value remains internal to the OGRSpatialReference and should
3036
 * not be freed, or modified.  It may be invalidated on the next
3037
 * OGRSpatialReference call.
3038
 *
3039
 * @return the value to multiply by linear distances to transform them to
3040
 * meters.
3041
 * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
3042
 */
3043
3044
double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3045
3046
0
{
3047
0
    return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3048
0
}
3049
3050
/**
3051
 * \brief Fetch linear projection units.
3052
 *
3053
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3054
 * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3055
 * for units.
3056
 *
3057
 * This method does the same thing as the C function OSRGetLinearUnits()
3058
 *
3059
 * @param ppszName a pointer to be updated with the pointer to the units name.
3060
 * The returned value remains internal to the OGRSpatialReference and should
3061
 * not be freed, or modified.  It may be invalidated on the next
3062
 * OGRSpatialReference call.
3063
 *
3064
 * @return the value to multiply by linear distances to transform them to
3065
 * meters.
3066
 * @since GDAL 2.3.0
3067
 */
3068
3069
double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3070
3071
28.4k
{
3072
28.4k
    return GetTargetLinearUnits(nullptr, ppszName);
3073
28.4k
}
3074
3075
/************************************************************************/
3076
/*                         OSRGetLinearUnits()                          */
3077
/************************************************************************/
3078
3079
/**
3080
 * \brief Fetch linear projection units.
3081
 *
3082
 * This function is the same as OGRSpatialReference::GetLinearUnits()
3083
 */
3084
double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3085
3086
0
{
3087
0
    VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3088
3089
0
    return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3090
0
}
3091
3092
/************************************************************************/
3093
/*                        GetTargetLinearUnits()                        */
3094
/************************************************************************/
3095
3096
/**
3097
 * \brief Fetch linear units for target.
3098
 *
3099
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3100
 *
3101
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3102
 *
3103
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3104
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3105
 * GEOCCS, GEOGCS and VERT_CS are looked up)
3106
 * @param ppszName a pointer to be updated with the pointer to the units name.
3107
 * The returned value remains internal to the OGRSpatialReference and should not
3108
 * be freed, or modified.  It may be invalidated on the next
3109
 * OGRSpatialReference call. ppszName can be set to NULL.
3110
 *
3111
 * @return the value to multiply by linear distances to transform them to
3112
 * meters.
3113
 *
3114
 * @since OGR 1.9.0
3115
 * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
3116
 * const.
3117
 */
3118
3119
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3120
                                                 const char **ppszName) const
3121
3122
42.4k
{
3123
42.4k
    TAKE_OPTIONAL_LOCK();
3124
3125
42.4k
    d->refreshProjObj();
3126
3127
42.4k
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3128
42.4k
    if (pszTargetKey == nullptr)
3129
28.5k
    {
3130
        // Use cached result if available
3131
28.5k
        if (!d->m_osLinearUnits.empty())
3132
17.8k
        {
3133
17.8k
            if (ppszName)
3134
17.8k
                *ppszName = d->m_osLinearUnits.c_str();
3135
17.8k
            return d->dfToMeter;
3136
17.8k
        }
3137
3138
10.6k
        while (true)
3139
10.6k
        {
3140
10.6k
            if (d->m_pj_crs == nullptr)
3141
4.20k
            {
3142
4.20k
                break;
3143
4.20k
            }
3144
3145
6.48k
            d->demoteFromBoundCRS();
3146
6.48k
            PJ *coordSys = nullptr;
3147
6.48k
            if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3148
97
            {
3149
119
                for (int iComponent = 0; iComponent < 2; iComponent++)
3150
119
                {
3151
119
                    auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3152
119
                                                       d->m_pj_crs, iComponent);
3153
119
                    if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3154
0
                    {
3155
0
                        auto temp =
3156
0
                            proj_get_source_crs(d->getPROJContext(), subCRS);
3157
0
                        proj_destroy(subCRS);
3158
0
                        subCRS = temp;
3159
0
                    }
3160
119
                    if (subCRS &&
3161
119
                        (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3162
119
                         proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3163
119
                         proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3164
97
                    {
3165
97
                        coordSys = proj_crs_get_coordinate_system(
3166
97
                            d->getPROJContext(), subCRS);
3167
97
                        proj_destroy(subCRS);
3168
97
                        break;
3169
97
                    }
3170
22
                    else if (subCRS)
3171
22
                    {
3172
22
                        proj_destroy(subCRS);
3173
22
                    }
3174
119
                }
3175
97
                if (coordSys == nullptr)
3176
0
                {
3177
0
                    d->undoDemoteFromBoundCRS();
3178
0
                    break;
3179
0
                }
3180
97
            }
3181
6.38k
            else
3182
6.38k
            {
3183
6.38k
                coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3184
6.38k
                                                          d->m_pj_crs);
3185
6.38k
            }
3186
3187
6.48k
            d->undoDemoteFromBoundCRS();
3188
6.48k
            if (!coordSys)
3189
0
            {
3190
0
                break;
3191
0
            }
3192
6.48k
            auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3193
3194
6.48k
            if (csType != PJ_CS_TYPE_CARTESIAN &&
3195
6.48k
                csType != PJ_CS_TYPE_VERTICAL &&
3196
6.48k
                csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3197
6.48k
                csType != PJ_CS_TYPE_SPHERICAL)
3198
0
            {
3199
0
                proj_destroy(coordSys);
3200
0
                break;
3201
0
            }
3202
3203
6.48k
            int axis = 0;
3204
3205
6.48k
            if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3206
6.48k
                csType == PJ_CS_TYPE_SPHERICAL)
3207
282
            {
3208
282
                const int axisCount =
3209
282
                    proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3210
3211
282
                if (axisCount == 3)
3212
105
                {
3213
105
                    axis = 2;
3214
105
                }
3215
177
                else
3216
177
                {
3217
177
                    proj_destroy(coordSys);
3218
177
                    break;
3219
177
                }
3220
282
            }
3221
3222
6.30k
            double dfConvFactor = 0.0;
3223
6.30k
            const char *pszUnitName = nullptr;
3224
6.30k
            if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3225
6.30k
                                       nullptr, nullptr, nullptr, &dfConvFactor,
3226
6.30k
                                       &pszUnitName, nullptr, nullptr))
3227
0
            {
3228
0
                proj_destroy(coordSys);
3229
0
                break;
3230
0
            }
3231
3232
6.30k
            d->m_osLinearUnits = pszUnitName;
3233
6.30k
            d->dfToMeter = dfConvFactor;
3234
6.30k
            if (ppszName)
3235
255
                *ppszName = d->m_osLinearUnits.c_str();
3236
3237
6.30k
            proj_destroy(coordSys);
3238
6.30k
            return dfConvFactor;
3239
6.30k
        }
3240
3241
4.37k
        d->m_osLinearUnits = "unknown";
3242
4.37k
        d->dfToMeter = 1.0;
3243
3244
4.37k
        if (ppszName != nullptr)
3245
4.20k
            *ppszName = d->m_osLinearUnits.c_str();
3246
4.37k
        return 1.0;
3247
10.6k
    }
3248
3249
13.9k
    const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3250
3251
13.9k
    if (ppszName != nullptr)
3252
13.9k
        *ppszName = "unknown";
3253
3254
13.9k
    if (poCS == nullptr)
3255
6
        return 1.0;
3256
3257
41.7k
    for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3258
41.7k
    {
3259
41.7k
        const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3260
3261
41.7k
        if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3262
13.9k
        {
3263
13.9k
            if (ppszName != nullptr)
3264
13.9k
                *ppszName = poChild->GetChild(0)->GetValue();
3265
3266
13.9k
            return CPLAtof(poChild->GetChild(1)->GetValue());
3267
13.9k
        }
3268
41.7k
    }
3269
3270
0
    return 1.0;
3271
13.9k
}
3272
3273
/**
3274
 * \brief Fetch linear units for target.
3275
 *
3276
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3277
 *
3278
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3279
 *
3280
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3281
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3282
 * GEOCCS and VERT_CS are looked up)
3283
 * @param ppszName a pointer to be updated with the pointer to the units name.
3284
 * The returned value remains internal to the OGRSpatialReference and should not
3285
 * be freed, or modified.  It may be invalidated on the next
3286
 * OGRSpatialReference call. ppszName can be set to NULL.
3287
 *
3288
 * @return the value to multiply by linear distances to transform them to
3289
 * meters.
3290
 *
3291
 * @since GDAL 2.3.0
3292
 */
3293
3294
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3295
                                                 char **ppszName) const
3296
3297
0
{
3298
0
    return GetTargetLinearUnits(pszTargetKey,
3299
0
                                const_cast<const char **>(ppszName));
3300
0
}
3301
3302
/************************************************************************/
3303
/*                      OSRGetTargetLinearUnits()                       */
3304
/************************************************************************/
3305
3306
/**
3307
 * \brief Fetch linear projection units.
3308
 *
3309
 * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3310
 *
3311
 * @since OGR 1.9.0
3312
 */
3313
double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3314
                               const char *pszTargetKey, char **ppszName)
3315
3316
0
{
3317
0
    VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3318
3319
0
    return ToPointer(hSRS)->GetTargetLinearUnits(
3320
0
        pszTargetKey, const_cast<const char **>(ppszName));
3321
0
}
3322
3323
/************************************************************************/
3324
/*                          GetPrimeMeridian()                          */
3325
/************************************************************************/
3326
3327
/**
3328
 * \brief Fetch prime meridian info.
3329
 *
3330
 * Returns the offset of the prime meridian from greenwich in degrees,
3331
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3332
 * in the coordinate system definition a value of "Greenwich" and an
3333
 * offset of 0.0 is assumed.
3334
 *
3335
 * If the prime meridian name is returned, the pointer is to an internal
3336
 * copy of the name. It should not be freed, altered or depended on after
3337
 * the next OGR call.
3338
 *
3339
 * This method is the same as the C function OSRGetPrimeMeridian().
3340
 *
3341
 * @param ppszName return location for prime meridian name.  If NULL, name
3342
 * is not returned.
3343
 *
3344
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3345
 * degrees.
3346
 * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3347
 */
3348
3349
double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3350
3351
0
{
3352
0
    TAKE_OPTIONAL_LOCK();
3353
3354
0
    d->refreshProjObj();
3355
3356
0
    if (!d->m_osPrimeMeridianName.empty())
3357
0
    {
3358
0
        if (ppszName != nullptr)
3359
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3360
0
        return d->dfFromGreenwich;
3361
0
    }
3362
3363
0
    while (true)
3364
0
    {
3365
0
        if (!d->m_pj_crs)
3366
0
            break;
3367
3368
0
        auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3369
0
        if (!pm)
3370
0
            break;
3371
3372
0
        d->m_osPrimeMeridianName = proj_get_name(pm);
3373
0
        if (ppszName)
3374
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3375
0
        double dfLongitude = 0.0;
3376
0
        double dfConvFactor = 0.0;
3377
0
        proj_prime_meridian_get_parameters(
3378
0
            d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3379
0
        proj_destroy(pm);
3380
0
        d->dfFromGreenwich =
3381
0
            dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3382
0
        return d->dfFromGreenwich;
3383
0
    }
3384
3385
0
    d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3386
0
    d->dfFromGreenwich = 0.0;
3387
0
    if (ppszName != nullptr)
3388
0
        *ppszName = d->m_osPrimeMeridianName.c_str();
3389
0
    return d->dfFromGreenwich;
3390
0
}
3391
3392
/**
3393
 * \brief Fetch prime meridian info.
3394
 *
3395
 * Returns the offset of the prime meridian from greenwich in degrees,
3396
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3397
 * in the coordinate system definition a value of "Greenwich" and an
3398
 * offset of 0.0 is assumed.
3399
 *
3400
 * If the prime meridian name is returned, the pointer is to an internal
3401
 * copy of the name. It should not be freed, altered or depended on after
3402
 * the next OGR call.
3403
 *
3404
 * This method is the same as the C function OSRGetPrimeMeridian().
3405
 *
3406
 * @param ppszName return location for prime meridian name.  If NULL, name
3407
 * is not returned.
3408
 *
3409
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3410
 * degrees.
3411
 * @since GDAL 2.3.0
3412
 */
3413
3414
double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3415
3416
0
{
3417
0
    return GetPrimeMeridian(const_cast<const char **>(ppszName));
3418
0
}
3419
3420
/************************************************************************/
3421
/*                        OSRGetPrimeMeridian()                         */
3422
/************************************************************************/
3423
3424
/**
3425
 * \brief Fetch prime meridian info.
3426
 *
3427
 * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3428
 */
3429
double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3430
3431
0
{
3432
0
    VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3433
3434
0
    return ToPointer(hSRS)->GetPrimeMeridian(
3435
0
        const_cast<const char **>(ppszName));
3436
0
}
3437
3438
/************************************************************************/
3439
/*                             SetGeogCS()                              */
3440
/************************************************************************/
3441
3442
/**
3443
 * \brief Set geographic coordinate system.
3444
 *
3445
 * This method is used to set the datum, ellipsoid, prime meridian and
3446
 * angular units for a geographic coordinate system.  It can be used on its
3447
 * own to establish a geographic spatial reference, or applied to a
3448
 * projected coordinate system to establish the underlying geographic
3449
 * coordinate system.
3450
 *
3451
 * This method does the same as the C function OSRSetGeogCS().
3452
 *
3453
 * @param pszGeogName user visible name for the geographic coordinate system
3454
 * (not to serve as a key).
3455
 *
3456
 * @param pszDatumName key name for this datum.  The OpenGIS specification
3457
 * lists some known values, and otherwise EPSG datum names with a standard
3458
 * transformation are considered legal keys.
3459
 *
3460
 * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3461
 *
3462
 * @param dfSemiMajor the semi major axis of the spheroid.
3463
 *
3464
 * @param dfInvFlattening the inverse flattening for the spheroid.
3465
 * This can be computed from the semi minor axis as
3466
 * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3467
 *
3468
 * @param pszPMName the name of the prime meridian (not to serve as a key)
3469
 * If this is NULL a default value of "Greenwich" will be used.
3470
 *
3471
 * @param dfPMOffset the longitude of Greenwich relative to this prime
3472
 * meridian. Always in Degrees
3473
 *
3474
 * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3475
 * standard names).  If NULL a value of "degrees" will be assumed.
3476
 *
3477
 * @param dfConvertToRadians value to multiply angular units by to transform
3478
 * them to radians.  A value of SRS_UA_DEGREE_CONV will be used if
3479
 * pszAngularUnits is NULL.
3480
 *
3481
 * @return OGRERR_NONE on success.
3482
 */
3483
3484
OGRErr OGRSpatialReference::SetGeogCS(
3485
    const char *pszGeogName, const char *pszDatumName,
3486
    const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3487
    const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3488
    double dfConvertToRadians)
3489
3490
32.0k
{
3491
32.0k
    TAKE_OPTIONAL_LOCK();
3492
3493
32.0k
    d->bNormInfoSet = FALSE;
3494
32.0k
    d->m_osAngularUnits.clear();
3495
32.0k
    d->m_dfAngularUnitToRadian = 0.0;
3496
32.0k
    d->m_osPrimeMeridianName.clear();
3497
32.0k
    d->dfFromGreenwich = 0.0;
3498
3499
    /* -------------------------------------------------------------------- */
3500
    /*      For a geocentric coordinate system we want to set the datum     */
3501
    /*      and ellipsoid based on the GEOGCS.  Create the GEOGCS in a      */
3502
    /*      temporary srs and use the copy method which has special         */
3503
    /*      handling for GEOCCS.                                            */
3504
    /* -------------------------------------------------------------------- */
3505
32.0k
    if (IsGeocentric())
3506
3.08k
    {
3507
3.08k
        OGRSpatialReference oGCS;
3508
3509
3.08k
        oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3510
3.08k
                       dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3511
3.08k
                       dfConvertToRadians);
3512
3.08k
        return CopyGeogCSFrom(&oGCS);
3513
3.08k
    }
3514
3515
28.9k
    auto cs = proj_create_ellipsoidal_2D_cs(
3516
28.9k
        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3517
28.9k
        dfConvertToRadians);
3518
    // Prime meridian expressed in Degree
3519
28.9k
    auto obj = proj_create_geographic_crs(
3520
28.9k
        d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3521
28.9k
        dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3522
28.9k
    proj_destroy(cs);
3523
3524
28.9k
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3525
28.9k
        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3526
9.12k
    {
3527
9.12k
        d->setPjCRS(obj);
3528
9.12k
    }
3529
19.8k
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3530
19.8k
    {
3531
19.8k
        d->setPjCRS(
3532
19.8k
            proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3533
19.8k
        proj_destroy(obj);
3534
19.8k
    }
3535
0
    else
3536
0
    {
3537
0
        proj_destroy(obj);
3538
0
    }
3539
3540
28.9k
    return OGRERR_NONE;
3541
32.0k
}
3542
3543
/************************************************************************/
3544
/*                            OSRSetGeogCS()                            */
3545
/************************************************************************/
3546
3547
/**
3548
 * \brief Set geographic coordinate system.
3549
 *
3550
 * This function is the same as OGRSpatialReference::SetGeogCS()
3551
 */
3552
OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3553
                    const char *pszDatumName, const char *pszSpheroidName,
3554
                    double dfSemiMajor, double dfInvFlattening,
3555
                    const char *pszPMName, double dfPMOffset,
3556
                    const char *pszAngularUnits, double dfConvertToRadians)
3557
3558
0
{
3559
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3560
3561
0
    return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3562
0
                                      pszSpheroidName, dfSemiMajor,
3563
0
                                      dfInvFlattening, pszPMName, dfPMOffset,
3564
0
                                      pszAngularUnits, dfConvertToRadians);
3565
0
}
3566
3567
/************************************************************************/
3568
/*                         SetWellKnownGeogCS()                         */
3569
/************************************************************************/
3570
3571
/**
3572
 * \brief Set a GeogCS based on well known name.
3573
 *
3574
 * This may be called on an empty OGRSpatialReference to make a geographic
3575
 * coordinate system, or on something with an existing PROJCS node to
3576
 * set the underlying geographic coordinate system of a projected coordinate
3577
 * system.
3578
 *
3579
 * The following well known text values are currently supported,
3580
 * Except for "EPSG:n", the others are without dependency on EPSG data files:
3581
 * <ul>
3582
 * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3583
 * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3584
 * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3585
 * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3586
 * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3587
 * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3588
 * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3589
 * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3590
 * </ul>
3591
 *
3592
 * @param pszName name of well known geographic coordinate system.
3593
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3594
 * recognised, the target object is already initialized, or an EPSG value
3595
 * can't be successfully looked up.
3596
 */
3597
3598
OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3599
3600
0
{
3601
0
    TAKE_OPTIONAL_LOCK();
3602
3603
    /* -------------------------------------------------------------------- */
3604
    /*      Check for EPSG authority numbers.                               */
3605
    /* -------------------------------------------------------------------- */
3606
0
    if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3607
0
    {
3608
0
        OGRSpatialReference oSRS2;
3609
0
        const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3610
0
        if (eErr != OGRERR_NONE)
3611
0
            return eErr;
3612
3613
0
        if (!oSRS2.IsGeographic())
3614
0
            return OGRERR_FAILURE;
3615
3616
0
        return CopyGeogCSFrom(&oSRS2);
3617
0
    }
3618
3619
    /* -------------------------------------------------------------------- */
3620
    /*      Check for simple names.                                         */
3621
    /* -------------------------------------------------------------------- */
3622
0
    const char *pszWKT = nullptr;
3623
3624
0
    if (EQUAL(pszName, "WGS84"))
3625
0
    {
3626
0
        pszWKT = SRS_WKT_WGS84_LAT_LONG;
3627
0
    }
3628
0
    else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3629
0
    {
3630
0
        pszWKT =
3631
0
            "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3632
0
            "ELLIPSOID[\"WGS "
3633
0
            "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3634
0
            "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3635
0
            "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3636
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3637
0
            "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3638
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3639
0
            "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3640
0
            "ID[\"OGC\",\"CRS84\"]]";
3641
0
    }
3642
0
    else if (EQUAL(pszName, "WGS72"))
3643
0
        pszWKT =
3644
0
            "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3645
0
            "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3646
0
            "AUTHORITY[\"EPSG\",\"6322\"]],"
3647
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3648
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3649
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3650
0
            "AUTHORITY[\"EPSG\",\"4322\"]]";
3651
3652
0
    else if (EQUAL(pszName, "NAD27"))
3653
0
        pszWKT =
3654
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3655
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3656
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3657
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3658
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3659
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3660
0
            "AUTHORITY[\"EPSG\",\"4267\"]]";
3661
3662
0
    else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3663
0
        pszWKT =
3664
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3665
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3666
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3667
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3668
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3669
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3670
3671
0
    else if (EQUAL(pszName, "NAD83"))
3672
0
        pszWKT =
3673
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3674
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3675
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3676
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3677
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3678
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3679
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3680
0
            "\"EPSG\",\"4269\"]]";
3681
3682
0
    else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3683
0
        pszWKT =
3684
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3685
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3686
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3687
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3688
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3689
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3690
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3691
3692
0
    else
3693
0
        return OGRERR_FAILURE;
3694
3695
    /* -------------------------------------------------------------------- */
3696
    /*      Import the WKT                                                  */
3697
    /* -------------------------------------------------------------------- */
3698
0
    OGRSpatialReference oSRS2;
3699
0
    const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3700
0
    if (eErr != OGRERR_NONE)
3701
0
        return eErr;
3702
3703
    /* -------------------------------------------------------------------- */
3704
    /*      Copy over.                                                      */
3705
    /* -------------------------------------------------------------------- */
3706
0
    return CopyGeogCSFrom(&oSRS2);
3707
0
}
3708
3709
/************************************************************************/
3710
/*                       OSRSetWellKnownGeogCS()                        */
3711
/************************************************************************/
3712
3713
/**
3714
 * \brief Set a GeogCS based on well known name.
3715
 *
3716
 * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3717
 */
3718
OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3719
3720
0
{
3721
0
    VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3722
3723
0
    return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3724
0
}
3725
3726
/************************************************************************/
3727
/*                           CopyGeogCSFrom()                           */
3728
/************************************************************************/
3729
3730
/**
3731
 * \brief Copy GEOGCS from another OGRSpatialReference.
3732
 *
3733
 * The GEOGCS information is copied into this OGRSpatialReference from another.
3734
 * If this object has a PROJCS root already, the GEOGCS is installed within
3735
 * it, otherwise it is installed as the root.
3736
 *
3737
 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3738
 *
3739
 * @return OGRERR_NONE on success or an error code.
3740
 */
3741
3742
OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3743
3744
3.09k
{
3745
3.09k
    TAKE_OPTIONAL_LOCK();
3746
3747
3.09k
    d->bNormInfoSet = FALSE;
3748
3.09k
    d->m_osAngularUnits.clear();
3749
3.09k
    d->m_dfAngularUnitToRadian = 0.0;
3750
3.09k
    d->m_osPrimeMeridianName.clear();
3751
3.09k
    d->dfFromGreenwich = 0.0;
3752
3753
3.09k
    d->refreshProjObj();
3754
3.09k
    poSrcSRS->d->refreshProjObj();
3755
3.09k
    if (!poSrcSRS->d->m_pj_crs)
3756
40
    {
3757
40
        return OGRERR_FAILURE;
3758
40
    }
3759
3.05k
    auto geodCRS =
3760
3.05k
        proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3761
3.05k
    if (!geodCRS)
3762
0
    {
3763
0
        return OGRERR_FAILURE;
3764
0
    }
3765
3766
    /* -------------------------------------------------------------------- */
3767
    /*      Handle geocentric coordinate systems specially.  We just        */
3768
    /*      want to copy the DATUM.                                         */
3769
    /* -------------------------------------------------------------------- */
3770
3.05k
    if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3771
3.04k
    {
3772
3.04k
        auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3773
3.04k
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
3774
3.04k
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3775
3.04k
        if (datum == nullptr)
3776
0
        {
3777
0
            datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3778
0
        }
3779
3.04k
#endif
3780
3.04k
        if (datum == nullptr)
3781
0
        {
3782
0
            proj_destroy(geodCRS);
3783
0
            return OGRERR_FAILURE;
3784
0
        }
3785
3786
3.04k
        const char *pszUnitName = nullptr;
3787
3.04k
        double unitConvFactor = GetLinearUnits(&pszUnitName);
3788
3789
3.04k
        auto pj_crs = proj_create_geocentric_crs_from_datum(
3790
3.04k
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3791
3.04k
            unitConvFactor);
3792
3.04k
        proj_destroy(datum);
3793
3794
3.04k
        d->setPjCRS(pj_crs);
3795
3.04k
    }
3796
3797
4
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3798
3
    {
3799
3
        auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3800
3
                                                  d->m_pj_crs, geodCRS);
3801
3
        d->setPjCRS(pj_crs);
3802
3
    }
3803
3804
1
    else
3805
1
    {
3806
1
        d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3807
1
    }
3808
3809
    // Apply TOWGS84 of source CRS
3810
3.05k
    if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3811
0
    {
3812
0
        auto target =
3813
0
            proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3814
0
        auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3815
0
                                              poSrcSRS->d->m_pj_crs);
3816
0
        d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3817
0
                                              target, co));
3818
0
        proj_destroy(target);
3819
0
        proj_destroy(co);
3820
0
    }
3821
3822
3.05k
    proj_destroy(geodCRS);
3823
3824
3.05k
    return OGRERR_NONE;
3825
3.05k
}
3826
3827
/************************************************************************/
3828
/*                         OSRCopyGeogCSFrom()                          */
3829
/************************************************************************/
3830
3831
/**
3832
 * \brief Copy GEOGCS from another OGRSpatialReference.
3833
 *
3834
 * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3835
 */
3836
OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3837
                         const OGRSpatialReferenceH hSrcSRS)
3838
3839
0
{
3840
0
    VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3841
0
    VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3842
3843
0
    return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3844
0
}
3845
3846
/************************************************************************/
3847
/*                   SET_FROM_USER_INPUT_LIMITATIONS_get()              */
3848
/************************************************************************/
3849
3850
/** Limitations for OGRSpatialReference::SetFromUserInput().
3851
 *
3852
 * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3853
 */
3854
const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3855
    "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3856
3857
/**
3858
 * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3859
 */
3860
CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3861
0
{
3862
0
    return SET_FROM_USER_INPUT_LIMITATIONS;
3863
0
}
3864
3865
/************************************************************************/
3866
/*                      RemoveIDFromMemberOfEnsembles()                 */
3867
/************************************************************************/
3868
3869
// cppcheck-suppress constParameterReference
3870
static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3871
0
{
3872
    // Remove "id" from members of datum ensembles for compatibility with
3873
    // older PROJ versions
3874
    // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3875
    // and https://github.com/OSGeo/PROJ/pull/3221
3876
0
    if (obj.GetType() == CPLJSONObject::Type::Object)
3877
0
    {
3878
0
        for (auto &subObj : obj.GetChildren())
3879
0
        {
3880
0
            RemoveIDFromMemberOfEnsembles(subObj);
3881
0
        }
3882
0
    }
3883
0
    else if (obj.GetType() == CPLJSONObject::Type::Array &&
3884
0
             obj.GetName() == "members")
3885
0
    {
3886
0
        for (auto &subObj : obj.ToArray())
3887
0
        {
3888
0
            if (subObj.GetType() == CPLJSONObject::Type::Object)
3889
0
            {
3890
0
                subObj.Delete("id");
3891
0
            }
3892
0
        }
3893
0
    }
3894
0
}
3895
3896
/************************************************************************/
3897
/*                          SetFromUserInput()                          */
3898
/************************************************************************/
3899
3900
/**
3901
 * \brief Set spatial reference from various text formats.
3902
 *
3903
 * This method will examine the provided input, and try to deduce the
3904
 * format, and then use it to initialize the spatial reference system.  It
3905
 * may take the following forms:
3906
 *
3907
 * <ol>
3908
 * <li> Well Known Text definition - passed on to importFromWkt().
3909
 * <li> "EPSG:n" - number passed on to importFromEPSG().
3910
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3911
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3912
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3913
 * <li> PROJ.4 definitions - passed on to importFromProj4().
3914
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3915
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3916
 * WGS84 or WGS72.
3917
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3918
 * <li> PROJJSON (PROJ &gt;= 6.2)
3919
 * </ol>
3920
 *
3921
 * It is expected that this method will be extended in the future to support
3922
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3923
 * State Plane definitions.
3924
 *
3925
 * This method is intended to be flexible, but by its nature it is
3926
 * imprecise as it must guess information about the format intended.  When
3927
 * possible applications should call the specific method appropriate if the
3928
 * input is known to be in a particular format.
3929
 *
3930
 * This method does the same thing as the OSRSetFromUserInput() function.
3931
 *
3932
 * @param pszDefinition text definition to try to deduce SRS from.
3933
 *
3934
 * @return OGRERR_NONE on success, or an error code if the name isn't
3935
 * recognised, the definition is corrupt, or an EPSG value can't be
3936
 * successfully looked up.
3937
 */
3938
3939
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
3940
0
{
3941
0
    return SetFromUserInput(pszDefinition, nullptr);
3942
0
}
3943
3944
/**
3945
 * \brief Set spatial reference from various text formats.
3946
 *
3947
 * This method will examine the provided input, and try to deduce the
3948
 * format, and then use it to initialize the spatial reference system.  It
3949
 * may take the following forms:
3950
 *
3951
 * <ol>
3952
 * <li> Well Known Text definition - passed on to importFromWkt().
3953
 * <li> "EPSG:n" - number passed on to importFromEPSG().
3954
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3955
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3956
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3957
 * <li> PROJ.4 definitions - passed on to importFromProj4().
3958
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3959
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3960
 * WGS84 or WGS72.
3961
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3962
 * <li> PROJJSON (PROJ &gt;= 6.2)
3963
 * </ol>
3964
 *
3965
 * It is expected that this method will be extended in the future to support
3966
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3967
 * State Plane definitions.
3968
 *
3969
 * This method is intended to be flexible, but by its nature it is
3970
 * imprecise as it must guess information about the format intended.  When
3971
 * possible applications should call the specific method appropriate if the
3972
 * input is known to be in a particular format.
3973
 *
3974
 * This method does the same thing as the OSRSetFromUserInput() and
3975
 * OSRSetFromUserInputEx() functions.
3976
 *
3977
 * @param pszDefinition text definition to try to deduce SRS from.
3978
 *
3979
 * @param papszOptions NULL terminated list of options, or NULL.
3980
 * <ol>
3981
 * <li> ALLOW_NETWORK_ACCESS=YES/NO.
3982
 *      Whether http:// or https:// access is allowed. Defaults to YES.
3983
 * <li> ALLOW_FILE_ACCESS=YES/NO.
3984
 *      Whether reading a file using the Virtual File System layer is allowed
3985
 *      (can also involve network access). Defaults to YES.
3986
 * </ol>
3987
 *
3988
 * @return OGRERR_NONE on success, or an error code if the name isn't
3989
 * recognised, the definition is corrupt, or an EPSG value can't be
3990
 * successfully looked up.
3991
 */
3992
3993
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
3994
                                             CSLConstList papszOptions)
3995
0
{
3996
0
    TAKE_OPTIONAL_LOCK();
3997
3998
    // Skip leading white space
3999
0
    while (isspace(static_cast<unsigned char>(*pszDefinition)))
4000
0
        pszDefinition++;
4001
4002
0
    if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4003
0
    {
4004
0
        pszDefinition += 6;
4005
0
    }
4006
4007
    /* -------------------------------------------------------------------- */
4008
    /*      Is it a recognised syntax?                                      */
4009
    /* -------------------------------------------------------------------- */
4010
0
    const char *const wktKeywords[] = {
4011
        // WKT1
4012
0
        "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4013
        // WKT2"
4014
0
        "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4015
0
        "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4016
0
        "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4017
0
    for (const char *keyword : wktKeywords)
4018
0
    {
4019
0
        if (STARTS_WITH_CI(pszDefinition, keyword))
4020
0
        {
4021
0
            return importFromWkt(pszDefinition);
4022
0
        }
4023
0
    }
4024
4025
0
    const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4026
0
    if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4027
0
    {
4028
0
        OGRErr eStatus = OGRERR_NONE;
4029
4030
0
        if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4031
0
        {
4032
            // Use proj_create() as it allows things like EPSG:3157+4617
4033
            // that are not normally supported by the below code that
4034
            // builds manually a compound CRS
4035
0
            PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4036
0
            if (!pj)
4037
0
            {
4038
0
                return OGRERR_FAILURE;
4039
0
            }
4040
0
            Clear();
4041
0
            d->setPjCRS(pj);
4042
0
            return OGRERR_NONE;
4043
0
        }
4044
0
        else
4045
0
        {
4046
0
            eStatus =
4047
0
                importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4048
0
        }
4049
4050
0
        return eStatus;
4051
0
    }
4052
4053
0
    if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4054
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4055
0
        STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4056
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4057
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4058
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4059
0
        return importFromURN(pszDefinition);
4060
4061
0
    if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4062
0
        STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4063
0
        STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4064
0
        STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4065
0
        STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4066
0
        return importFromCRSURL(pszDefinition);
4067
4068
0
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4069
0
        return importFromWMSAUTO(pszDefinition);
4070
4071
    // WMS/WCS OGC codes like OGC:CRS84.
4072
0
    if (EQUAL(pszDefinition, "OGC:CRS84"))
4073
0
        return SetWellKnownGeogCS(pszDefinition + 4);
4074
4075
0
    if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4076
0
        return SetWellKnownGeogCS(pszDefinition);
4077
4078
0
    if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4079
0
    {
4080
0
        char *pszFile = CPLStrdup(pszDefinition + 5);
4081
0
        char *pszCode = strstr(pszFile, ",") + 1;
4082
4083
0
        pszCode[-1] = '\0';
4084
4085
0
        OGRErr err = importFromDict(pszFile, pszCode);
4086
0
        CPLFree(pszFile);
4087
4088
0
        return err;
4089
0
    }
4090
4091
0
    if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4092
0
        EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4093
0
    {
4094
0
        Clear();
4095
0
        return SetWellKnownGeogCS(pszDefinition);
4096
0
    }
4097
4098
    // PROJJSON
4099
0
    if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4100
0
        (strstr(pszDefinition, "GeodeticCRS") ||
4101
0
         strstr(pszDefinition, "GeographicCRS") ||
4102
0
         strstr(pszDefinition, "ProjectedCRS") ||
4103
0
         strstr(pszDefinition, "VerticalCRS") ||
4104
0
         strstr(pszDefinition, "BoundCRS") ||
4105
0
         strstr(pszDefinition, "CompoundCRS") ||
4106
0
         strstr(pszDefinition, "DerivedGeodeticCRS") ||
4107
0
         strstr(pszDefinition, "DerivedGeographicCRS") ||
4108
0
         strstr(pszDefinition, "DerivedProjectedCRS") ||
4109
0
         strstr(pszDefinition, "DerivedVerticalCRS") ||
4110
0
         strstr(pszDefinition, "EngineeringCRS") ||
4111
0
         strstr(pszDefinition, "DerivedEngineeringCRS") ||
4112
0
         strstr(pszDefinition, "ParametricCRS") ||
4113
0
         strstr(pszDefinition, "DerivedParametricCRS") ||
4114
0
         strstr(pszDefinition, "TemporalCRS") ||
4115
0
         strstr(pszDefinition, "DerivedTemporalCRS")))
4116
0
    {
4117
0
        PJ *pj;
4118
0
        if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4119
0
        {
4120
            // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4121
            // a unknown id.
4122
0
            CPLJSONDocument oCRSDoc;
4123
0
            if (!oCRSDoc.LoadMemory(pszDefinition))
4124
0
                return OGRERR_CORRUPT_DATA;
4125
0
            CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4126
0
            RemoveIDFromMemberOfEnsembles(oCRSRoot);
4127
0
            pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4128
0
        }
4129
0
        else
4130
0
        {
4131
0
            pj = proj_create(d->getPROJContext(), pszDefinition);
4132
0
        }
4133
0
        if (!pj)
4134
0
        {
4135
0
            return OGRERR_FAILURE;
4136
0
        }
4137
0
        Clear();
4138
0
        d->setPjCRS(pj);
4139
0
        return OGRERR_NONE;
4140
0
    }
4141
4142
0
    if (strstr(pszDefinition, "+proj") != nullptr ||
4143
0
        strstr(pszDefinition, "+init") != nullptr)
4144
0
        return importFromProj4(pszDefinition);
4145
4146
0
    if (STARTS_WITH_CI(pszDefinition, "http://") ||
4147
0
        STARTS_WITH_CI(pszDefinition, "https://"))
4148
0
    {
4149
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4150
0
                                             "ALLOW_NETWORK_ACCESS", "YES")))
4151
0
            return importFromUrl(pszDefinition);
4152
4153
0
        CPLError(CE_Failure, CPLE_AppDefined,
4154
0
                 "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4155
0
                 pszDefinition);
4156
0
        return OGRERR_FAILURE;
4157
0
    }
4158
4159
0
    if (EQUAL(pszDefinition, "osgb:BNG"))
4160
0
    {
4161
0
        return importFromEPSG(27700);
4162
0
    }
4163
4164
    // Used by German CityGML files
4165
0
    if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4166
0
    {
4167
        // "ETRS89 / UTM Zone 32N + DHHN92 height"
4168
0
        return SetFromUserInput("EPSG:25832+5783");
4169
0
    }
4170
0
    else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4171
0
    {
4172
        // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4173
0
        return SetFromUserInput("EPSG:25832+7837");
4174
0
    }
4175
4176
    // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4177
0
    const char *pszDot = strrchr(pszDefinition, ':');
4178
0
    if (pszDot)
4179
0
    {
4180
0
        CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4181
0
        auto authorities =
4182
0
            proj_get_authorities_from_database(d->getPROJContext());
4183
0
        if (authorities)
4184
0
        {
4185
0
            std::set<std::string> aosCandidateAuthorities;
4186
0
            for (auto iter = authorities; *iter; ++iter)
4187
0
            {
4188
0
                if (*iter == osPrefix)
4189
0
                {
4190
0
                    aosCandidateAuthorities.clear();
4191
0
                    aosCandidateAuthorities.insert(*iter);
4192
0
                    break;
4193
0
                }
4194
                // Deal with "IAU_2015" as authority in the list and input
4195
                // "IAU:code"
4196
0
                else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4197
0
                             0 &&
4198
0
                         (*iter)[osPrefix.size()] == '_')
4199
0
                {
4200
0
                    aosCandidateAuthorities.insert(*iter);
4201
0
                }
4202
                // Deal with "IAU_2015" as authority in the list and input
4203
                // "IAU:2015:code"
4204
0
                else if (osPrefix.find(':') != std::string::npos &&
4205
0
                         osPrefix.size() == strlen(*iter) &&
4206
0
                         CPLString(osPrefix).replaceAll(':', '_') == *iter)
4207
0
                {
4208
0
                    aosCandidateAuthorities.clear();
4209
0
                    aosCandidateAuthorities.insert(*iter);
4210
0
                    break;
4211
0
                }
4212
0
            }
4213
4214
0
            proj_string_list_destroy(authorities);
4215
4216
0
            if (!aosCandidateAuthorities.empty())
4217
0
            {
4218
0
                auto obj = proj_create_from_database(
4219
0
                    d->getPROJContext(),
4220
0
                    aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4221
0
                    PJ_CATEGORY_CRS, false, nullptr);
4222
0
                if (!obj)
4223
0
                {
4224
0
                    return OGRERR_FAILURE;
4225
0
                }
4226
0
                Clear();
4227
0
                d->setPjCRS(obj);
4228
0
                return OGRERR_NONE;
4229
0
            }
4230
0
        }
4231
0
    }
4232
4233
    /* -------------------------------------------------------------------- */
4234
    /*      Try to open it as a file.                                       */
4235
    /* -------------------------------------------------------------------- */
4236
0
    if (!CPLTestBool(
4237
0
            CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4238
0
    {
4239
0
        VSIStatBufL sStat;
4240
0
        if (STARTS_WITH(pszDefinition, "/vsi") ||
4241
0
            VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4242
0
        {
4243
0
            CPLError(CE_Failure, CPLE_AppDefined,
4244
0
                     "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4245
0
                     pszDefinition);
4246
0
            return OGRERR_FAILURE;
4247
0
        }
4248
        // We used to silently return an error without a CE_Failure message
4249
        // Cf https://github.com/Toblerity/Fiona/issues/1063
4250
0
        return OGRERR_CORRUPT_DATA;
4251
0
    }
4252
4253
0
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4254
0
    VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4255
0
    if (fp == nullptr)
4256
0
        return OGRERR_CORRUPT_DATA;
4257
4258
0
    const size_t nBufMax = 100000;
4259
0
    char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4260
0
    const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4261
0
    VSIFCloseL(fp);
4262
4263
0
    if (nBytes == nBufMax - 1)
4264
0
    {
4265
0
        CPLDebug("OGR",
4266
0
                 "OGRSpatialReference::SetFromUserInput(%s), opened file "
4267
0
                 "but it is to large for our generous buffer.  Is it really "
4268
0
                 "just a WKT definition?",
4269
0
                 pszDefinition);
4270
0
        CPLFree(pszBuffer);
4271
0
        return OGRERR_FAILURE;
4272
0
    }
4273
4274
0
    pszBuffer[nBytes] = '\0';
4275
4276
0
    char *pszBufPtr = pszBuffer;
4277
0
    while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4278
0
        pszBufPtr++;
4279
4280
0
    OGRErr err = OGRERR_NONE;
4281
0
    if (pszBufPtr[0] == '<')
4282
0
        err = importFromXML(pszBufPtr);
4283
0
    else if ((strstr(pszBuffer, "+proj") != nullptr ||
4284
0
              strstr(pszBuffer, "+init") != nullptr) &&
4285
0
             (strstr(pszBuffer, "EXTENSION") == nullptr &&
4286
0
              strstr(pszBuffer, "extension") == nullptr))
4287
0
        err = importFromProj4(pszBufPtr);
4288
0
    else
4289
0
    {
4290
0
        if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4291
0
        {
4292
0
            pszBufPtr += 6;
4293
0
        }
4294
4295
        // coverity[tainted_data]
4296
0
        err = importFromWkt(pszBufPtr);
4297
0
    }
4298
4299
0
    CPLFree(pszBuffer);
4300
4301
0
    return err;
4302
0
}
4303
4304
/************************************************************************/
4305
/*                        OSRSetFromUserInput()                         */
4306
/************************************************************************/
4307
4308
/**
4309
 * \brief Set spatial reference from various text formats.
4310
 *
4311
 * This function is the same as OGRSpatialReference::SetFromUserInput()
4312
 *
4313
 * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4314
 */
4315
OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4316
                                       const char *pszDef)
4317
4318
0
{
4319
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4320
4321
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef);
4322
0
}
4323
4324
/************************************************************************/
4325
/*                       OSRSetFromUserInputEx()                        */
4326
/************************************************************************/
4327
4328
/**
4329
 * \brief Set spatial reference from various text formats.
4330
 *
4331
 * This function is the same as OGRSpatialReference::SetFromUserInput().
4332
 *
4333
 * @since GDAL 3.9
4334
 */
4335
OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4336
                             CSLConstList papszOptions)
4337
4338
0
{
4339
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4340
4341
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4342
0
}
4343
4344
/************************************************************************/
4345
/*                          ImportFromUrl()                             */
4346
/************************************************************************/
4347
4348
/**
4349
 * \brief Set spatial reference from a URL.
4350
 *
4351
 * This method will download the spatial reference at a given URL and
4352
 * feed it into SetFromUserInput for you.
4353
 *
4354
 * This method does the same thing as the OSRImportFromUrl() function.
4355
 *
4356
 * @param pszUrl text definition to try to deduce SRS from.
4357
 *
4358
 * @return OGRERR_NONE on success, or an error code with the curl
4359
 * error message if it is unable to download data.
4360
 */
4361
4362
OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4363
4364
0
{
4365
0
    TAKE_OPTIONAL_LOCK();
4366
4367
0
    if (!STARTS_WITH_CI(pszUrl, "http://") &&
4368
0
        !STARTS_WITH_CI(pszUrl, "https://"))
4369
0
    {
4370
0
        CPLError(CE_Failure, CPLE_AppDefined,
4371
0
                 "The given string is not recognized as a URL"
4372
0
                 "starting with 'http://' -- %s",
4373
0
                 pszUrl);
4374
0
        return OGRERR_FAILURE;
4375
0
    }
4376
4377
    /* -------------------------------------------------------------------- */
4378
    /*      Fetch the result.                                               */
4379
    /* -------------------------------------------------------------------- */
4380
0
    CPLErrorReset();
4381
4382
0
    std::string osUrl(pszUrl);
4383
    // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4384
    // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4385
    // to query WKT. To allow a static server to be used, rather append a
4386
    // "ogcwkt/" suffix.
4387
0
    for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4388
0
                                  "http://spatialreference.org/ref/"})
4389
0
    {
4390
0
        if (STARTS_WITH(pszUrl, pszPrefix))
4391
0
        {
4392
0
            const CPLStringList aosTokens(
4393
0
                CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4394
0
            if (aosTokens.size() == 2)
4395
0
            {
4396
0
                osUrl = "https://spatialreference.org/ref/";
4397
0
                osUrl += aosTokens[0];  // authority
4398
0
                osUrl += '/';
4399
0
                osUrl += aosTokens[1];  // code
4400
0
                osUrl += "/ogcwkt/";
4401
0
            }
4402
0
            break;
4403
0
        }
4404
0
    }
4405
4406
0
    const char *pszTimeout = "TIMEOUT=10";
4407
0
    char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4408
4409
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4410
4411
    /* -------------------------------------------------------------------- */
4412
    /*      Try to handle errors.                                           */
4413
    /* -------------------------------------------------------------------- */
4414
4415
0
    if (psResult == nullptr)
4416
0
        return OGRERR_FAILURE;
4417
0
    if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4418
0
        psResult->pabyData == nullptr)
4419
0
    {
4420
0
        if (CPLGetLastErrorNo() == 0)
4421
0
        {
4422
0
            CPLError(CE_Failure, CPLE_AppDefined,
4423
0
                     "No data was returned from the given URL");
4424
0
        }
4425
0
        CPLHTTPDestroyResult(psResult);
4426
0
        return OGRERR_FAILURE;
4427
0
    }
4428
4429
0
    if (psResult->nStatus != 0)
4430
0
    {
4431
0
        CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4432
0
                 psResult->nStatus, psResult->pszErrBuf);
4433
0
        CPLHTTPDestroyResult(psResult);
4434
0
        return OGRERR_FAILURE;
4435
0
    }
4436
4437
0
    const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4438
0
    if (STARTS_WITH_CI(pszData, "http://") ||
4439
0
        STARTS_WITH_CI(pszData, "https://"))
4440
0
    {
4441
0
        CPLError(CE_Failure, CPLE_AppDefined,
4442
0
                 "The data that was downloaded also starts with 'http://' "
4443
0
                 "and cannot be passed into SetFromUserInput.  Is this "
4444
0
                 "really a spatial reference definition? ");
4445
0
        CPLHTTPDestroyResult(psResult);
4446
0
        return OGRERR_FAILURE;
4447
0
    }
4448
0
    if (OGRERR_NONE != SetFromUserInput(pszData))
4449
0
    {
4450
0
        CPLHTTPDestroyResult(psResult);
4451
0
        return OGRERR_FAILURE;
4452
0
    }
4453
4454
0
    CPLHTTPDestroyResult(psResult);
4455
0
    return OGRERR_NONE;
4456
0
}
4457
4458
/************************************************************************/
4459
/*                        OSRimportFromUrl()                            */
4460
/************************************************************************/
4461
4462
/**
4463
 * \brief Set spatial reference from a URL.
4464
 *
4465
 * This function is the same as OGRSpatialReference::importFromUrl()
4466
 */
4467
OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4468
4469
0
{
4470
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4471
4472
0
    return ToPointer(hSRS)->importFromUrl(pszUrl);
4473
0
}
4474
4475
/************************************************************************/
4476
/*                         importFromURNPart()                          */
4477
/************************************************************************/
4478
OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4479
                                              const char *pszCode,
4480
                                              const char *pszURN)
4481
0
{
4482
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4483
0
    (void)this;
4484
0
    (void)pszAuthority;
4485
0
    (void)pszCode;
4486
0
    (void)pszURN;
4487
0
    return OGRERR_FAILURE;
4488
#else
4489
    /* -------------------------------------------------------------------- */
4490
    /*      Is this an EPSG code? Note that we import it with EPSG          */
4491
    /*      preferred axis ordering for geographic coordinate systems.      */
4492
    /* -------------------------------------------------------------------- */
4493
    if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4494
        return importFromEPSGA(atoi(pszCode));
4495
4496
    /* -------------------------------------------------------------------- */
4497
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4498
    /* -------------------------------------------------------------------- */
4499
    if (STARTS_WITH_CI(pszAuthority, "IAU"))
4500
        return importFromDict("IAU2000.wkt", pszCode);
4501
4502
    /* -------------------------------------------------------------------- */
4503
    /*      Is this an OGC code?                                            */
4504
    /* -------------------------------------------------------------------- */
4505
    if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4506
    {
4507
        CPLError(CE_Failure, CPLE_AppDefined,
4508
                 "URN %s has unrecognized authority.", pszURN);
4509
        return OGRERR_FAILURE;
4510
    }
4511
4512
    if (STARTS_WITH_CI(pszCode, "CRS84"))
4513
        return SetWellKnownGeogCS(pszCode);
4514
    else if (STARTS_WITH_CI(pszCode, "CRS83"))
4515
        return SetWellKnownGeogCS(pszCode);
4516
    else if (STARTS_WITH_CI(pszCode, "CRS27"))
4517
        return SetWellKnownGeogCS(pszCode);
4518
    else if (STARTS_WITH_CI(pszCode, "84"))  // urn:ogc:def:crs:OGC:2:84
4519
        return SetWellKnownGeogCS("CRS84");
4520
4521
    /* -------------------------------------------------------------------- */
4522
    /*      Handle auto codes.  We need to convert from format              */
4523
    /*      AUTO42001:99:8888 to format AUTO:42001,99,8888.                 */
4524
    /* -------------------------------------------------------------------- */
4525
    else if (STARTS_WITH_CI(pszCode, "AUTO"))
4526
    {
4527
        char szWMSAuto[100] = {'\0'};
4528
4529
        if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4530
            return OGRERR_FAILURE;
4531
4532
        snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4533
        for (int i = 5; szWMSAuto[i] != '\0'; i++)
4534
        {
4535
            if (szWMSAuto[i] == ':')
4536
                szWMSAuto[i] = ',';
4537
        }
4538
4539
        return importFromWMSAUTO(szWMSAuto);
4540
    }
4541
4542
    /* -------------------------------------------------------------------- */
4543
    /*      Not a recognise OGC item.                                       */
4544
    /* -------------------------------------------------------------------- */
4545
    CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4546
             pszURN);
4547
4548
    return OGRERR_FAILURE;
4549
#endif
4550
0
}
4551
4552
/************************************************************************/
4553
/*                           importFromURN()                            */
4554
/*                                                                      */
4555
/*      See OGC recommendation paper 06-023r1 or later for details.     */
4556
/************************************************************************/
4557
4558
/**
4559
 * \brief Initialize from OGC URN.
4560
 *
4561
 * Initializes this spatial reference from a coordinate system defined
4562
 * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4563
 * paper 06-023r1.  Currently EPSG and OGC authority values are supported,
4564
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4565
 *
4566
 * This method is also support through SetFromUserInput() which can
4567
 * normally be used for URNs.
4568
 *
4569
 * @param pszURN the urn string.
4570
 *
4571
 * @return OGRERR_NONE on success or an error code.
4572
 */
4573
4574
OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4575
4576
0
{
4577
0
    constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4578
0
    if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4579
0
        CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4580
0
            CPL_VALUE_INTEGER)
4581
0
    {
4582
0
        return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4583
0
    }
4584
4585
0
    TAKE_OPTIONAL_LOCK();
4586
4587
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4588
4589
    // PROJ 8.2.0 has support for IAU codes now.
4590
#if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4591
    /* -------------------------------------------------------------------- */
4592
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4593
    /* -------------------------------------------------------------------- */
4594
    const char *pszIAU = strstr(pszURN, "IAU");
4595
    if (pszIAU)
4596
    {
4597
        const char *pszCode = strchr(pszIAU, ':');
4598
        if (pszCode)
4599
        {
4600
            ++pszCode;
4601
            if (*pszCode == ':')
4602
                ++pszCode;
4603
            return importFromDict("IAU2000.wkt", pszCode);
4604
        }
4605
    }
4606
#endif
4607
4608
0
    if (strlen(pszURN) >= 1000)
4609
0
    {
4610
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4611
0
        return OGRERR_CORRUPT_DATA;
4612
0
    }
4613
0
    auto obj = proj_create(d->getPROJContext(), pszURN);
4614
0
    if (!obj)
4615
0
    {
4616
0
        return OGRERR_FAILURE;
4617
0
    }
4618
0
    Clear();
4619
0
    d->setPjCRS(obj);
4620
0
    return OGRERR_NONE;
4621
#else
4622
    const char *pszCur = nullptr;
4623
4624
    if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4625
        pszCur = pszURN + 16;
4626
    else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4627
        pszCur = pszURN + 20;
4628
    else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4629
        pszCur = pszURN + 18;
4630
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4631
        pszCur = pszURN + 16;
4632
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4633
        pszCur = pszURN + 20;
4634
    else
4635
    {
4636
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4637
                 pszURN);
4638
        return OGRERR_FAILURE;
4639
    }
4640
4641
    /* -------------------------------------------------------------------- */
4642
    /*      Clear any existing definition.                                  */
4643
    /* -------------------------------------------------------------------- */
4644
    Clear();
4645
4646
    /* -------------------------------------------------------------------- */
4647
    /*      Find code (ignoring version) out of string like:                */
4648
    /*                                                                      */
4649
    /*      authority:[version]:code                                        */
4650
    /* -------------------------------------------------------------------- */
4651
    const char *pszAuthority = pszCur;
4652
4653
    // skip authority
4654
    while (*pszCur != ':' && *pszCur)
4655
        pszCur++;
4656
    if (*pszCur == ':')
4657
        pszCur++;
4658
4659
    // skip version
4660
    const char *pszBeforeVersion = pszCur;
4661
    while (*pszCur != ':' && *pszCur)
4662
        pszCur++;
4663
    if (*pszCur == ':')
4664
        pszCur++;
4665
    else
4666
        // We come here in the case, the content to parse is authority:code
4667
        // (instead of authority::code) which is probably illegal according to
4668
        // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4669
        // for example in what is returned by GeoServer.
4670
        pszCur = pszBeforeVersion;
4671
4672
    const char *pszCode = pszCur;
4673
4674
    const char *pszComma = strchr(pszCur, ',');
4675
    if (pszComma == nullptr)
4676
        return importFromURNPart(pszAuthority, pszCode, pszURN);
4677
4678
    // There's a second part with the vertical SRS.
4679
    pszCur = pszComma + 1;
4680
    if (!STARTS_WITH(pszCur, "crs:"))
4681
    {
4682
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4683
                 pszURN);
4684
        return OGRERR_FAILURE;
4685
    }
4686
4687
    pszCur += 4;
4688
4689
    char *pszFirstCode = CPLStrdup(pszCode);
4690
    pszFirstCode[pszComma - pszCode] = '\0';
4691
    OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4692
    CPLFree(pszFirstCode);
4693
4694
    // Do we want to turn this into a compound definition
4695
    // with a vertical datum?
4696
    if (eStatus != OGRERR_NONE)
4697
        return eStatus;
4698
4699
    /* -------------------------------------------------------------------- */
4700
    /*      Find code (ignoring version) out of string like:                */
4701
    /*                                                                      */
4702
    /*      authority:[version]:code                                        */
4703
    /* -------------------------------------------------------------------- */
4704
    pszAuthority = pszCur;
4705
4706
    // skip authority
4707
    while (*pszCur != ':' && *pszCur)
4708
        pszCur++;
4709
    if (*pszCur == ':')
4710
        pszCur++;
4711
4712
    // skip version
4713
    pszBeforeVersion = pszCur;
4714
    while (*pszCur != ':' && *pszCur)
4715
        pszCur++;
4716
    if (*pszCur == ':')
4717
        pszCur++;
4718
    else
4719
        pszCur = pszBeforeVersion;
4720
4721
    pszCode = pszCur;
4722
4723
    OGRSpatialReference oVertSRS;
4724
    eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4725
    if (eStatus == OGRERR_NONE)
4726
    {
4727
        OGRSpatialReference oHorizSRS(*this);
4728
4729
        Clear();
4730
4731
        oHorizSRS.d->refreshProjObj();
4732
        oVertSRS.d->refreshProjObj();
4733
        if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4734
            return OGRERR_FAILURE;
4735
4736
        const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4737
        const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4738
4739
        CPLString osName = pszHorizName ? pszHorizName : "";
4740
        osName += " + ";
4741
        osName += pszVertName ? pszVertName : "";
4742
4743
        SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4744
    }
4745
4746
    return eStatus;
4747
#endif
4748
0
}
4749
4750
/************************************************************************/
4751
/*                           importFromCRSURL()                         */
4752
/*                                                                      */
4753
/*      See OGC Best Practice document 11-135 for details.              */
4754
/************************************************************************/
4755
4756
/**
4757
 * \brief Initialize from OGC URL.
4758
 *
4759
 * Initializes this spatial reference from a coordinate system defined
4760
 * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4761
 * paper 11-135.  Currently EPSG and OGC authority values are supported,
4762
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4763
 *
4764
 * This method is also supported through SetFromUserInput() which can
4765
 * normally be used for URLs.
4766
 *
4767
 * @param pszURL the URL string.
4768
 *
4769
 * @return OGRERR_NONE on success or an error code.
4770
 */
4771
4772
OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4773
4774
0
{
4775
0
    TAKE_OPTIONAL_LOCK();
4776
4777
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4778
0
    if (strlen(pszURL) >= 10000)
4779
0
    {
4780
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4781
0
        return OGRERR_CORRUPT_DATA;
4782
0
    }
4783
4784
0
    PJ *obj;
4785
#if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4786
    if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4787
    {
4788
        obj = proj_create(
4789
            d->getPROJContext(),
4790
            CPLSPrintf("IAU:%s",
4791
                       pszURL +
4792
                           strlen("http://www.opengis.net/def/crs/IAU/0/")));
4793
    }
4794
    else
4795
#endif
4796
0
    {
4797
0
        obj = proj_create(d->getPROJContext(), pszURL);
4798
0
    }
4799
0
    if (!obj)
4800
0
    {
4801
0
        return OGRERR_FAILURE;
4802
0
    }
4803
0
    Clear();
4804
0
    d->setPjCRS(obj);
4805
0
    return OGRERR_NONE;
4806
#else
4807
    const char *pszCur = nullptr;
4808
4809
    if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4810
        pszCur = pszURL + 26;
4811
    else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4812
        pszCur = pszURL + 27;
4813
    else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4814
        pszCur = pszURL + 30;
4815
    else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4816
        pszCur = pszURL + 31;
4817
    else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4818
        pszCur = pszURL + 23;
4819
    else
4820
    {
4821
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4822
                 pszURL);
4823
        return OGRERR_FAILURE;
4824
    }
4825
4826
    if (*pszCur == '\0')
4827
    {
4828
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4829
        return OGRERR_FAILURE;
4830
    }
4831
4832
    /* -------------------------------------------------------------------- */
4833
    /*      Clear any existing definition.                                  */
4834
    /* -------------------------------------------------------------------- */
4835
    Clear();
4836
4837
    if (STARTS_WITH_CI(pszCur, "-compound?1="))
4838
    {
4839
        /* --------------------------------------------------------------------
4840
         */
4841
        /*      It's a compound CRS, of the form: */
4842
        /*                                                                      */
4843
        /*      http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4844
        /* --------------------------------------------------------------------
4845
         */
4846
        pszCur += 12;
4847
4848
        // Extract each component CRS URL.
4849
        int iComponentUrl = 2;
4850
4851
        CPLString osName = "";
4852
        Clear();
4853
4854
        while (iComponentUrl != -1)
4855
        {
4856
            char searchStr[15] = {};
4857
            snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4858
4859
            const char *pszUrlEnd = strstr(pszCur, searchStr);
4860
4861
            // Figure out the next component URL.
4862
            char *pszComponentUrl = nullptr;
4863
4864
            if (pszUrlEnd)
4865
            {
4866
                size_t nLen = pszUrlEnd - pszCur;
4867
                pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4868
                strncpy(pszComponentUrl, pszCur, nLen);
4869
                pszComponentUrl[nLen] = '\0';
4870
4871
                ++iComponentUrl;
4872
                pszCur += nLen + strlen(searchStr);
4873
            }
4874
            else
4875
            {
4876
                if (iComponentUrl == 2)
4877
                {
4878
                    CPLError(CE_Failure, CPLE_AppDefined,
4879
                             "Compound CRS URLs must have at least two "
4880
                             "component CRSs.");
4881
                    return OGRERR_FAILURE;
4882
                }
4883
                else
4884
                {
4885
                    pszComponentUrl = CPLStrdup(pszCur);
4886
                    // no more components
4887
                    iComponentUrl = -1;
4888
                }
4889
            }
4890
4891
            OGRSpatialReference oComponentSRS;
4892
            OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
4893
4894
            CPLFree(pszComponentUrl);
4895
            pszComponentUrl = nullptr;
4896
4897
            if (eStatus == OGRERR_NONE)
4898
            {
4899
                if (osName.length() != 0)
4900
                {
4901
                    osName += " + ";
4902
                }
4903
                osName += oComponentSRS.GetRoot()->GetValue();
4904
                SetNode("COMPD_CS", osName);
4905
                GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
4906
            }
4907
            else
4908
                return eStatus;
4909
        }
4910
4911
        return OGRERR_NONE;
4912
    }
4913
4914
    /* -------------------------------------------------------------------- */
4915
    /*      It's a normal CRS URL, of the form:                             */
4916
    /*                                                                      */
4917
    /*      http://opengis.net/def/crs/AUTHORITY/VERSION/CODE               */
4918
    /* -------------------------------------------------------------------- */
4919
    ++pszCur;
4920
    const char *pszAuthority = pszCur;
4921
4922
    // skip authority
4923
    while (*pszCur != '/' && *pszCur)
4924
        pszCur++;
4925
    if (*pszCur == '/')
4926
        pszCur++;
4927
4928
    // skip version
4929
    while (*pszCur != '/' && *pszCur)
4930
        pszCur++;
4931
    if (*pszCur == '/')
4932
        pszCur++;
4933
4934
    const char *pszCode = pszCur;
4935
4936
    return importFromURNPart(pszAuthority, pszCode, pszURL);
4937
#endif
4938
0
}
4939
4940
/************************************************************************/
4941
/*                         importFromWMSAUTO()                          */
4942
/************************************************************************/
4943
4944
/**
4945
 * \brief Initialize from WMSAUTO string.
4946
 *
4947
 * Note that the WMS 1.3 specification does not include the
4948
 * units code, while apparently earlier specs do.  We try to
4949
 * guess around this.
4950
 *
4951
 * @param pszDefinition the WMSAUTO string
4952
 *
4953
 * @return OGRERR_NONE on success or an error code.
4954
 */
4955
OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
4956
4957
0
{
4958
0
    TAKE_OPTIONAL_LOCK();
4959
4960
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4961
0
    if (strlen(pszDefinition) >= 10000)
4962
0
    {
4963
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4964
0
        return OGRERR_CORRUPT_DATA;
4965
0
    }
4966
4967
0
    auto obj = proj_create(d->getPROJContext(), pszDefinition);
4968
0
    if (!obj)
4969
0
    {
4970
0
        return OGRERR_FAILURE;
4971
0
    }
4972
0
    Clear();
4973
0
    d->setPjCRS(obj);
4974
0
    return OGRERR_NONE;
4975
#else
4976
    int nProjId, nUnitsId;
4977
    double dfRefLong, dfRefLat = 0.0;
4978
4979
    /* -------------------------------------------------------------------- */
4980
    /*      Tokenize                                                        */
4981
    /* -------------------------------------------------------------------- */
4982
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4983
        pszDefinition += 5;
4984
4985
    char **papszTokens =
4986
        CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
4987
4988
    if (CSLCount(papszTokens) == 4)
4989
    {
4990
        nProjId = atoi(papszTokens[0]);
4991
        nUnitsId = atoi(papszTokens[1]);
4992
        dfRefLong = CPLAtof(papszTokens[2]);
4993
        dfRefLat = CPLAtof(papszTokens[3]);
4994
    }
4995
    else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
4996
    {
4997
        nProjId = atoi(papszTokens[0]);
4998
        nUnitsId = atoi(papszTokens[1]);
4999
        dfRefLong = CPLAtof(papszTokens[2]);
5000
        dfRefLat = 0.0;
5001
    }
5002
    else if (CSLCount(papszTokens) == 3)
5003
    {
5004
        nProjId = atoi(papszTokens[0]);
5005
        nUnitsId = 9001;
5006
        dfRefLong = CPLAtof(papszTokens[1]);
5007
        dfRefLat = CPLAtof(papszTokens[2]);
5008
    }
5009
    else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5010
    {
5011
        nProjId = atoi(papszTokens[0]);
5012
        nUnitsId = 9001;
5013
        dfRefLong = CPLAtof(papszTokens[1]);
5014
    }
5015
    else
5016
    {
5017
        CSLDestroy(papszTokens);
5018
        CPLError(CE_Failure, CPLE_AppDefined,
5019
                 "AUTO projection has wrong number of arguments, expected\n"
5020
                 "AUTO:proj_id,units_id,ref_long,ref_lat or"
5021
                 "AUTO:proj_id,ref_long,ref_lat");
5022
        return OGRERR_FAILURE;
5023
    }
5024
5025
    CSLDestroy(papszTokens);
5026
    papszTokens = nullptr;
5027
5028
    /* -------------------------------------------------------------------- */
5029
    /*      Build coordsys.                                                 */
5030
    /* -------------------------------------------------------------------- */
5031
    Clear();
5032
5033
    /* -------------------------------------------------------------------- */
5034
    /*      Set WGS84.                                                      */
5035
    /* -------------------------------------------------------------------- */
5036
    SetWellKnownGeogCS("WGS84");
5037
5038
    switch (nProjId)
5039
    {
5040
        case 42001:  // Auto UTM
5041
            SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5042
                   dfRefLat >= 0.0);
5043
            break;
5044
5045
        case 42002:  // Auto TM (strangely very UTM-like).
5046
            SetTM(0, dfRefLong, 0.9996, 500000.0,
5047
                  (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5048
            break;
5049
5050
        case 42003:  // Auto Orthographic.
5051
            SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5052
            break;
5053
5054
        case 42004:  // Auto Equirectangular
5055
            SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5056
            break;
5057
5058
        case 42005:
5059
            SetMollweide(dfRefLong, 0.0, 0.0);
5060
            break;
5061
5062
        default:
5063
            CPLError(CE_Failure, CPLE_AppDefined,
5064
                     "Unsupported projection id in importFromWMSAUTO(): %d",
5065
                     nProjId);
5066
            return OGRERR_FAILURE;
5067
    }
5068
5069
    /* -------------------------------------------------------------------- */
5070
    /*      Set units.                                                      */
5071
    /* -------------------------------------------------------------------- */
5072
5073
    switch (nUnitsId)
5074
    {
5075
        case 9001:
5076
            SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5077
            break;
5078
5079
        case 9002:
5080
            SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5081
            break;
5082
5083
        case 9003:
5084
            SetTargetLinearUnits(nullptr, "US survey foot",
5085
                                 CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5086
            break;
5087
5088
        default:
5089
            CPLError(CE_Failure, CPLE_AppDefined,
5090
                     "Unsupported units code (%d).", nUnitsId);
5091
            return OGRERR_FAILURE;
5092
            break;
5093
    }
5094
5095
    return OGRERR_NONE;
5096
#endif
5097
0
}
5098
5099
/************************************************************************/
5100
/*                            GetSemiMajor()                            */
5101
/************************************************************************/
5102
5103
/**
5104
 * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5105
 *
5106
 * This method does the same thing as the C function OSRGetSemiMajor().
5107
 *
5108
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5109
 * can be found.
5110
 *
5111
 * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5112
 */
5113
5114
double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5115
5116
0
{
5117
0
    TAKE_OPTIONAL_LOCK();
5118
5119
0
    if (pnErr != nullptr)
5120
0
        *pnErr = OGRERR_FAILURE;
5121
5122
0
    d->refreshProjObj();
5123
0
    if (!d->m_pj_crs)
5124
0
        return SRS_WGS84_SEMIMAJOR;
5125
5126
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5127
0
    if (!ellps)
5128
0
        return SRS_WGS84_SEMIMAJOR;
5129
5130
0
    double dfSemiMajor = 0.0;
5131
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5132
0
                                  nullptr, nullptr, nullptr);
5133
0
    proj_destroy(ellps);
5134
5135
0
    if (dfSemiMajor > 0)
5136
0
    {
5137
0
        if (pnErr != nullptr)
5138
0
            *pnErr = OGRERR_NONE;
5139
0
        return dfSemiMajor;
5140
0
    }
5141
5142
0
    return SRS_WGS84_SEMIMAJOR;
5143
0
}
5144
5145
/************************************************************************/
5146
/*                          OSRGetSemiMajor()                           */
5147
/************************************************************************/
5148
5149
/**
5150
 * \brief Get spheroid semi major axis.
5151
 *
5152
 * This function is the same as OGRSpatialReference::GetSemiMajor()
5153
 */
5154
double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5155
5156
0
{
5157
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5158
5159
0
    return ToPointer(hSRS)->GetSemiMajor(pnErr);
5160
0
}
5161
5162
/************************************************************************/
5163
/*                          GetInvFlattening()                          */
5164
/************************************************************************/
5165
5166
/**
5167
 * \brief Get spheroid inverse flattening.
5168
 *
5169
 * This method does the same thing as the C function OSRGetInvFlattening().
5170
 *
5171
 * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5172
 * can be found.
5173
 *
5174
 * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5175
 */
5176
5177
double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5178
5179
0
{
5180
0
    TAKE_OPTIONAL_LOCK();
5181
5182
0
    if (pnErr != nullptr)
5183
0
        *pnErr = OGRERR_FAILURE;
5184
5185
0
    d->refreshProjObj();
5186
0
    if (!d->m_pj_crs)
5187
0
        return SRS_WGS84_INVFLATTENING;
5188
5189
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5190
0
    if (!ellps)
5191
0
        return SRS_WGS84_INVFLATTENING;
5192
5193
0
    double dfInvFlattening = -1.0;
5194
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5195
0
                                  nullptr, &dfInvFlattening);
5196
0
    proj_destroy(ellps);
5197
5198
0
    if (dfInvFlattening >= 0.0)
5199
0
    {
5200
0
        if (pnErr != nullptr)
5201
0
            *pnErr = OGRERR_NONE;
5202
0
        return dfInvFlattening;
5203
0
    }
5204
5205
0
    return SRS_WGS84_INVFLATTENING;
5206
0
}
5207
5208
/************************************************************************/
5209
/*                        OSRGetInvFlattening()                         */
5210
/************************************************************************/
5211
5212
/**
5213
 * \brief Get spheroid inverse flattening.
5214
 *
5215
 * This function is the same as OGRSpatialReference::GetInvFlattening()
5216
 */
5217
double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5218
5219
0
{
5220
0
    VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5221
5222
0
    return ToPointer(hSRS)->GetInvFlattening(pnErr);
5223
0
}
5224
5225
/************************************************************************/
5226
/*                           GetEccentricity()                          */
5227
/************************************************************************/
5228
5229
/**
5230
 * \brief Get spheroid eccentricity
5231
 *
5232
 * @return eccentricity (or -1 in case of error)
5233
 * @since GDAL 2.3
5234
 */
5235
5236
double OGRSpatialReference::GetEccentricity() const
5237
5238
0
{
5239
0
    OGRErr eErr = OGRERR_NONE;
5240
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5241
0
    if (eErr != OGRERR_NONE)
5242
0
    {
5243
0
        return -1.0;
5244
0
    }
5245
0
    if (dfInvFlattening == 0.0)
5246
0
        return 0.0;
5247
0
    if (dfInvFlattening < 0.5)
5248
0
        return -1.0;
5249
0
    return sqrt(2.0 / dfInvFlattening -
5250
0
                1.0 / (dfInvFlattening * dfInvFlattening));
5251
0
}
5252
5253
/************************************************************************/
5254
/*                      GetSquaredEccentricity()                        */
5255
/************************************************************************/
5256
5257
/**
5258
 * \brief Get spheroid squared eccentricity
5259
 *
5260
 * @return squared eccentricity (or -1 in case of error)
5261
 * @since GDAL 2.3
5262
 */
5263
5264
double OGRSpatialReference::GetSquaredEccentricity() const
5265
5266
0
{
5267
0
    OGRErr eErr = OGRERR_NONE;
5268
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5269
0
    if (eErr != OGRERR_NONE)
5270
0
    {
5271
0
        return -1.0;
5272
0
    }
5273
0
    if (dfInvFlattening == 0.0)
5274
0
        return 0.0;
5275
0
    if (dfInvFlattening < 0.5)
5276
0
        return -1.0;
5277
0
    return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5278
0
}
5279
5280
/************************************************************************/
5281
/*                            GetSemiMinor()                            */
5282
/************************************************************************/
5283
5284
/**
5285
 * \brief Get spheroid semi minor axis.
5286
 *
5287
 * This method does the same thing as the C function OSRGetSemiMinor().
5288
 *
5289
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5290
 * can be found.
5291
 *
5292
 * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5293
 */
5294
5295
double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5296
5297
0
{
5298
0
    const double dfSemiMajor = GetSemiMajor(pnErr);
5299
0
    const double dfInvFlattening = GetInvFlattening(pnErr);
5300
5301
0
    return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5302
0
}
5303
5304
/************************************************************************/
5305
/*                          OSRGetSemiMinor()                           */
5306
/************************************************************************/
5307
5308
/**
5309
 * \brief Get spheroid semi minor axis.
5310
 *
5311
 * This function is the same as OGRSpatialReference::GetSemiMinor()
5312
 */
5313
double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5314
5315
0
{
5316
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5317
5318
0
    return ToPointer(hSRS)->GetSemiMinor(pnErr);
5319
0
}
5320
5321
/************************************************************************/
5322
/*                             SetLocalCS()                             */
5323
/************************************************************************/
5324
5325
/**
5326
 * \brief Set the user visible LOCAL_CS name.
5327
 *
5328
 * This method is the same as the C function OSRSetLocalCS().
5329
 *
5330
 * This method will ensure a LOCAL_CS node is created as the root,
5331
 * and set the provided name on it.  It must be used before SetLinearUnits().
5332
 *
5333
 * @param pszName the user visible name to assign.  Not used as a key.
5334
 *
5335
 * @return OGRERR_NONE on success.
5336
 */
5337
5338
OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5339
5340
8.50k
{
5341
8.50k
    TAKE_OPTIONAL_LOCK();
5342
5343
8.50k
    if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5344
8.50k
        d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5345
8.50k
    {
5346
8.50k
        d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5347
8.50k
    }
5348
0
    else
5349
0
    {
5350
0
        CPLDebug("OGR",
5351
0
                 "OGRSpatialReference::SetLocalCS(%s) failed.  "
5352
0
                 "It appears an incompatible object already exists.",
5353
0
                 pszName);
5354
0
        return OGRERR_FAILURE;
5355
0
    }
5356
5357
8.50k
    return OGRERR_NONE;
5358
8.50k
}
5359
5360
/************************************************************************/
5361
/*                           OSRSetLocalCS()                            */
5362
/************************************************************************/
5363
5364
/**
5365
 * \brief Set the user visible LOCAL_CS name.
5366
 *
5367
 * This function is the same as OGRSpatialReference::SetLocalCS()
5368
 */
5369
OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5370
5371
0
{
5372
0
    VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5373
5374
0
    return ToPointer(hSRS)->SetLocalCS(pszName);
5375
0
}
5376
5377
/************************************************************************/
5378
/*                             SetGeocCS()                              */
5379
/************************************************************************/
5380
5381
/**
5382
 * \brief Set the user visible GEOCCS name.
5383
 *
5384
 * This method is the same as the C function OSRSetGeocCS().
5385
5386
 * This method will ensure a GEOCCS node is created as the root,
5387
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5388
 * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5389
 * the GEOGCS.
5390
 *
5391
 * @param pszName the user visible name to assign.  Not used as a key.
5392
 *
5393
 * @return OGRERR_NONE on success.
5394
 *
5395
 * @since OGR 1.9.0
5396
 */
5397
5398
OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5399
5400
3.08k
{
5401
3.08k
    TAKE_OPTIONAL_LOCK();
5402
5403
3.08k
    OGRErr eErr = OGRERR_NONE;
5404
3.08k
    d->refreshProjObj();
5405
3.08k
    d->demoteFromBoundCRS();
5406
3.08k
    if (d->m_pjType == PJ_TYPE_UNKNOWN)
5407
3.08k
    {
5408
3.08k
        d->setPjCRS(proj_create_geocentric_crs(
5409
3.08k
            d->getPROJContext(), pszName, "World Geodetic System 1984",
5410
3.08k
            "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5411
3.08k
            SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5412
3.08k
            "Metre", 1.0));
5413
3.08k
    }
5414
0
    else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5415
0
    {
5416
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5417
0
    }
5418
0
    else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5419
0
             d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5420
0
    {
5421
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5422
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
5423
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5424
0
        if (datum == nullptr)
5425
0
        {
5426
0
            datum =
5427
0
                proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5428
0
        }
5429
0
#endif
5430
0
        if (datum == nullptr)
5431
0
        {
5432
0
            d->undoDemoteFromBoundCRS();
5433
0
            return OGRERR_FAILURE;
5434
0
        }
5435
5436
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
5437
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5438
0
            0.0);
5439
0
        d->setPjCRS(pj_crs);
5440
5441
0
        proj_destroy(datum);
5442
0
    }
5443
0
    else
5444
0
    {
5445
0
        CPLDebug("OGR",
5446
0
                 "OGRSpatialReference::SetGeocCS(%s) failed.  "
5447
0
                 "It appears an incompatible object already exists.",
5448
0
                 pszName);
5449
0
        eErr = OGRERR_FAILURE;
5450
0
    }
5451
3.08k
    d->undoDemoteFromBoundCRS();
5452
5453
3.08k
    return eErr;
5454
3.08k
}
5455
5456
/************************************************************************/
5457
/*                            OSRSetGeocCS()                            */
5458
/************************************************************************/
5459
5460
/**
5461
 * \brief Set the user visible PROJCS name.
5462
 *
5463
 * This function is the same as OGRSpatialReference::SetGeocCS()
5464
 *
5465
 * @since OGR 1.9.0
5466
 */
5467
OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5468
5469
0
{
5470
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5471
5472
0
    return ToPointer(hSRS)->SetGeocCS(pszName);
5473
0
}
5474
5475
/************************************************************************/
5476
/*                             SetVertCS()                              */
5477
/************************************************************************/
5478
5479
/**
5480
 * \brief Set the user visible VERT_CS name.
5481
 *
5482
 * This method is the same as the C function OSRSetVertCS().
5483
5484
 * This method will ensure a VERT_CS node is created if needed.  If the
5485
 * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5486
 * turned into a COMPD_CS.
5487
 *
5488
 * @param pszVertCSName the user visible name of the vertical coordinate
5489
 * system. Not used as a key.
5490
 *
5491
 * @param pszVertDatumName the user visible name of the vertical datum.  It
5492
 * is helpful if this matches the EPSG name.
5493
 *
5494
 * @param nVertDatumType the OGC vertical datum type. Ignored
5495
 *
5496
 * @return OGRERR_NONE on success.
5497
 *
5498
 * @since OGR 1.9.0
5499
 */
5500
5501
OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5502
                                      const char *pszVertDatumName,
5503
                                      int nVertDatumType)
5504
5505
0
{
5506
0
    TAKE_OPTIONAL_LOCK();
5507
5508
0
    CPL_IGNORE_RET_VAL(nVertDatumType);
5509
5510
0
    d->refreshProjObj();
5511
5512
0
    auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5513
0
                                            pszVertDatumName, nullptr, 0.0);
5514
5515
    /* -------------------------------------------------------------------- */
5516
    /*      Handle the case where we want to make a compound coordinate     */
5517
    /*      system.                                                         */
5518
    /* -------------------------------------------------------------------- */
5519
0
    if (IsProjected() || IsGeographic())
5520
0
    {
5521
0
        auto compoundCRS = proj_create_compound_crs(
5522
0
            d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5523
0
        proj_destroy(vertCRS);
5524
0
        d->setPjCRS(compoundCRS);
5525
0
    }
5526
0
    else
5527
0
    {
5528
0
        d->setPjCRS(vertCRS);
5529
0
    }
5530
0
    return OGRERR_NONE;
5531
0
}
5532
5533
/************************************************************************/
5534
/*                            OSRSetVertCS()                            */
5535
/************************************************************************/
5536
5537
/**
5538
 * \brief Setup the vertical coordinate system.
5539
 *
5540
 * This function is the same as OGRSpatialReference::SetVertCS()
5541
 *
5542
 * @since OGR 1.9.0
5543
 */
5544
OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5545
                    const char *pszVertDatumName, int nVertDatumType)
5546
5547
0
{
5548
0
    VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5549
5550
0
    return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5551
0
                                      nVertDatumType);
5552
0
}
5553
5554
/************************************************************************/
5555
/*                           SetCompoundCS()                            */
5556
/************************************************************************/
5557
5558
/**
5559
 * \brief Setup a compound coordinate system.
5560
 *
5561
 * This method is the same as the C function OSRSetCompoundCS().
5562
5563
 * This method is replace the current SRS with a COMPD_CS coordinate system
5564
 * consisting of the passed in horizontal and vertical coordinate systems.
5565
 *
5566
 * @param pszName the name of the compound coordinate system.
5567
 *
5568
 * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5569
 *
5570
 * @param poVertSRS the vertical SRS (VERT_CS).
5571
 *
5572
 * @return OGRERR_NONE on success.
5573
 */
5574
5575
OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5576
                                          const OGRSpatialReference *poHorizSRS,
5577
                                          const OGRSpatialReference *poVertSRS)
5578
5579
0
{
5580
0
    TAKE_OPTIONAL_LOCK();
5581
5582
    /* -------------------------------------------------------------------- */
5583
    /*      Verify these are legal horizontal and vertical coordinate       */
5584
    /*      systems.                                                        */
5585
    /* -------------------------------------------------------------------- */
5586
0
    if (!poVertSRS->IsVertical())
5587
0
    {
5588
0
        CPLError(CE_Failure, CPLE_AppDefined,
5589
0
                 "SetCompoundCS() fails, vertical component is not VERT_CS.");
5590
0
        return OGRERR_FAILURE;
5591
0
    }
5592
0
    if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5593
0
    {
5594
0
        CPLError(CE_Failure, CPLE_AppDefined,
5595
0
                 "SetCompoundCS() fails, horizontal component is not PROJCS or "
5596
0
                 "GEOGCS.");
5597
0
        return OGRERR_FAILURE;
5598
0
    }
5599
5600
    /* -------------------------------------------------------------------- */
5601
    /*      Replace with compound srs.                                      */
5602
    /* -------------------------------------------------------------------- */
5603
0
    Clear();
5604
5605
0
    auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5606
0
                                                poHorizSRS->d->m_pj_crs,
5607
0
                                                poVertSRS->d->m_pj_crs);
5608
0
    d->setPjCRS(compoundCRS);
5609
5610
0
    return OGRERR_NONE;
5611
0
}
5612
5613
/************************************************************************/
5614
/*                          OSRSetCompoundCS()                          */
5615
/************************************************************************/
5616
5617
/**
5618
 * \brief Setup a compound coordinate system.
5619
 *
5620
 * This function is the same as OGRSpatialReference::SetCompoundCS()
5621
 */
5622
OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5623
                        OGRSpatialReferenceH hHorizSRS,
5624
                        OGRSpatialReferenceH hVertSRS)
5625
5626
0
{
5627
0
    VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5628
0
    VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5629
0
    VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5630
5631
0
    return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5632
0
                                          ToPointer(hVertSRS));
5633
0
}
5634
5635
/************************************************************************/
5636
/*                             SetProjCS()                              */
5637
/************************************************************************/
5638
5639
/**
5640
 * \brief Set the user visible PROJCS name.
5641
 *
5642
 * This method is the same as the C function OSRSetProjCS().
5643
 *
5644
 * This method will ensure a PROJCS node is created as the root,
5645
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5646
 * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5647
 *
5648
 * @param pszName the user visible name to assign.  Not used as a key.
5649
 *
5650
 * @return OGRERR_NONE on success.
5651
 */
5652
5653
OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5654
5655
20.3k
{
5656
20.3k
    TAKE_OPTIONAL_LOCK();
5657
5658
20.3k
    d->refreshProjObj();
5659
20.3k
    d->demoteFromBoundCRS();
5660
20.3k
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5661
205
    {
5662
205
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5663
205
    }
5664
20.1k
    else
5665
20.1k
    {
5666
20.1k
        auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5667
20.1k
                                                nullptr, nullptr, nullptr,
5668
20.1k
                                                nullptr, nullptr, 0, nullptr);
5669
20.1k
        auto cs = proj_create_cartesian_2D_cs(
5670
20.1k
            d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5671
5672
20.1k
        auto projCRS = proj_create_projected_crs(
5673
20.1k
            d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5674
20.1k
        proj_destroy(dummyConv);
5675
20.1k
        proj_destroy(cs);
5676
5677
20.1k
        d->setPjCRS(projCRS);
5678
20.1k
    }
5679
20.3k
    d->undoDemoteFromBoundCRS();
5680
20.3k
    return OGRERR_NONE;
5681
20.3k
}
5682
5683
/************************************************************************/
5684
/*                            OSRSetProjCS()                            */
5685
/************************************************************************/
5686
5687
/**
5688
 * \brief Set the user visible PROJCS name.
5689
 *
5690
 * This function is the same as OGRSpatialReference::SetProjCS()
5691
 */
5692
OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5693
5694
0
{
5695
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5696
5697
0
    return ToPointer(hSRS)->SetProjCS(pszName);
5698
0
}
5699
5700
/************************************************************************/
5701
/*                           SetProjection()                            */
5702
/************************************************************************/
5703
5704
/**
5705
 * \brief Set a projection name.
5706
 *
5707
 * This method is the same as the C function OSRSetProjection().
5708
 *
5709
 * @param pszProjection the projection name, which should be selected from
5710
 * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5711
 *
5712
 * @return OGRERR_NONE on success.
5713
 */
5714
5715
OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5716
5717
1.56k
{
5718
1.56k
    TAKE_OPTIONAL_LOCK();
5719
5720
1.56k
    OGR_SRSNode *poGeogCS = nullptr;
5721
5722
1.56k
    if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5723
0
    {
5724
0
        poGeogCS = d->m_poRoot;
5725
0
        d->m_poRoot = nullptr;
5726
0
    }
5727
5728
1.56k
    if (!GetAttrNode("PROJCS"))
5729
1
    {
5730
1
        SetNode("PROJCS", "unnamed");
5731
1
    }
5732
5733
1.56k
    const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5734
1.56k
    if (eErr != OGRERR_NONE)
5735
0
        return eErr;
5736
5737
1.56k
    if (poGeogCS != nullptr)
5738
0
        d->m_poRoot->InsertChild(poGeogCS, 1);
5739
5740
1.56k
    return OGRERR_NONE;
5741
1.56k
}
5742
5743
/************************************************************************/
5744
/*                            OSRSetProjection()                        */
5745
/************************************************************************/
5746
5747
/**
5748
 * \brief Set a projection name.
5749
 *
5750
 * This function is the same as OGRSpatialReference::SetProjection()
5751
 */
5752
OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5753
5754
0
{
5755
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5756
5757
0
    return ToPointer(hSRS)->SetProjection(pszProjection);
5758
0
}
5759
5760
/************************************************************************/
5761
/*                      GetWKT2ProjectionMethod()                       */
5762
/************************************************************************/
5763
5764
/**
5765
 * \brief Returns info on the projection method, based on WKT2 naming
5766
 * conventions.
5767
 *
5768
 * The returned strings are short lived and should be considered to be
5769
 * invalidated by any further call to the GDAL API.
5770
 *
5771
 * @param[out] ppszMethodName Pointer to a string that will receive the
5772
 * projection method name.
5773
 * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5774
 * receive the name of the authority that defines the projection method.
5775
 * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5776
 * an authority.
5777
 * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5778
 * receive the code that defines the projection method.
5779
 * *ppszMethodCode may be nullptr if the projection method is not linked to
5780
 * an authority.
5781
 *
5782
 * @return OGRERR_NONE on success.
5783
 */
5784
OGRErr
5785
OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5786
                                             const char **ppszMethodAuthName,
5787
                                             const char **ppszMethodCode) const
5788
0
{
5789
0
    TAKE_OPTIONAL_LOCK();
5790
5791
0
    auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5792
0
    if (!conv)
5793
0
        return OGRERR_FAILURE;
5794
0
    const char *pszTmpMethodName = "";
5795
0
    const char *pszTmpMethodAuthName = "";
5796
0
    const char *pszTmpMethodCode = "";
5797
0
    int ret = proj_coordoperation_get_method_info(
5798
0
        d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5799
0
        &pszTmpMethodCode);
5800
    // "Internalize" temporary strings returned by PROJ
5801
0
    CPLAssert(pszTmpMethodName);
5802
0
    if (ppszMethodName)
5803
0
        *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5804
0
    if (ppszMethodAuthName)
5805
0
        *ppszMethodAuthName = pszTmpMethodAuthName
5806
0
                                  ? CPLSPrintf("%s", pszTmpMethodAuthName)
5807
0
                                  : nullptr;
5808
0
    if (ppszMethodCode)
5809
0
        *ppszMethodCode =
5810
0
            pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5811
0
    proj_destroy(conv);
5812
0
    return ret ? OGRERR_NONE : OGRERR_FAILURE;
5813
0
}
5814
5815
/************************************************************************/
5816
/*                            SetProjParm()                             */
5817
/************************************************************************/
5818
5819
/**
5820
 * \brief Set a projection parameter value.
5821
 *
5822
 * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5823
 *
5824
 * This method is the same as the C function OSRSetProjParm().
5825
 *
5826
 * Please check https://gdal.org/proj_list pages for
5827
 * legal parameter names for specific projections.
5828
 *
5829
 *
5830
 * @param pszParamName the parameter name, which should be selected from
5831
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5832
 *
5833
 * @param dfValue value to assign.
5834
 *
5835
 * @return OGRERR_NONE on success.
5836
 */
5837
5838
OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5839
                                        double dfValue)
5840
5841
0
{
5842
0
    TAKE_OPTIONAL_LOCK();
5843
5844
0
    OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5845
5846
0
    if (poPROJCS == nullptr)
5847
0
        return OGRERR_FAILURE;
5848
5849
0
    char szValue[64] = {'\0'};
5850
0
    OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5851
5852
    /* -------------------------------------------------------------------- */
5853
    /*      Try to find existing parameter with this name.                  */
5854
    /* -------------------------------------------------------------------- */
5855
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5856
0
    {
5857
0
        OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5858
5859
0
        if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5860
0
            poParam->GetChildCount() == 2 &&
5861
0
            EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5862
0
        {
5863
0
            poParam->GetChild(1)->SetValue(szValue);
5864
0
            return OGRERR_NONE;
5865
0
        }
5866
0
    }
5867
5868
    /* -------------------------------------------------------------------- */
5869
    /*      Otherwise create a new parameter and append.                    */
5870
    /* -------------------------------------------------------------------- */
5871
0
    OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5872
0
    poParam->AddChild(new OGR_SRSNode(pszParamName));
5873
0
    poParam->AddChild(new OGR_SRSNode(szValue));
5874
5875
0
    poPROJCS->AddChild(poParam);
5876
5877
0
    return OGRERR_NONE;
5878
0
}
5879
5880
/************************************************************************/
5881
/*                           OSRSetProjParm()                           */
5882
/************************************************************************/
5883
5884
/**
5885
 * \brief Set a projection parameter value.
5886
 *
5887
 * This function is the same as OGRSpatialReference::SetProjParm()
5888
 */
5889
OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5890
                      double dfValue)
5891
5892
0
{
5893
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
5894
5895
0
    return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
5896
0
}
5897
5898
/************************************************************************/
5899
/*                            FindProjParm()                            */
5900
/************************************************************************/
5901
5902
/**
5903
 * \brief Return the child index of the named projection parameter on
5904
 * its parent PROJCS node.
5905
 *
5906
 * @param pszParameter projection parameter to look for
5907
 * @param poPROJCS projection CS node to look in. If NULL is passed,
5908
 *        the PROJCS node of the SpatialReference object will be searched.
5909
 *
5910
 * @return the child index of the named projection parameter. -1 on failure
5911
 */
5912
int OGRSpatialReference::FindProjParm(const char *pszParameter,
5913
                                      const OGR_SRSNode *poPROJCS) const
5914
5915
0
{
5916
0
    TAKE_OPTIONAL_LOCK();
5917
5918
0
    if (poPROJCS == nullptr)
5919
0
        poPROJCS = GetAttrNode("PROJCS");
5920
5921
0
    if (poPROJCS == nullptr)
5922
0
        return -1;
5923
5924
    /* -------------------------------------------------------------------- */
5925
    /*      Search for requested parameter.                                 */
5926
    /* -------------------------------------------------------------------- */
5927
0
    bool bIsWKT2 = false;
5928
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5929
0
    {
5930
0
        const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5931
5932
0
        if (poParameter->GetChildCount() >= 2)
5933
0
        {
5934
0
            const char *pszValue = poParameter->GetValue();
5935
0
            if (EQUAL(pszValue, "PARAMETER") &&
5936
0
                EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5937
0
                      pszParameter))
5938
0
            {
5939
0
                return iChild;
5940
0
            }
5941
0
            else if (EQUAL(pszValue, "METHOD"))
5942
0
            {
5943
0
                bIsWKT2 = true;
5944
0
            }
5945
0
        }
5946
0
    }
5947
5948
    /* -------------------------------------------------------------------- */
5949
    /*      Try similar names, for selected parameters.                     */
5950
    /* -------------------------------------------------------------------- */
5951
0
    if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
5952
0
    {
5953
0
        if (bIsWKT2)
5954
0
        {
5955
0
            int iChild = FindProjParm(
5956
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5957
0
            if (iChild == -1)
5958
0
                iChild = FindProjParm(
5959
0
                    EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
5960
0
            return iChild;
5961
0
        }
5962
0
        return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
5963
0
    }
5964
5965
0
    if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
5966
0
    {
5967
0
        if (bIsWKT2)
5968
0
        {
5969
0
            int iChild = FindProjParm(
5970
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5971
0
            if (iChild == -1)
5972
0
                iChild = FindProjParm(
5973
0
                    EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
5974
0
            return iChild;
5975
0
        }
5976
0
        int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
5977
0
        if (iChild == -1)
5978
0
            iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
5979
0
        return iChild;
5980
0
    }
5981
5982
0
    return -1;
5983
0
}
5984
5985
/************************************************************************/
5986
/*                            GetProjParm()                             */
5987
/************************************************************************/
5988
5989
/**
5990
 * \brief Fetch a projection parameter value.
5991
 *
5992
 * NOTE: This code should be modified to translate non degree angles into
5993
 * degrees based on the GEOGCS unit.  This has not yet been done.
5994
 *
5995
 * This method is the same as the C function OSRGetProjParm().
5996
 *
5997
 * @param pszName the name of the parameter to fetch, from the set of
5998
 * SRS_PP codes in ogr_srs_api.h.
5999
 *
6000
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6001
 *
6002
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6003
 *
6004
 * @return value of parameter.
6005
 */
6006
6007
double OGRSpatialReference::GetProjParm(const char *pszName,
6008
                                        double dfDefaultValue,
6009
                                        OGRErr *pnErr) const
6010
6011
0
{
6012
0
    TAKE_OPTIONAL_LOCK();
6013
6014
0
    d->refreshProjObj();
6015
0
    GetRoot();  // force update of d->m_bNodesWKT2
6016
6017
0
    if (pnErr != nullptr)
6018
0
        *pnErr = OGRERR_NONE;
6019
6020
    /* -------------------------------------------------------------------- */
6021
    /*      Find the desired parameter.                                     */
6022
    /* -------------------------------------------------------------------- */
6023
0
    const OGR_SRSNode *poPROJCS =
6024
0
        GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6025
0
    if (poPROJCS == nullptr)
6026
0
    {
6027
0
        if (pnErr != nullptr)
6028
0
            *pnErr = OGRERR_FAILURE;
6029
0
        return dfDefaultValue;
6030
0
    }
6031
6032
0
    const int iChild = FindProjParm(pszName, poPROJCS);
6033
0
    if (iChild == -1)
6034
0
    {
6035
0
        if (IsProjected() && GetAxesCount() == 3)
6036
0
        {
6037
0
            OGRSpatialReference *poSRSTmp = Clone();
6038
0
            poSRSTmp->DemoteTo2D(nullptr);
6039
0
            const double dfRet =
6040
0
                poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6041
0
            delete poSRSTmp;
6042
0
            return dfRet;
6043
0
        }
6044
6045
0
        if (pnErr != nullptr)
6046
0
            *pnErr = OGRERR_FAILURE;
6047
0
        return dfDefaultValue;
6048
0
    }
6049
6050
0
    const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6051
0
    return CPLAtof(poParameter->GetChild(1)->GetValue());
6052
0
}
6053
6054
/************************************************************************/
6055
/*                           OSRGetProjParm()                           */
6056
/************************************************************************/
6057
6058
/**
6059
 * \brief Fetch a projection parameter value.
6060
 *
6061
 * This function is the same as OGRSpatialReference::GetProjParm()
6062
 */
6063
double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6064
                      double dfDefaultValue, OGRErr *pnErr)
6065
6066
0
{
6067
0
    VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6068
6069
0
    return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6070
0
}
6071
6072
/************************************************************************/
6073
/*                          GetNormProjParm()                           */
6074
/************************************************************************/
6075
6076
/**
6077
 * \brief Fetch a normalized projection parameter value.
6078
 *
6079
 * This method is the same as GetProjParm() except that the value of
6080
 * the parameter is "normalized" into degrees or meters depending on
6081
 * whether it is linear or angular.
6082
 *
6083
 * This method is the same as the C function OSRGetNormProjParm().
6084
 *
6085
 * @param pszName the name of the parameter to fetch, from the set of
6086
 * SRS_PP codes in ogr_srs_api.h.
6087
 *
6088
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6089
 *
6090
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6091
 *
6092
 * @return value of parameter.
6093
 */
6094
6095
double OGRSpatialReference::GetNormProjParm(const char *pszName,
6096
                                            double dfDefaultValue,
6097
                                            OGRErr *pnErr) const
6098
6099
0
{
6100
0
    TAKE_OPTIONAL_LOCK();
6101
6102
0
    GetNormInfo();
6103
6104
0
    OGRErr nError = OGRERR_NONE;
6105
0
    double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6106
0
    if (pnErr != nullptr)
6107
0
        *pnErr = nError;
6108
6109
    // If we got the default just return it unadjusted.
6110
0
    if (nError != OGRERR_NONE)
6111
0
        return dfRawResult;
6112
6113
0
    if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6114
0
        dfRawResult *= d->dfToDegrees;
6115
6116
0
    if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6117
0
        return dfRawResult * d->dfToMeter;
6118
6119
0
    return dfRawResult;
6120
0
}
6121
6122
/************************************************************************/
6123
/*                         OSRGetNormProjParm()                         */
6124
/************************************************************************/
6125
6126
/**
6127
 * \brief This function is the same as OGRSpatialReference::
6128
 *
6129
 * This function is the same as OGRSpatialReference::GetNormProjParm()
6130
 */
6131
double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6132
                          double dfDefaultValue, OGRErr *pnErr)
6133
6134
0
{
6135
0
    VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6136
6137
0
    return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6138
0
}
6139
6140
/************************************************************************/
6141
/*                          SetNormProjParm()                           */
6142
/************************************************************************/
6143
6144
/**
6145
 * \brief Set a projection parameter with a normalized value.
6146
 *
6147
 * This method is the same as SetProjParm() except that the value of
6148
 * the parameter passed in is assumed to be in "normalized" form (decimal
6149
 * degrees for angular values, meters for linear values.  The values are
6150
 * converted in a form suitable for the GEOGCS and linear units in effect.
6151
 *
6152
 * This method is the same as the C function OSRSetNormProjParm().
6153
 *
6154
 * @param pszName the parameter name, which should be selected from
6155
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6156
 *
6157
 * @param dfValue value to assign.
6158
 *
6159
 * @return OGRERR_NONE on success.
6160
 */
6161
6162
OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6163
6164
0
{
6165
0
    TAKE_OPTIONAL_LOCK();
6166
6167
0
    GetNormInfo();
6168
6169
0
    if (d->dfToDegrees != 0.0 &&
6170
0
        (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6171
0
        IsAngularParameter(pszName))
6172
0
    {
6173
0
        dfValue /= d->dfToDegrees;
6174
0
    }
6175
0
    else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6176
0
             IsLinearParameter(pszName))
6177
0
        dfValue /= d->dfToMeter;
6178
6179
0
    return SetProjParm(pszName, dfValue);
6180
0
}
6181
6182
/************************************************************************/
6183
/*                         OSRSetNormProjParm()                         */
6184
/************************************************************************/
6185
6186
/**
6187
 * \brief Set a projection parameter with a normalized value.
6188
 *
6189
 * This function is the same as OGRSpatialReference::SetNormProjParm()
6190
 */
6191
OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6192
                          double dfValue)
6193
6194
0
{
6195
0
    VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6196
6197
0
    return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6198
0
}
6199
6200
/************************************************************************/
6201
/*                               SetTM()                                */
6202
/************************************************************************/
6203
6204
OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6205
                                  double dfScale, double dfFalseEasting,
6206
                                  double dfFalseNorthing)
6207
6208
831
{
6209
831
    TAKE_OPTIONAL_LOCK();
6210
6211
831
    return d->replaceConversionAndUnref(
6212
831
        proj_create_conversion_transverse_mercator(
6213
831
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6214
831
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6215
831
}
6216
6217
/************************************************************************/
6218
/*                              OSRSetTM()                              */
6219
/************************************************************************/
6220
6221
OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6222
                double dfCenterLong, double dfScale, double dfFalseEasting,
6223
                double dfFalseNorthing)
6224
6225
0
{
6226
0
    VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6227
6228
0
    return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6229
0
                                  dfFalseEasting, dfFalseNorthing);
6230
0
}
6231
6232
/************************************************************************/
6233
/*                            SetTMVariant()                            */
6234
/************************************************************************/
6235
6236
OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6237
                                         double dfCenterLat,
6238
                                         double dfCenterLong, double dfScale,
6239
                                         double dfFalseEasting,
6240
                                         double dfFalseNorthing)
6241
6242
0
{
6243
0
    TAKE_OPTIONAL_LOCK();
6244
6245
0
    SetProjection(pszVariantName);
6246
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6247
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6248
0
    SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6249
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6250
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6251
6252
0
    return OGRERR_NONE;
6253
0
}
6254
6255
/************************************************************************/
6256
/*                          OSRSetTMVariant()                           */
6257
/************************************************************************/
6258
6259
OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6260
                       double dfCenterLat, double dfCenterLong, double dfScale,
6261
                       double dfFalseEasting, double dfFalseNorthing)
6262
6263
0
{
6264
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6265
6266
0
    return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6267
0
                                         dfCenterLong, dfScale, dfFalseEasting,
6268
0
                                         dfFalseNorthing);
6269
0
}
6270
6271
/************************************************************************/
6272
/*                              SetTMSO()                               */
6273
/************************************************************************/
6274
6275
OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6276
                                    double dfScale, double dfFalseEasting,
6277
                                    double dfFalseNorthing)
6278
6279
6
{
6280
6
    TAKE_OPTIONAL_LOCK();
6281
6282
6
    auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6283
6
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6284
6
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6285
6286
6
    const char *pszName = nullptr;
6287
6
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6288
6
    CPLString osName = pszName ? pszName : "";
6289
6290
6
    d->refreshProjObj();
6291
6292
6
    d->demoteFromBoundCRS();
6293
6294
6
    auto cs = proj_create_cartesian_2D_cs(
6295
6
        d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6296
6
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6297
6
    auto projCRS =
6298
6
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6299
6
                                  d->getGeodBaseCRS(), conv, cs);
6300
6
    proj_destroy(conv);
6301
6
    proj_destroy(cs);
6302
6303
6
    d->setPjCRS(projCRS);
6304
6305
6
    d->undoDemoteFromBoundCRS();
6306
6307
6
    return OGRERR_NONE;
6308
6
}
6309
6310
/************************************************************************/
6311
/*                             OSRSetTMSO()                             */
6312
/************************************************************************/
6313
6314
OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6315
                  double dfCenterLong, double dfScale, double dfFalseEasting,
6316
                  double dfFalseNorthing)
6317
6318
0
{
6319
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6320
6321
0
    return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6322
0
                                    dfFalseEasting, dfFalseNorthing);
6323
0
}
6324
6325
/************************************************************************/
6326
/*                              SetTPED()                               */
6327
/************************************************************************/
6328
6329
OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6330
                                    double dfLat2, double dfLong2,
6331
                                    double dfFalseEasting,
6332
                                    double dfFalseNorthing)
6333
6334
0
{
6335
0
    TAKE_OPTIONAL_LOCK();
6336
6337
0
    return d->replaceConversionAndUnref(
6338
0
        proj_create_conversion_two_point_equidistant(
6339
0
            d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6340
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6341
0
}
6342
6343
/************************************************************************/
6344
/*                             OSRSetTPED()                             */
6345
/************************************************************************/
6346
6347
OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6348
                  double dfLat2, double dfLong2, double dfFalseEasting,
6349
                  double dfFalseNorthing)
6350
6351
0
{
6352
0
    VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6353
6354
0
    return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6355
0
                                    dfFalseEasting, dfFalseNorthing);
6356
0
}
6357
6358
/************************************************************************/
6359
/*                               SetTMG()                               */
6360
/************************************************************************/
6361
6362
OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6363
                                   double dfFalseEasting,
6364
                                   double dfFalseNorthing)
6365
6366
0
{
6367
0
    TAKE_OPTIONAL_LOCK();
6368
6369
0
    return d->replaceConversionAndUnref(
6370
0
        proj_create_conversion_tunisia_mapping_grid(
6371
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6372
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6373
0
}
6374
6375
/************************************************************************/
6376
/*                             OSRSetTMG()                              */
6377
/************************************************************************/
6378
6379
OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6380
                 double dfCenterLong, double dfFalseEasting,
6381
                 double dfFalseNorthing)
6382
6383
0
{
6384
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6385
6386
0
    return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6387
0
                                   dfFalseNorthing);
6388
0
}
6389
6390
/************************************************************************/
6391
/*                              SetACEA()                               */
6392
/************************************************************************/
6393
6394
OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6395
                                    double dfCenterLat, double dfCenterLong,
6396
                                    double dfFalseEasting,
6397
                                    double dfFalseNorthing)
6398
6399
29
{
6400
29
    TAKE_OPTIONAL_LOCK();
6401
6402
    // Note different order of parameters. The one in PROJ is conformant with
6403
    // EPSG
6404
29
    return d->replaceConversionAndUnref(
6405
29
        proj_create_conversion_albers_equal_area(
6406
29
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6407
29
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6408
29
}
6409
6410
/************************************************************************/
6411
/*                             OSRSetACEA()                             */
6412
/************************************************************************/
6413
6414
OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6415
                  double dfCenterLat, double dfCenterLong,
6416
                  double dfFalseEasting, double dfFalseNorthing)
6417
6418
0
{
6419
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6420
6421
0
    return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6422
0
                                    dfFalseEasting, dfFalseNorthing);
6423
0
}
6424
6425
/************************************************************************/
6426
/*                               SetAE()                                */
6427
/************************************************************************/
6428
6429
OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6430
                                  double dfFalseEasting, double dfFalseNorthing)
6431
6432
1.89k
{
6433
1.89k
    TAKE_OPTIONAL_LOCK();
6434
6435
1.89k
    return d->replaceConversionAndUnref(
6436
1.89k
        proj_create_conversion_azimuthal_equidistant(
6437
1.89k
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6438
1.89k
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6439
1.89k
}
6440
6441
/************************************************************************/
6442
/*                              OSRSetAE()                              */
6443
/************************************************************************/
6444
6445
OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6446
                double dfCenterLong, double dfFalseEasting,
6447
                double dfFalseNorthing)
6448
6449
0
{
6450
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6451
6452
0
    return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6453
0
                                  dfFalseNorthing);
6454
0
}
6455
6456
/************************************************************************/
6457
/*                              SetBonne()                              */
6458
/************************************************************************/
6459
6460
OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6461
                                     double dfFalseEasting,
6462
                                     double dfFalseNorthing)
6463
6464
0
{
6465
0
    TAKE_OPTIONAL_LOCK();
6466
6467
0
    return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6468
0
        d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6469
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6470
0
}
6471
6472
/************************************************************************/
6473
/*                            OSRSetBonne()                             */
6474
/************************************************************************/
6475
6476
OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6477
                   double dfCentralMeridian, double dfFalseEasting,
6478
                   double dfFalseNorthing)
6479
6480
0
{
6481
0
    VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6482
6483
0
    return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6484
0
                                     dfFalseNorthing);
6485
0
}
6486
6487
/************************************************************************/
6488
/*                               SetCEA()                               */
6489
/************************************************************************/
6490
6491
OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6492
                                   double dfFalseEasting,
6493
                                   double dfFalseNorthing)
6494
6495
76
{
6496
76
    TAKE_OPTIONAL_LOCK();
6497
6498
76
    return d->replaceConversionAndUnref(
6499
76
        proj_create_conversion_lambert_cylindrical_equal_area(
6500
76
            d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6501
76
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6502
76
}
6503
6504
/************************************************************************/
6505
/*                             OSRSetCEA()                              */
6506
/************************************************************************/
6507
6508
OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6509
                 double dfCentralMeridian, double dfFalseEasting,
6510
                 double dfFalseNorthing)
6511
6512
0
{
6513
0
    VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6514
6515
0
    return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6516
0
                                   dfFalseNorthing);
6517
0
}
6518
6519
/************************************************************************/
6520
/*                               SetCS()                                */
6521
/************************************************************************/
6522
6523
OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6524
                                  double dfFalseEasting, double dfFalseNorthing)
6525
6526
63
{
6527
63
    TAKE_OPTIONAL_LOCK();
6528
6529
63
    return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6530
63
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6531
63
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6532
63
}
6533
6534
/************************************************************************/
6535
/*                              OSRSetCS()                              */
6536
/************************************************************************/
6537
6538
OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6539
                double dfCenterLong, double dfFalseEasting,
6540
                double dfFalseNorthing)
6541
6542
0
{
6543
0
    VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6544
6545
0
    return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6546
0
                                  dfFalseNorthing);
6547
0
}
6548
6549
/************************************************************************/
6550
/*                               SetEC()                                */
6551
/************************************************************************/
6552
6553
OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6554
                                  double dfCenterLat, double dfCenterLong,
6555
                                  double dfFalseEasting, double dfFalseNorthing)
6556
6557
30
{
6558
30
    TAKE_OPTIONAL_LOCK();
6559
6560
    // Note: different order of arguments
6561
30
    return d->replaceConversionAndUnref(
6562
30
        proj_create_conversion_equidistant_conic(
6563
30
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6564
30
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6565
30
}
6566
6567
/************************************************************************/
6568
/*                              OSRSetEC()                              */
6569
/************************************************************************/
6570
6571
OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6572
                double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6573
                double dfFalseNorthing)
6574
6575
0
{
6576
0
    VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6577
6578
0
    return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6579
0
                                  dfFalseEasting, dfFalseNorthing);
6580
0
}
6581
6582
/************************************************************************/
6583
/*                             SetEckert()                              */
6584
/************************************************************************/
6585
6586
OGRErr OGRSpatialReference::SetEckert(int nVariation,  // 1-6.
6587
                                      double dfCentralMeridian,
6588
                                      double dfFalseEasting,
6589
                                      double dfFalseNorthing)
6590
6591
0
{
6592
0
    TAKE_OPTIONAL_LOCK();
6593
6594
0
    PJ *conv;
6595
0
    if (nVariation == 1)
6596
0
    {
6597
0
        conv = proj_create_conversion_eckert_i(
6598
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6599
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6600
0
    }
6601
0
    else if (nVariation == 2)
6602
0
    {
6603
0
        conv = proj_create_conversion_eckert_ii(
6604
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6605
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6606
0
    }
6607
0
    else if (nVariation == 3)
6608
0
    {
6609
0
        conv = proj_create_conversion_eckert_iii(
6610
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6611
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6612
0
    }
6613
0
    else if (nVariation == 4)
6614
0
    {
6615
0
        conv = proj_create_conversion_eckert_iv(
6616
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6617
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6618
0
    }
6619
0
    else if (nVariation == 5)
6620
0
    {
6621
0
        conv = proj_create_conversion_eckert_v(
6622
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6623
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6624
0
    }
6625
0
    else if (nVariation == 6)
6626
0
    {
6627
0
        conv = proj_create_conversion_eckert_vi(
6628
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6629
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6630
0
    }
6631
0
    else
6632
0
    {
6633
0
        CPLError(CE_Failure, CPLE_AppDefined,
6634
0
                 "Unsupported Eckert variation (%d).", nVariation);
6635
0
        return OGRERR_UNSUPPORTED_SRS;
6636
0
    }
6637
6638
0
    return d->replaceConversionAndUnref(conv);
6639
0
}
6640
6641
/************************************************************************/
6642
/*                            OSRSetEckert()                            */
6643
/************************************************************************/
6644
6645
OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6646
                    double dfCentralMeridian, double dfFalseEasting,
6647
                    double dfFalseNorthing)
6648
6649
0
{
6650
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6651
6652
0
    return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6653
0
                                      dfFalseEasting, dfFalseNorthing);
6654
0
}
6655
6656
/************************************************************************/
6657
/*                            SetEckertIV()                             */
6658
/*                                                                      */
6659
/*      Deprecated                                                      */
6660
/************************************************************************/
6661
6662
OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6663
                                        double dfFalseEasting,
6664
                                        double dfFalseNorthing)
6665
6666
0
{
6667
0
    return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6668
0
}
6669
6670
/************************************************************************/
6671
/*                           OSRSetEckertIV()                           */
6672
/************************************************************************/
6673
6674
OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6675
                      double dfFalseEasting, double dfFalseNorthing)
6676
6677
0
{
6678
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6679
6680
0
    return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6681
0
                                        dfFalseNorthing);
6682
0
}
6683
6684
/************************************************************************/
6685
/*                            SetEckertVI()                             */
6686
/*                                                                      */
6687
/*      Deprecated                                                      */
6688
/************************************************************************/
6689
6690
OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6691
                                        double dfFalseEasting,
6692
                                        double dfFalseNorthing)
6693
6694
0
{
6695
0
    return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6696
0
}
6697
6698
/************************************************************************/
6699
/*                           OSRSetEckertVI()                           */
6700
/************************************************************************/
6701
6702
OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6703
                      double dfFalseEasting, double dfFalseNorthing)
6704
6705
0
{
6706
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6707
6708
0
    return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6709
0
                                        dfFalseNorthing);
6710
0
}
6711
6712
/************************************************************************/
6713
/*                         SetEquirectangular()                         */
6714
/************************************************************************/
6715
6716
OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6717
                                               double dfCenterLong,
6718
                                               double dfFalseEasting,
6719
                                               double dfFalseNorthing)
6720
6721
0
{
6722
0
    TAKE_OPTIONAL_LOCK();
6723
6724
0
    if (dfCenterLat == 0.0)
6725
0
    {
6726
0
        return d->replaceConversionAndUnref(
6727
0
            proj_create_conversion_equidistant_cylindrical(
6728
0
                d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6729
0
                dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6730
0
    }
6731
6732
    // Non-standard extension with non-zero latitude of origin
6733
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6734
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6735
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6736
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6737
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6738
6739
0
    return OGRERR_NONE;
6740
0
}
6741
6742
/************************************************************************/
6743
/*                       OSRSetEquirectangular()                        */
6744
/************************************************************************/
6745
6746
OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6747
                             double dfCenterLong, double dfFalseEasting,
6748
                             double dfFalseNorthing)
6749
6750
0
{
6751
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6752
6753
0
    return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6754
0
                                               dfFalseEasting, dfFalseNorthing);
6755
0
}
6756
6757
/************************************************************************/
6758
/*                         SetEquirectangular2()                        */
6759
/* Generalized form                                                     */
6760
/************************************************************************/
6761
6762
OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6763
                                                double dfCenterLong,
6764
                                                double dfStdParallel1,
6765
                                                double dfFalseEasting,
6766
                                                double dfFalseNorthing)
6767
6768
19
{
6769
19
    TAKE_OPTIONAL_LOCK();
6770
6771
19
    if (dfCenterLat == 0.0)
6772
19
    {
6773
19
        return d->replaceConversionAndUnref(
6774
19
            proj_create_conversion_equidistant_cylindrical(
6775
19
                d->getPROJContext(), dfStdParallel1, dfCenterLong,
6776
19
                dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6777
19
    }
6778
6779
    // Non-standard extension with non-zero latitude of origin
6780
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6781
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6782
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6783
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6784
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6785
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6786
6787
0
    return OGRERR_NONE;
6788
19
}
6789
6790
/************************************************************************/
6791
/*                       OSRSetEquirectangular2()                       */
6792
/************************************************************************/
6793
6794
OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6795
                              double dfCenterLong, double dfStdParallel1,
6796
                              double dfFalseEasting, double dfFalseNorthing)
6797
6798
0
{
6799
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6800
6801
0
    return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6802
0
                                                dfStdParallel1, dfFalseEasting,
6803
0
                                                dfFalseNorthing);
6804
0
}
6805
6806
/************************************************************************/
6807
/*                               SetGS()                                */
6808
/************************************************************************/
6809
6810
OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6811
                                  double dfFalseEasting, double dfFalseNorthing)
6812
6813
0
{
6814
0
    return d->replaceConversionAndUnref(proj_create_conversion_gall(
6815
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6816
0
        nullptr, 0.0, nullptr, 0.0));
6817
0
}
6818
6819
/************************************************************************/
6820
/*                              OSRSetGS()                              */
6821
/************************************************************************/
6822
6823
OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6824
                double dfFalseEasting, double dfFalseNorthing)
6825
6826
0
{
6827
0
    VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6828
6829
0
    return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6830
0
                                  dfFalseNorthing);
6831
0
}
6832
6833
/************************************************************************/
6834
/*                               SetGH()                                */
6835
/************************************************************************/
6836
6837
OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6838
                                  double dfFalseEasting, double dfFalseNorthing)
6839
6840
0
{
6841
0
    TAKE_OPTIONAL_LOCK();
6842
6843
0
    return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6844
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6845
0
        nullptr, 0.0, nullptr, 0.0));
6846
0
}
6847
6848
/************************************************************************/
6849
/*                              OSRSetGH()                              */
6850
/************************************************************************/
6851
6852
OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6853
                double dfFalseEasting, double dfFalseNorthing)
6854
6855
0
{
6856
0
    VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6857
6858
0
    return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6859
0
                                  dfFalseNorthing);
6860
0
}
6861
6862
/************************************************************************/
6863
/*                              SetIGH()                                */
6864
/************************************************************************/
6865
6866
OGRErr OGRSpatialReference::SetIGH()
6867
6868
0
{
6869
0
    TAKE_OPTIONAL_LOCK();
6870
6871
0
    return d->replaceConversionAndUnref(
6872
0
        proj_create_conversion_interrupted_goode_homolosine(
6873
0
            d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6874
0
}
6875
6876
/************************************************************************/
6877
/*                              OSRSetIGH()                             */
6878
/************************************************************************/
6879
6880
OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6881
6882
0
{
6883
0
    VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6884
6885
0
    return ToPointer(hSRS)->SetIGH();
6886
0
}
6887
6888
/************************************************************************/
6889
/*                              SetGEOS()                               */
6890
/************************************************************************/
6891
6892
OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
6893
                                    double dfSatelliteHeight,
6894
                                    double dfFalseEasting,
6895
                                    double dfFalseNorthing)
6896
6897
0
{
6898
0
    TAKE_OPTIONAL_LOCK();
6899
6900
0
    return d->replaceConversionAndUnref(
6901
0
        proj_create_conversion_geostationary_satellite_sweep_y(
6902
0
            d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
6903
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6904
0
}
6905
6906
/************************************************************************/
6907
/*                              OSRSetGEOS()                             */
6908
/************************************************************************/
6909
6910
OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6911
                  double dfSatelliteHeight, double dfFalseEasting,
6912
                  double dfFalseNorthing)
6913
6914
0
{
6915
0
    VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
6916
6917
0
    return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
6918
0
                                    dfFalseEasting, dfFalseNorthing);
6919
0
}
6920
6921
/************************************************************************/
6922
/*                       SetGaussSchreiberTMercator()                   */
6923
/************************************************************************/
6924
6925
OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
6926
                                                       double dfCenterLong,
6927
                                                       double dfScale,
6928
                                                       double dfFalseEasting,
6929
                                                       double dfFalseNorthing)
6930
6931
0
{
6932
0
    TAKE_OPTIONAL_LOCK();
6933
6934
0
    return d->replaceConversionAndUnref(
6935
0
        proj_create_conversion_gauss_schreiber_transverse_mercator(
6936
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6937
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6938
0
}
6939
6940
/************************************************************************/
6941
/*                     OSRSetGaussSchreiberTMercator()                  */
6942
/************************************************************************/
6943
6944
OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
6945
                                     double dfCenterLat, double dfCenterLong,
6946
                                     double dfScale, double dfFalseEasting,
6947
                                     double dfFalseNorthing)
6948
6949
0
{
6950
0
    VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
6951
6952
0
    return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6953
0
        dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
6954
0
}
6955
6956
/************************************************************************/
6957
/*                            SetGnomonic()                             */
6958
/************************************************************************/
6959
6960
OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
6961
                                        double dfFalseEasting,
6962
                                        double dfFalseNorthing)
6963
6964
258
{
6965
258
    TAKE_OPTIONAL_LOCK();
6966
6967
258
    return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
6968
258
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6969
258
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6970
258
}
6971
6972
/************************************************************************/
6973
/*                           OSRSetGnomonic()                           */
6974
/************************************************************************/
6975
6976
OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
6977
                      double dfCenterLong, double dfFalseEasting,
6978
                      double dfFalseNorthing)
6979
6980
0
{
6981
0
    VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
6982
6983
0
    return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
6984
0
                                        dfFalseEasting, dfFalseNorthing);
6985
0
}
6986
6987
/************************************************************************/
6988
/*                              SetHOMAC()                              */
6989
/************************************************************************/
6990
6991
/**
6992
 * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
6993
 * azimuth angle.
6994
 *
6995
 * This projection corresponds to EPSG projection method 9815, also
6996
 * sometimes known as hotine oblique mercator (variant B).
6997
 *
6998
 * This method does the same thing as the C function OSRSetHOMAC().
6999
 *
7000
 * @param dfCenterLat Latitude of the projection origin.
7001
 * @param dfCenterLong Longitude of the projection origin.
7002
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7003
 * centerline.
7004
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7005
 * @param dfScale Scale factor applies to the projection origin.
7006
 * @param dfFalseEasting False easting.
7007
 * @param dfFalseNorthing False northing.
7008
 *
7009
 * @return OGRERR_NONE on success.
7010
 */
7011
7012
OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7013
                                     double dfAzimuth, double dfRectToSkew,
7014
                                     double dfScale, double dfFalseEasting,
7015
                                     double dfFalseNorthing)
7016
7017
1
{
7018
1
    TAKE_OPTIONAL_LOCK();
7019
7020
1
    return d->replaceConversionAndUnref(
7021
1
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7022
1
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7023
1
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7024
1
            0.0, nullptr, 0.0));
7025
1
}
7026
7027
/************************************************************************/
7028
/*                            OSRSetHOMAC()                             */
7029
/************************************************************************/
7030
7031
/**
7032
 * \brief Set an Oblique Mercator projection using azimuth angle.
7033
 *
7034
 * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7035
 */
7036
OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7037
                   double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7038
                   double dfScale, double dfFalseEasting,
7039
                   double dfFalseNorthing)
7040
7041
0
{
7042
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7043
7044
0
    return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7045
0
                                     dfRectToSkew, dfScale, dfFalseEasting,
7046
0
                                     dfFalseNorthing);
7047
0
}
7048
7049
/************************************************************************/
7050
/*                               SetHOM()                               */
7051
/************************************************************************/
7052
7053
/**
7054
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7055
 *
7056
 * This projection corresponds to EPSG projection method 9812, also
7057
 * sometimes known as hotine oblique mercator (variant A)..
7058
 *
7059
 * This method does the same thing as the C function OSRSetHOM().
7060
 *
7061
 * @param dfCenterLat Latitude of the projection origin.
7062
 * @param dfCenterLong Longitude of the projection origin.
7063
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7064
 * centerline.
7065
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7066
 * @param dfScale Scale factor applies to the projection origin.
7067
 * @param dfFalseEasting False easting.
7068
 * @param dfFalseNorthing False northing.
7069
 *
7070
 * @return OGRERR_NONE on success.
7071
 */
7072
7073
OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7074
                                   double dfAzimuth, double dfRectToSkew,
7075
                                   double dfScale, double dfFalseEasting,
7076
                                   double dfFalseNorthing)
7077
7078
707
{
7079
707
    TAKE_OPTIONAL_LOCK();
7080
7081
707
    return d->replaceConversionAndUnref(
7082
707
        proj_create_conversion_hotine_oblique_mercator_variant_a(
7083
707
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7084
707
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7085
707
            0.0, nullptr, 0.0));
7086
707
}
7087
7088
/************************************************************************/
7089
/*                             OSRSetHOM()                              */
7090
/************************************************************************/
7091
/**
7092
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7093
 *
7094
 * This is the same as the C++ method OGRSpatialReference::SetHOM()
7095
 */
7096
OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7097
                 double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7098
                 double dfScale, double dfFalseEasting, double dfFalseNorthing)
7099
7100
0
{
7101
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7102
7103
0
    return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7104
0
                                   dfRectToSkew, dfScale, dfFalseEasting,
7105
0
                                   dfFalseNorthing);
7106
0
}
7107
7108
/************************************************************************/
7109
/*                             SetHOM2PNO()                             */
7110
/************************************************************************/
7111
7112
/**
7113
 * \brief Set a Hotine Oblique Mercator projection using two points on
7114
 * projection centerline.
7115
 *
7116
 * This method does the same thing as the C function OSRSetHOM2PNO().
7117
 *
7118
 * @param dfCenterLat Latitude of the projection origin.
7119
 * @param dfLat1 Latitude of the first point on center line.
7120
 * @param dfLong1 Longitude of the first point on center line.
7121
 * @param dfLat2 Latitude of the second point on center line.
7122
 * @param dfLong2 Longitude of the second point on center line.
7123
 * @param dfScale Scale factor applies to the projection origin.
7124
 * @param dfFalseEasting False easting.
7125
 * @param dfFalseNorthing False northing.
7126
 *
7127
 * @return OGRERR_NONE on success.
7128
 */
7129
7130
OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7131
                                       double dfLong1, double dfLat2,
7132
                                       double dfLong2, double dfScale,
7133
                                       double dfFalseEasting,
7134
                                       double dfFalseNorthing)
7135
7136
0
{
7137
0
    TAKE_OPTIONAL_LOCK();
7138
7139
0
    return d->replaceConversionAndUnref(
7140
0
        proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7141
0
            d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7142
0
            dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7143
0
            0.0));
7144
0
}
7145
7146
/************************************************************************/
7147
/*                           OSRSetHOM2PNO()                            */
7148
/************************************************************************/
7149
/**
7150
 * \brief Set a Hotine Oblique Mercator projection using two points on
7151
 *  projection centerline.
7152
 *
7153
 * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7154
 */
7155
OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7156
                     double dfLat1, double dfLong1, double dfLat2,
7157
                     double dfLong2, double dfScale, double dfFalseEasting,
7158
                     double dfFalseNorthing)
7159
7160
0
{
7161
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7162
7163
0
    return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7164
0
                                       dfLong2, dfScale, dfFalseEasting,
7165
0
                                       dfFalseNorthing);
7166
0
}
7167
7168
/************************************************************************/
7169
/*                               SetLOM()                               */
7170
/************************************************************************/
7171
7172
/**
7173
 * \brief Set a Laborde Oblique Mercator projection.
7174
 *
7175
 * @param dfCenterLat Latitude of the projection origin.
7176
 * @param dfCenterLong Longitude of the projection origin.
7177
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7178
 * centerline.
7179
 * @param dfScale Scale factor on the initiali line
7180
 * @param dfFalseEasting False easting.
7181
 * @param dfFalseNorthing False northing.
7182
 *
7183
 * @return OGRERR_NONE on success.
7184
 */
7185
7186
OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7187
                                   double dfAzimuth, double dfScale,
7188
                                   double dfFalseEasting,
7189
                                   double dfFalseNorthing)
7190
7191
843
{
7192
843
    TAKE_OPTIONAL_LOCK();
7193
7194
843
    return d->replaceConversionAndUnref(
7195
843
        proj_create_conversion_laborde_oblique_mercator(
7196
843
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7197
843
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7198
843
}
7199
7200
/************************************************************************/
7201
/*                            SetIWMPolyconic()                         */
7202
/************************************************************************/
7203
7204
OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7205
                                            double dfCenterLong,
7206
                                            double dfFalseEasting,
7207
                                            double dfFalseNorthing)
7208
7209
0
{
7210
0
    TAKE_OPTIONAL_LOCK();
7211
7212
0
    return d->replaceConversionAndUnref(
7213
0
        proj_create_conversion_international_map_world_polyconic(
7214
0
            d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7215
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7216
0
}
7217
7218
/************************************************************************/
7219
/*                          OSRSetIWMPolyconic()                        */
7220
/************************************************************************/
7221
7222
OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7223
                          double dfLat2, double dfCenterLong,
7224
                          double dfFalseEasting, double dfFalseNorthing)
7225
7226
0
{
7227
0
    VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7228
7229
0
    return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7230
0
                                            dfFalseEasting, dfFalseNorthing);
7231
0
}
7232
7233
/************************************************************************/
7234
/*                             SetKrovak()                              */
7235
/************************************************************************/
7236
7237
/** Krovak east-north projection.
7238
 *
7239
 * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7240
 * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7241
 */
7242
OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7243
                                      double dfAzimuth,
7244
                                      double dfPseudoStdParallel1,
7245
                                      double dfScale, double dfFalseEasting,
7246
                                      double dfFalseNorthing)
7247
7248
0
{
7249
0
    TAKE_OPTIONAL_LOCK();
7250
7251
0
    return d->replaceConversionAndUnref(
7252
0
        proj_create_conversion_krovak_north_oriented(
7253
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7254
0
            dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7255
0
            nullptr, 0.0, nullptr, 0.0));
7256
0
}
7257
7258
/************************************************************************/
7259
/*                            OSRSetKrovak()                            */
7260
/************************************************************************/
7261
7262
OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7263
                    double dfCenterLong, double dfAzimuth,
7264
                    double dfPseudoStdParallel1, double dfScale,
7265
                    double dfFalseEasting, double dfFalseNorthing)
7266
7267
0
{
7268
0
    VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7269
7270
0
    return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7271
0
                                      dfPseudoStdParallel1, dfScale,
7272
0
                                      dfFalseEasting, dfFalseNorthing);
7273
0
}
7274
7275
/************************************************************************/
7276
/*                              SetLAEA()                               */
7277
/************************************************************************/
7278
7279
OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7280
                                    double dfFalseEasting,
7281
                                    double dfFalseNorthing)
7282
7283
59
{
7284
59
    TAKE_OPTIONAL_LOCK();
7285
7286
59
    auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7287
59
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7288
59
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7289
7290
59
    const char *pszName = nullptr;
7291
59
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7292
59
    CPLString osName = pszName ? pszName : "";
7293
7294
59
    d->refreshProjObj();
7295
7296
59
    d->demoteFromBoundCRS();
7297
7298
59
    auto cs = proj_create_cartesian_2D_cs(
7299
59
        d->getPROJContext(),
7300
59
        std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7301
59
            ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7302
59
        : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7303
59
            ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7304
59
            : PJ_CART2D_EASTING_NORTHING,
7305
59
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7306
59
    auto projCRS =
7307
59
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7308
59
                                  d->getGeodBaseCRS(), conv, cs);
7309
59
    proj_destroy(conv);
7310
59
    proj_destroy(cs);
7311
7312
59
    d->setPjCRS(projCRS);
7313
7314
59
    d->undoDemoteFromBoundCRS();
7315
7316
59
    return OGRERR_NONE;
7317
59
}
7318
7319
/************************************************************************/
7320
/*                             OSRSetLAEA()                             */
7321
/************************************************************************/
7322
7323
OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7324
                  double dfCenterLong, double dfFalseEasting,
7325
                  double dfFalseNorthing)
7326
7327
0
{
7328
0
    VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7329
7330
0
    return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7331
0
                                    dfFalseNorthing);
7332
0
}
7333
7334
/************************************************************************/
7335
/*                               SetLCC()                               */
7336
/************************************************************************/
7337
7338
OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7339
                                   double dfCenterLat, double dfCenterLong,
7340
                                   double dfFalseEasting,
7341
                                   double dfFalseNorthing)
7342
7343
42
{
7344
42
    TAKE_OPTIONAL_LOCK();
7345
7346
42
    return d->replaceConversionAndUnref(
7347
42
        proj_create_conversion_lambert_conic_conformal_2sp(
7348
42
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7349
42
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7350
42
}
7351
7352
/************************************************************************/
7353
/*                             OSRSetLCC()                              */
7354
/************************************************************************/
7355
7356
OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7357
                 double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7358
                 double dfFalseNorthing)
7359
7360
0
{
7361
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7362
7363
0
    return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7364
0
                                   dfFalseEasting, dfFalseNorthing);
7365
0
}
7366
7367
/************************************************************************/
7368
/*                             SetLCC1SP()                              */
7369
/************************************************************************/
7370
7371
OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7372
                                      double dfScale, double dfFalseEasting,
7373
                                      double dfFalseNorthing)
7374
7375
429
{
7376
429
    TAKE_OPTIONAL_LOCK();
7377
7378
429
    return d->replaceConversionAndUnref(
7379
429
        proj_create_conversion_lambert_conic_conformal_1sp(
7380
429
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7381
429
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7382
429
}
7383
7384
/************************************************************************/
7385
/*                            OSRSetLCC1SP()                            */
7386
/************************************************************************/
7387
7388
OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7389
                    double dfCenterLong, double dfScale, double dfFalseEasting,
7390
                    double dfFalseNorthing)
7391
7392
0
{
7393
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7394
7395
0
    return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7396
0
                                      dfFalseEasting, dfFalseNorthing);
7397
0
}
7398
7399
/************************************************************************/
7400
/*                              SetLCCB()                               */
7401
/************************************************************************/
7402
7403
OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7404
                                    double dfCenterLat, double dfCenterLong,
7405
                                    double dfFalseEasting,
7406
                                    double dfFalseNorthing)
7407
7408
0
{
7409
0
    TAKE_OPTIONAL_LOCK();
7410
7411
0
    return d->replaceConversionAndUnref(
7412
0
        proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7413
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7414
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7415
0
}
7416
7417
/************************************************************************/
7418
/*                             OSRSetLCCB()                             */
7419
/************************************************************************/
7420
7421
OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7422
                  double dfCenterLat, double dfCenterLong,
7423
                  double dfFalseEasting, double dfFalseNorthing)
7424
7425
0
{
7426
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7427
7428
0
    return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7429
0
                                    dfFalseEasting, dfFalseNorthing);
7430
0
}
7431
7432
/************************************************************************/
7433
/*                               SetMC()                                */
7434
/************************************************************************/
7435
7436
OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7437
                                  double dfFalseEasting, double dfFalseNorthing)
7438
7439
9
{
7440
9
    TAKE_OPTIONAL_LOCK();
7441
7442
9
    (void)dfCenterLat;  // ignored
7443
7444
9
    return d->replaceConversionAndUnref(
7445
9
        proj_create_conversion_miller_cylindrical(
7446
9
            d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7447
9
            nullptr, 0, nullptr, 0));
7448
9
}
7449
7450
/************************************************************************/
7451
/*                              OSRSetMC()                              */
7452
/************************************************************************/
7453
7454
OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7455
                double dfCenterLong, double dfFalseEasting,
7456
                double dfFalseNorthing)
7457
7458
0
{
7459
0
    VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7460
7461
0
    return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7462
0
                                  dfFalseNorthing);
7463
0
}
7464
7465
/************************************************************************/
7466
/*                            SetMercator()                             */
7467
/************************************************************************/
7468
7469
OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7470
                                        double dfScale, double dfFalseEasting,
7471
                                        double dfFalseNorthing)
7472
7473
48
{
7474
48
    TAKE_OPTIONAL_LOCK();
7475
7476
48
    if (dfCenterLat != 0.0 && dfScale == 1.0)
7477
0
    {
7478
        // Not sure this is correct, but this is how it has been used
7479
        // historically
7480
0
        return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7481
0
                              dfFalseNorthing);
7482
0
    }
7483
48
    return d->replaceConversionAndUnref(
7484
48
        proj_create_conversion_mercator_variant_a(
7485
48
            d->getPROJContext(),
7486
48
            dfCenterLat,  // should be zero
7487
48
            dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7488
48
            nullptr, 0));
7489
48
}
7490
7491
/************************************************************************/
7492
/*                           OSRSetMercator()                           */
7493
/************************************************************************/
7494
7495
OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7496
                      double dfCenterLong, double dfScale,
7497
                      double dfFalseEasting, double dfFalseNorthing)
7498
7499
0
{
7500
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7501
7502
0
    return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7503
0
                                        dfFalseEasting, dfFalseNorthing);
7504
0
}
7505
7506
/************************************************************************/
7507
/*                           SetMercator2SP()                           */
7508
/************************************************************************/
7509
7510
OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7511
                                           double dfCenterLong,
7512
                                           double dfFalseEasting,
7513
                                           double dfFalseNorthing)
7514
7515
43
{
7516
43
    if (dfCenterLat == 0.0)
7517
43
    {
7518
43
        return d->replaceConversionAndUnref(
7519
43
            proj_create_conversion_mercator_variant_b(
7520
43
                d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7521
43
                dfFalseNorthing, nullptr, 0, nullptr, 0));
7522
43
    }
7523
7524
0
    TAKE_OPTIONAL_LOCK();
7525
7526
0
    SetProjection(SRS_PT_MERCATOR_2SP);
7527
7528
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7529
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7530
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7531
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7532
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7533
7534
0
    return OGRERR_NONE;
7535
43
}
7536
7537
/************************************************************************/
7538
/*                         OSRSetMercator2SP()                          */
7539
/************************************************************************/
7540
7541
OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7542
                         double dfCenterLat, double dfCenterLong,
7543
                         double dfFalseEasting, double dfFalseNorthing)
7544
7545
0
{
7546
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7547
7548
0
    return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7549
0
                                           dfFalseEasting, dfFalseNorthing);
7550
0
}
7551
7552
/************************************************************************/
7553
/*                            SetMollweide()                            */
7554
/************************************************************************/
7555
7556
OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7557
                                         double dfFalseEasting,
7558
                                         double dfFalseNorthing)
7559
7560
0
{
7561
0
    TAKE_OPTIONAL_LOCK();
7562
7563
0
    return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7564
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7565
0
        nullptr, 0, nullptr, 0));
7566
0
}
7567
7568
/************************************************************************/
7569
/*                          OSRSetMollweide()                           */
7570
/************************************************************************/
7571
7572
OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7573
                       double dfFalseEasting, double dfFalseNorthing)
7574
7575
0
{
7576
0
    VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7577
7578
0
    return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7579
0
                                         dfFalseNorthing);
7580
0
}
7581
7582
/************************************************************************/
7583
/*                              SetNZMG()                               */
7584
/************************************************************************/
7585
7586
OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7587
                                    double dfFalseEasting,
7588
                                    double dfFalseNorthing)
7589
7590
5
{
7591
5
    TAKE_OPTIONAL_LOCK();
7592
7593
5
    return d->replaceConversionAndUnref(
7594
5
        proj_create_conversion_new_zealand_mapping_grid(
7595
5
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7596
5
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7597
5
}
7598
7599
/************************************************************************/
7600
/*                             OSRSetNZMG()                             */
7601
/************************************************************************/
7602
7603
OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7604
                  double dfCenterLong, double dfFalseEasting,
7605
                  double dfFalseNorthing)
7606
7607
0
{
7608
0
    VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7609
7610
0
    return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7611
0
                                    dfFalseNorthing);
7612
0
}
7613
7614
/************************************************************************/
7615
/*                               SetOS()                                */
7616
/************************************************************************/
7617
7618
OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7619
                                  double dfScale, double dfFalseEasting,
7620
                                  double dfFalseNorthing)
7621
7622
19
{
7623
19
    TAKE_OPTIONAL_LOCK();
7624
7625
19
    return d->replaceConversionAndUnref(
7626
19
        proj_create_conversion_oblique_stereographic(
7627
19
            d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7628
19
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7629
19
}
7630
7631
/************************************************************************/
7632
/*                              OSRSetOS()                              */
7633
/************************************************************************/
7634
7635
OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7636
                double dfCMeridian, double dfScale, double dfFalseEasting,
7637
                double dfFalseNorthing)
7638
7639
0
{
7640
0
    VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7641
7642
0
    return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7643
0
                                  dfFalseEasting, dfFalseNorthing);
7644
0
}
7645
7646
/************************************************************************/
7647
/*                          SetOrthographic()                           */
7648
/************************************************************************/
7649
7650
OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7651
                                            double dfCenterLong,
7652
                                            double dfFalseEasting,
7653
                                            double dfFalseNorthing)
7654
7655
1
{
7656
1
    TAKE_OPTIONAL_LOCK();
7657
7658
1
    return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7659
1
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7660
1
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7661
1
}
7662
7663
/************************************************************************/
7664
/*                         OSRSetOrthographic()                         */
7665
/************************************************************************/
7666
7667
OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7668
                          double dfCenterLong, double dfFalseEasting,
7669
                          double dfFalseNorthing)
7670
7671
0
{
7672
0
    VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7673
7674
0
    return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7675
0
                                            dfFalseEasting, dfFalseNorthing);
7676
0
}
7677
7678
/************************************************************************/
7679
/*                            SetPolyconic()                            */
7680
/************************************************************************/
7681
7682
OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7683
                                         double dfCenterLong,
7684
                                         double dfFalseEasting,
7685
                                         double dfFalseNorthing)
7686
7687
2
{
7688
2
    TAKE_OPTIONAL_LOCK();
7689
7690
    // note: it seems that by some definitions this should include a
7691
    //       scale_factor parameter.
7692
2
    return d->replaceConversionAndUnref(
7693
2
        proj_create_conversion_american_polyconic(
7694
2
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7695
2
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7696
2
}
7697
7698
/************************************************************************/
7699
/*                          OSRSetPolyconic()                           */
7700
/************************************************************************/
7701
7702
OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7703
                       double dfCenterLong, double dfFalseEasting,
7704
                       double dfFalseNorthing)
7705
7706
0
{
7707
0
    VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7708
7709
0
    return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7710
0
                                         dfFalseEasting, dfFalseNorthing);
7711
0
}
7712
7713
/************************************************************************/
7714
/*                               SetPS()                                */
7715
/************************************************************************/
7716
7717
/** Sets a Polar Stereographic projection.
7718
 *
7719
 * Two variants are possible:
7720
 * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7721
 *   interpreted as the latitude of origin, combined with the scale factor
7722
 * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7723
 *   is interpreted as the latitude of true scale. In that situation, dfScale
7724
 *   must be set to 1 (it is ignored in the projection parameters)
7725
 */
7726
OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7727
                                  double dfScale, double dfFalseEasting,
7728
                                  double dfFalseNorthing)
7729
7730
23
{
7731
23
    TAKE_OPTIONAL_LOCK();
7732
7733
23
    PJ *conv;
7734
23
    if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7735
15
    {
7736
15
        conv = proj_create_conversion_polar_stereographic_variant_b(
7737
15
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7738
15
            dfFalseNorthing, nullptr, 0, nullptr, 0);
7739
15
    }
7740
8
    else
7741
8
    {
7742
8
        conv = proj_create_conversion_polar_stereographic_variant_a(
7743
8
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7744
8
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7745
8
    }
7746
7747
23
    const char *pszName = nullptr;
7748
23
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7749
23
    CPLString osName = pszName ? pszName : "";
7750
7751
23
    d->refreshProjObj();
7752
7753
23
    d->demoteFromBoundCRS();
7754
7755
23
    auto cs = proj_create_cartesian_2D_cs(
7756
23
        d->getPROJContext(),
7757
23
        dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7758
23
                        : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7759
23
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7760
23
    auto projCRS =
7761
23
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7762
23
                                  d->getGeodBaseCRS(), conv, cs);
7763
23
    proj_destroy(conv);
7764
23
    proj_destroy(cs);
7765
7766
23
    d->setPjCRS(projCRS);
7767
7768
23
    d->undoDemoteFromBoundCRS();
7769
7770
23
    return OGRERR_NONE;
7771
23
}
7772
7773
/************************************************************************/
7774
/*                              OSRSetPS()                              */
7775
/************************************************************************/
7776
7777
OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7778
                double dfCenterLong, double dfScale, double dfFalseEasting,
7779
                double dfFalseNorthing)
7780
7781
0
{
7782
0
    VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7783
7784
0
    return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7785
0
                                  dfFalseEasting, dfFalseNorthing);
7786
0
}
7787
7788
/************************************************************************/
7789
/*                            SetRobinson()                             */
7790
/************************************************************************/
7791
7792
OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7793
                                        double dfFalseEasting,
7794
                                        double dfFalseNorthing)
7795
7796
9
{
7797
9
    TAKE_OPTIONAL_LOCK();
7798
7799
9
    return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7800
9
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7801
9
        nullptr, 0, nullptr, 0));
7802
9
}
7803
7804
/************************************************************************/
7805
/*                           OSRSetRobinson()                           */
7806
/************************************************************************/
7807
7808
OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7809
                      double dfFalseEasting, double dfFalseNorthing)
7810
7811
0
{
7812
0
    VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7813
7814
0
    return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7815
0
                                        dfFalseNorthing);
7816
0
}
7817
7818
/************************************************************************/
7819
/*                           SetSinusoidal()                            */
7820
/************************************************************************/
7821
7822
OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7823
                                          double dfFalseEasting,
7824
                                          double dfFalseNorthing)
7825
7826
5
{
7827
5
    TAKE_OPTIONAL_LOCK();
7828
7829
5
    return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7830
5
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7831
5
        nullptr, 0, nullptr, 0));
7832
5
}
7833
7834
/************************************************************************/
7835
/*                          OSRSetSinusoidal()                          */
7836
/************************************************************************/
7837
7838
OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7839
                        double dfFalseEasting, double dfFalseNorthing)
7840
7841
0
{
7842
0
    VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7843
7844
0
    return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7845
0
                                          dfFalseNorthing);
7846
0
}
7847
7848
/************************************************************************/
7849
/*                          SetStereographic()                          */
7850
/************************************************************************/
7851
7852
OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7853
                                             double dfCMeridian, double dfScale,
7854
                                             double dfFalseEasting,
7855
                                             double dfFalseNorthing)
7856
7857
208
{
7858
208
    TAKE_OPTIONAL_LOCK();
7859
7860
208
    return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7861
208
        d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7862
208
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7863
208
}
7864
7865
/************************************************************************/
7866
/*                        OSRSetStereographic()                         */
7867
/************************************************************************/
7868
7869
OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7870
                           double dfCMeridian, double dfScale,
7871
                           double dfFalseEasting, double dfFalseNorthing)
7872
7873
0
{
7874
0
    VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7875
7876
0
    return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7877
0
                                             dfFalseEasting, dfFalseNorthing);
7878
0
}
7879
7880
/************************************************************************/
7881
/*                               SetSOC()                               */
7882
/*                                                                      */
7883
/*      NOTE: This definition isn't really used in practice any more    */
7884
/*      and should be considered deprecated.  It seems that swiss       */
7885
/*      oblique mercator is now define as Hotine_Oblique_Mercator       */
7886
/*      with an azimuth of 90 and a rectified_grid_angle of 90.  See    */
7887
/*      EPSG:2056 and Bug 423.                                          */
7888
/************************************************************************/
7889
7890
OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
7891
                                   double dfCentralMeridian,
7892
                                   double dfFalseEasting,
7893
                                   double dfFalseNorthing)
7894
7895
0
{
7896
0
    TAKE_OPTIONAL_LOCK();
7897
7898
0
    return d->replaceConversionAndUnref(
7899
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7900
0
            d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
7901
0
            90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7902
0
            0.0));
7903
#if 0
7904
    SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7905
    SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7906
    SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7907
    SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7908
    SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7909
7910
    return OGRERR_NONE;
7911
#endif
7912
0
}
7913
7914
/************************************************************************/
7915
/*                             OSRSetSOC()                              */
7916
/************************************************************************/
7917
7918
OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
7919
                 double dfCentralMeridian, double dfFalseEasting,
7920
                 double dfFalseNorthing)
7921
7922
0
{
7923
0
    VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
7924
7925
0
    return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
7926
0
                                   dfFalseEasting, dfFalseNorthing);
7927
0
}
7928
7929
/************************************************************************/
7930
/*                               SetVDG()                               */
7931
/************************************************************************/
7932
7933
OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
7934
                                   double dfFalseNorthing)
7935
7936
4
{
7937
4
    TAKE_OPTIONAL_LOCK();
7938
7939
4
    return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
7940
4
        d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
7941
4
        nullptr, 0, nullptr, 0));
7942
4
}
7943
7944
/************************************************************************/
7945
/*                             OSRSetVDG()                              */
7946
/************************************************************************/
7947
7948
OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7949
                 double dfFalseEasting, double dfFalseNorthing)
7950
7951
0
{
7952
0
    VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
7953
7954
0
    return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
7955
0
                                   dfFalseNorthing);
7956
0
}
7957
7958
/************************************************************************/
7959
/*                               SetUTM()                               */
7960
/************************************************************************/
7961
7962
/**
7963
 * \brief Set UTM projection definition.
7964
 *
7965
 * This will generate a projection definition with the full set of
7966
 * transverse mercator projection parameters for the given UTM zone.
7967
 * If no PROJCS[] description is set yet, one will be set to look
7968
 * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7969
 *
7970
 * This method is the same as the C function OSRSetUTM().
7971
 *
7972
 * @param nZone UTM zone.
7973
 *
7974
 * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7975
 * hemisphere.
7976
 *
7977
 * @return OGRERR_NONE on success.
7978
 */
7979
7980
OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
7981
7982
0
{
7983
0
    TAKE_OPTIONAL_LOCK();
7984
7985
0
    if (nZone < 0 || nZone > 60)
7986
0
    {
7987
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
7988
0
        return OGRERR_FAILURE;
7989
0
    }
7990
7991
0
    return d->replaceConversionAndUnref(
7992
0
        proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
7993
0
}
7994
7995
/************************************************************************/
7996
/*                             OSRSetUTM()                              */
7997
/************************************************************************/
7998
7999
/**
8000
 * \brief Set UTM projection definition.
8001
 *
8002
 * This is the same as the C++ method OGRSpatialReference::SetUTM()
8003
 */
8004
OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8005
8006
0
{
8007
0
    VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8008
8009
0
    return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8010
0
}
8011
8012
/************************************************************************/
8013
/*                             GetUTMZone()                             */
8014
/*                                                                      */
8015
/*      Returns zero if it isn't UTM.                                   */
8016
/************************************************************************/
8017
8018
/**
8019
 * \brief Get utm zone information.
8020
 *
8021
 * This is the same as the C function OSRGetUTMZone().
8022
 *
8023
 * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8024
 * zone which is negative in the southern hemisphere instead of having the
8025
 * pbNorth flag used in the C and C++ interface.
8026
 *
8027
 * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8028
 * FALSE if southern.
8029
 *
8030
 * @return UTM zone number or zero if this isn't a UTM definition.
8031
 */
8032
8033
int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8034
8035
0
{
8036
0
    TAKE_OPTIONAL_LOCK();
8037
8038
0
    if (IsProjected() && GetAxesCount() == 3)
8039
0
    {
8040
0
        OGRSpatialReference *poSRSTmp = Clone();
8041
0
        poSRSTmp->DemoteTo2D(nullptr);
8042
0
        const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8043
0
        delete poSRSTmp;
8044
0
        return nZone;
8045
0
    }
8046
8047
0
    const char *pszProjection = GetAttrValue("PROJECTION");
8048
8049
0
    if (pszProjection == nullptr ||
8050
0
        !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8051
0
        return 0;
8052
8053
0
    if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8054
0
        return 0;
8055
8056
0
    if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8057
0
        return 0;
8058
8059
0
    if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8060
0
        return 0;
8061
8062
0
    const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8063
8064
0
    if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8065
0
        return 0;
8066
8067
0
    if (pbNorth != nullptr)
8068
0
        *pbNorth = (dfFalseNorthing == 0);
8069
8070
0
    const double dfCentralMeridian =
8071
0
        GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8072
0
    const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8073
8074
0
    if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8075
0
        std::isnan(dfZone) ||
8076
0
        std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8077
0
        return 0;
8078
8079
0
    return static_cast<int>(dfZone);
8080
0
}
8081
8082
/************************************************************************/
8083
/*                           OSRGetUTMZone()                            */
8084
/************************************************************************/
8085
8086
/**
8087
 * \brief Get utm zone information.
8088
 *
8089
 * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8090
 */
8091
int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8092
8093
0
{
8094
0
    VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8095
8096
0
    return ToPointer(hSRS)->GetUTMZone(pbNorth);
8097
0
}
8098
8099
/************************************************************************/
8100
/*                             SetWagner()                              */
8101
/************************************************************************/
8102
8103
OGRErr OGRSpatialReference::SetWagner(int nVariation,  // 1--7.
8104
                                      double dfCenterLat, double dfFalseEasting,
8105
                                      double dfFalseNorthing)
8106
8107
0
{
8108
0
    TAKE_OPTIONAL_LOCK();
8109
8110
0
    PJ *conv;
8111
0
    if (nVariation == 1)
8112
0
    {
8113
0
        conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8114
0
                                               dfFalseEasting, dfFalseNorthing,
8115
0
                                               nullptr, 0.0, nullptr, 0.0);
8116
0
    }
8117
0
    else if (nVariation == 2)
8118
0
    {
8119
0
        conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8120
0
                                                dfFalseEasting, dfFalseNorthing,
8121
0
                                                nullptr, 0.0, nullptr, 0.0);
8122
0
    }
8123
0
    else if (nVariation == 3)
8124
0
    {
8125
0
        conv = proj_create_conversion_wagner_iii(
8126
0
            d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8127
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8128
0
    }
8129
0
    else if (nVariation == 4)
8130
0
    {
8131
0
        conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8132
0
                                                dfFalseEasting, dfFalseNorthing,
8133
0
                                                nullptr, 0.0, nullptr, 0.0);
8134
0
    }
8135
0
    else if (nVariation == 5)
8136
0
    {
8137
0
        conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8138
0
                                               dfFalseEasting, dfFalseNorthing,
8139
0
                                               nullptr, 0.0, nullptr, 0.0);
8140
0
    }
8141
0
    else if (nVariation == 6)
8142
0
    {
8143
0
        conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8144
0
                                                dfFalseEasting, dfFalseNorthing,
8145
0
                                                nullptr, 0.0, nullptr, 0.0);
8146
0
    }
8147
0
    else if (nVariation == 7)
8148
0
    {
8149
0
        conv = proj_create_conversion_wagner_vii(
8150
0
            d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8151
0
            0.0, nullptr, 0.0);
8152
0
    }
8153
0
    else
8154
0
    {
8155
0
        CPLError(CE_Failure, CPLE_AppDefined,
8156
0
                 "Unsupported Wagner variation (%d).", nVariation);
8157
0
        return OGRERR_UNSUPPORTED_SRS;
8158
0
    }
8159
8160
0
    return d->replaceConversionAndUnref(conv);
8161
0
}
8162
8163
/************************************************************************/
8164
/*                            OSRSetWagner()                            */
8165
/************************************************************************/
8166
8167
OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8168
                    double dfCenterLat, double dfFalseEasting,
8169
                    double dfFalseNorthing)
8170
8171
0
{
8172
0
    VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8173
8174
0
    return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8175
0
                                      dfFalseNorthing);
8176
0
}
8177
8178
/************************************************************************/
8179
/*                            SetQSC()                     */
8180
/************************************************************************/
8181
8182
OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8183
0
{
8184
0
    TAKE_OPTIONAL_LOCK();
8185
8186
0
    return d->replaceConversionAndUnref(
8187
0
        proj_create_conversion_quadrilateralized_spherical_cube(
8188
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8189
0
            0, nullptr, 0));
8190
0
}
8191
8192
/************************************************************************/
8193
/*                           OSRSetQSC()                   */
8194
/************************************************************************/
8195
8196
OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8197
                 double dfCenterLong)
8198
8199
0
{
8200
0
    VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8201
8202
0
    return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8203
0
}
8204
8205
/************************************************************************/
8206
/*                            SetSCH()                     */
8207
/************************************************************************/
8208
8209
OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8210
                                   double dfPegHeading, double dfPegHgt)
8211
8212
0
{
8213
0
    TAKE_OPTIONAL_LOCK();
8214
8215
0
    return d->replaceConversionAndUnref(
8216
0
        proj_create_conversion_spherical_cross_track_height(
8217
0
            d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8218
0
            nullptr, 0, nullptr, 0));
8219
0
}
8220
8221
/************************************************************************/
8222
/*                           OSRSetSCH()                   */
8223
/************************************************************************/
8224
8225
OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8226
                 double dfPegHeading, double dfPegHgt)
8227
8228
0
{
8229
0
    VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8230
8231
0
    return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8232
0
}
8233
8234
/************************************************************************/
8235
/*                         SetVerticalPerspective()                     */
8236
/************************************************************************/
8237
8238
OGRErr OGRSpatialReference::SetVerticalPerspective(
8239
    double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8240
    double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8241
0
{
8242
0
    TAKE_OPTIONAL_LOCK();
8243
8244
0
    return d->replaceConversionAndUnref(
8245
0
        proj_create_conversion_vertical_perspective(
8246
0
            d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8247
0
            dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8248
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
8249
0
}
8250
8251
/************************************************************************/
8252
/*                       OSRSetVerticalPerspective()                    */
8253
/************************************************************************/
8254
8255
OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8256
                                 double dfTopoOriginLat, double dfTopoOriginLon,
8257
                                 double dfTopoOriginHeight,
8258
                                 double dfViewPointHeight,
8259
                                 double dfFalseEasting, double dfFalseNorthing)
8260
8261
0
{
8262
0
    VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8263
8264
0
    return ToPointer(hSRS)->SetVerticalPerspective(
8265
0
        dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8266
0
        dfFalseEasting, dfFalseNorthing);
8267
0
}
8268
8269
/************************************************************************/
8270
/*             SetDerivedGeogCRSWithPoleRotationGRIBConvention()        */
8271
/************************************************************************/
8272
8273
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8274
    const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8275
    double dfAxisRotation)
8276
0
{
8277
0
    TAKE_OPTIONAL_LOCK();
8278
8279
0
    d->refreshProjObj();
8280
0
    if (!d->m_pj_crs)
8281
0
        return OGRERR_FAILURE;
8282
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8283
0
        return OGRERR_FAILURE;
8284
0
    auto ctxt = d->getPROJContext();
8285
0
    auto conv = proj_create_conversion_pole_rotation_grib_convention(
8286
0
        ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8287
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8288
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8289
0
                                                   d->m_pj_crs, conv, cs));
8290
0
    proj_destroy(conv);
8291
0
    proj_destroy(cs);
8292
0
    return OGRERR_NONE;
8293
0
}
8294
8295
/************************************************************************/
8296
/*         SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention()        */
8297
/************************************************************************/
8298
8299
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8300
    const char *pszCRSName, double dfGridNorthPoleLat,
8301
    double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8302
0
{
8303
0
    TAKE_OPTIONAL_LOCK();
8304
8305
0
#if PROJ_VERSION_MAJOR > 8 ||                                                  \
8306
0
    (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8307
0
    d->refreshProjObj();
8308
0
    if (!d->m_pj_crs)
8309
0
        return OGRERR_FAILURE;
8310
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8311
0
        return OGRERR_FAILURE;
8312
0
    auto ctxt = d->getPROJContext();
8313
0
    auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8314
0
        ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8315
0
        nullptr, 0);
8316
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8317
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8318
0
                                                   d->m_pj_crs, conv, cs));
8319
0
    proj_destroy(conv);
8320
0
    proj_destroy(cs);
8321
0
    return OGRERR_NONE;
8322
#else
8323
    (void)pszCRSName;
8324
    SetProjection("Rotated_pole");
8325
    SetExtension(
8326
        "PROJCS", "PROJ4",
8327
        CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8328
                   "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8329
                   "+to_meter=0.0174532925199433 "
8330
                   "+wktext",
8331
                   180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8332
                   dfGridNorthPoleLat, GetSemiMajor(nullptr),
8333
                   GetSemiMinor(nullptr)));
8334
    return OGRERR_NONE;
8335
#endif
8336
0
}
8337
8338
/************************************************************************/
8339
/*                            SetAuthority()                            */
8340
/************************************************************************/
8341
8342
/**
8343
 * \brief Set the authority for a node.
8344
 *
8345
 * This method is the same as the C function OSRSetAuthority().
8346
 *
8347
 * @param pszTargetKey the partial or complete path to the node to
8348
 * set an authority on.  i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8349
 *
8350
 * @param pszAuthority authority name, such as "EPSG".
8351
 *
8352
 * @param nCode code for value with this authority.
8353
 *
8354
 * @return OGRERR_NONE on success.
8355
 */
8356
8357
OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8358
                                         const char *pszAuthority, int nCode)
8359
8360
66.9k
{
8361
66.9k
    TAKE_OPTIONAL_LOCK();
8362
8363
66.9k
    d->refreshProjObj();
8364
66.9k
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8365
8366
66.9k
    if (pszTargetKey == nullptr)
8367
4.77k
    {
8368
4.77k
        if (!d->m_pj_crs)
8369
36
            return OGRERR_FAILURE;
8370
4.74k
        CPLString osCode;
8371
4.74k
        osCode.Printf("%d", nCode);
8372
4.74k
        d->demoteFromBoundCRS();
8373
4.74k
        d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8374
4.74k
                                  pszAuthority, osCode.c_str()));
8375
4.74k
        d->undoDemoteFromBoundCRS();
8376
4.74k
        return OGRERR_NONE;
8377
4.77k
    }
8378
8379
62.2k
    d->demoteFromBoundCRS();
8380
62.2k
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8381
10.9k
    {
8382
10.9k
        CPLString osCode;
8383
10.9k
        osCode.Printf("%d", nCode);
8384
10.9k
        auto newGeogCRS =
8385
10.9k
            proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8386
10.9k
                          pszAuthority, osCode.c_str());
8387
8388
10.9k
        auto conv =
8389
10.9k
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8390
8391
10.9k
        auto projCRS = proj_create_projected_crs(
8392
10.9k
            d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8393
10.9k
            d->getProjCRSCoordSys());
8394
8395
        // Preserve existing id on the PROJCRS
8396
10.9k
        const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8397
10.9k
        const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8398
10.9k
        if (pszProjCRSAuthName && pszProjCRSCode)
8399
0
        {
8400
0
            auto projCRSWithId =
8401
0
                proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8402
0
                              pszProjCRSCode);
8403
0
            proj_destroy(projCRS);
8404
0
            projCRS = projCRSWithId;
8405
0
        }
8406
8407
10.9k
        proj_destroy(newGeogCRS);
8408
10.9k
        proj_destroy(conv);
8409
8410
10.9k
        d->setPjCRS(projCRS);
8411
10.9k
        d->undoDemoteFromBoundCRS();
8412
10.9k
        return OGRERR_NONE;
8413
10.9k
    }
8414
51.2k
    d->undoDemoteFromBoundCRS();
8415
8416
    /* -------------------------------------------------------------------- */
8417
    /*      Find the node below which the authority should be put.          */
8418
    /* -------------------------------------------------------------------- */
8419
51.2k
    OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8420
8421
51.2k
    if (poNode == nullptr)
8422
1.25k
        return OGRERR_FAILURE;
8423
8424
    /* -------------------------------------------------------------------- */
8425
    /*      If there is an existing AUTHORITY child blow it away before     */
8426
    /*      trying to set a new one.                                        */
8427
    /* -------------------------------------------------------------------- */
8428
50.0k
    int iOldChild = poNode->FindChild("AUTHORITY");
8429
50.0k
    if (iOldChild != -1)
8430
0
        poNode->DestroyChild(iOldChild);
8431
8432
    /* -------------------------------------------------------------------- */
8433
    /*      Create a new authority node.                                    */
8434
    /* -------------------------------------------------------------------- */
8435
50.0k
    char szCode[32] = {};
8436
8437
50.0k
    snprintf(szCode, sizeof(szCode), "%d", nCode);
8438
8439
50.0k
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8440
50.0k
    poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8441
50.0k
    poAuthNode->AddChild(new OGR_SRSNode(szCode));
8442
8443
50.0k
    poNode->AddChild(poAuthNode);
8444
8445
50.0k
    return OGRERR_NONE;
8446
51.2k
}
8447
8448
/************************************************************************/
8449
/*                          OSRSetAuthority()                           */
8450
/************************************************************************/
8451
8452
/**
8453
 * \brief Set the authority for a node.
8454
 *
8455
 * This function is the same as OGRSpatialReference::SetAuthority().
8456
 */
8457
OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8458
                       const char *pszAuthority, int nCode)
8459
8460
0
{
8461
0
    VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8462
8463
0
    return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8464
0
}
8465
8466
/************************************************************************/
8467
/*                          GetAuthorityCode()                          */
8468
/************************************************************************/
8469
8470
/**
8471
 * \brief Get the authority code for a node.
8472
 *
8473
 * This method is used to query an AUTHORITY[] node from within the
8474
 * WKT tree, and fetch the code value.
8475
 *
8476
 * While in theory values may be non-numeric, for the EPSG authority all
8477
 * code values should be integral.
8478
 *
8479
 * This method is the same as the C function OSRGetAuthorityCode().
8480
 *
8481
 * @param pszTargetKey the partial or complete path to the node to
8482
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8483
 * search for an authority node on the root element.
8484
 *
8485
 * @return value code from authority node, or NULL on failure.  The value
8486
 * returned is internal and should not be freed or modified.
8487
 */
8488
8489
const char *
8490
OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8491
8492
14.3k
{
8493
14.3k
    TAKE_OPTIONAL_LOCK();
8494
8495
14.3k
    d->refreshProjObj();
8496
14.3k
    const char *pszInputTargetKey = pszTargetKey;
8497
14.3k
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8498
14.3k
    if (pszTargetKey == nullptr)
8499
14.0k
    {
8500
14.0k
        if (!d->m_pj_crs)
8501
0
        {
8502
0
            return nullptr;
8503
0
        }
8504
14.0k
        d->demoteFromBoundCRS();
8505
14.0k
        auto ret = proj_get_id_code(d->m_pj_crs, 0);
8506
14.0k
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8507
889
        {
8508
889
            auto ctxt = d->getPROJContext();
8509
889
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8510
889
            if (cs)
8511
889
            {
8512
889
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8513
889
                proj_destroy(cs);
8514
889
                if (axisCount == 3)
8515
0
                {
8516
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8517
                    // 2002 in which case, using the WKT1 representation will
8518
                    // enable us to recover the EPSG code.
8519
0
                    pszTargetKey = pszInputTargetKey;
8520
0
                }
8521
889
            }
8522
889
        }
8523
14.0k
        d->undoDemoteFromBoundCRS();
8524
14.0k
        if (ret != nullptr || pszTargetKey == nullptr)
8525
14.0k
        {
8526
14.0k
            return ret;
8527
14.0k
        }
8528
14.0k
    }
8529
8530
    // Special key for that context
8531
302
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8532
302
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8533
0
    {
8534
0
        auto ctxt = d->getPROJContext();
8535
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8536
0
        if (crs)
8537
0
        {
8538
0
            const char *ret = proj_get_id_code(crs, 0);
8539
0
            if (ret)
8540
0
                ret = CPLSPrintf("%s", ret);
8541
0
            proj_destroy(crs);
8542
0
            return ret;
8543
0
        }
8544
0
    }
8545
302
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8546
302
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8547
0
    {
8548
0
        auto ctxt = d->getPROJContext();
8549
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8550
0
        if (crs)
8551
0
        {
8552
0
            const char *ret = proj_get_id_code(crs, 0);
8553
0
            if (ret)
8554
0
                ret = CPLSPrintf("%s", ret);
8555
0
            proj_destroy(crs);
8556
0
            return ret;
8557
0
        }
8558
0
    }
8559
8560
    /* -------------------------------------------------------------------- */
8561
    /*      Find the node below which the authority should be put.          */
8562
    /* -------------------------------------------------------------------- */
8563
302
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8564
8565
302
    if (poNode == nullptr)
8566
1
        return nullptr;
8567
8568
    /* -------------------------------------------------------------------- */
8569
    /*      Fetch AUTHORITY child if there is one.                          */
8570
    /* -------------------------------------------------------------------- */
8571
301
    if (poNode->FindChild("AUTHORITY") == -1)
8572
15
        return nullptr;
8573
8574
286
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8575
8576
    /* -------------------------------------------------------------------- */
8577
    /*      Create a new authority node.                                    */
8578
    /* -------------------------------------------------------------------- */
8579
286
    if (poNode->GetChildCount() < 2)
8580
0
        return nullptr;
8581
8582
286
    return poNode->GetChild(1)->GetValue();
8583
286
}
8584
8585
/************************************************************************/
8586
/*                          OSRGetAuthorityCode()                       */
8587
/************************************************************************/
8588
8589
/**
8590
 * \brief Get the authority code for a node.
8591
 *
8592
 * This function is the same as OGRSpatialReference::GetAuthorityCode().
8593
 */
8594
const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8595
                                const char *pszTargetKey)
8596
8597
0
{
8598
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8599
8600
0
    return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8601
0
}
8602
8603
/************************************************************************/
8604
/*                          GetAuthorityName()                          */
8605
/************************************************************************/
8606
8607
/**
8608
 * \brief Get the authority name for a node.
8609
 *
8610
 * This method is used to query an AUTHORITY[] node from within the
8611
 * WKT tree, and fetch the authority name value.
8612
 *
8613
 * The most common authority is "EPSG".
8614
 *
8615
 * This method is the same as the C function OSRGetAuthorityName().
8616
 *
8617
 * @param pszTargetKey the partial or complete path to the node to
8618
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8619
 * search for an authority node on the root element.
8620
 *
8621
 * @return value code from authority node, or NULL on failure. The value
8622
 * returned is internal and should not be freed or modified.
8623
 */
8624
8625
const char *
8626
OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8627
8628
280
{
8629
280
    TAKE_OPTIONAL_LOCK();
8630
8631
280
    d->refreshProjObj();
8632
280
    const char *pszInputTargetKey = pszTargetKey;
8633
280
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8634
280
    if (pszTargetKey == nullptr)
8635
0
    {
8636
0
        if (!d->m_pj_crs)
8637
0
        {
8638
0
            return nullptr;
8639
0
        }
8640
0
        d->demoteFromBoundCRS();
8641
0
        auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8642
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8643
0
        {
8644
0
            auto ctxt = d->getPROJContext();
8645
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8646
0
            if (cs)
8647
0
            {
8648
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8649
0
                proj_destroy(cs);
8650
0
                if (axisCount == 3)
8651
0
                {
8652
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8653
                    // 2002 in which case, using the WKT1 representation will
8654
                    // enable us to recover the EPSG code.
8655
0
                    pszTargetKey = pszInputTargetKey;
8656
0
                }
8657
0
            }
8658
0
        }
8659
0
        d->undoDemoteFromBoundCRS();
8660
0
        if (ret != nullptr || pszTargetKey == nullptr)
8661
0
        {
8662
0
            return ret;
8663
0
        }
8664
0
    }
8665
8666
    // Special key for that context
8667
280
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8668
280
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8669
0
    {
8670
0
        auto ctxt = d->getPROJContext();
8671
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8672
0
        if (crs)
8673
0
        {
8674
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8675
0
            if (ret)
8676
0
                ret = CPLSPrintf("%s", ret);
8677
0
            proj_destroy(crs);
8678
0
            return ret;
8679
0
        }
8680
0
    }
8681
280
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8682
280
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8683
0
    {
8684
0
        auto ctxt = d->getPROJContext();
8685
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8686
0
        if (crs)
8687
0
        {
8688
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8689
0
            if (ret)
8690
0
                ret = CPLSPrintf("%s", ret);
8691
0
            proj_destroy(crs);
8692
0
            return ret;
8693
0
        }
8694
0
    }
8695
8696
    /* -------------------------------------------------------------------- */
8697
    /*      Find the node below which the authority should be put.          */
8698
    /* -------------------------------------------------------------------- */
8699
280
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8700
8701
280
    if (poNode == nullptr)
8702
1
        return nullptr;
8703
8704
    /* -------------------------------------------------------------------- */
8705
    /*      Fetch AUTHORITY child if there is one.                          */
8706
    /* -------------------------------------------------------------------- */
8707
279
    if (poNode->FindChild("AUTHORITY") == -1)
8708
15
        return nullptr;
8709
8710
264
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8711
8712
    /* -------------------------------------------------------------------- */
8713
    /*      Create a new authority node.                                    */
8714
    /* -------------------------------------------------------------------- */
8715
264
    if (poNode->GetChildCount() < 2)
8716
0
        return nullptr;
8717
8718
264
    return poNode->GetChild(0)->GetValue();
8719
264
}
8720
8721
/************************************************************************/
8722
/*                        OSRGetAuthorityName()                         */
8723
/************************************************************************/
8724
8725
/**
8726
 * \brief Get the authority name for a node.
8727
 *
8728
 * This function is the same as OGRSpatialReference::GetAuthorityName().
8729
 */
8730
const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8731
                                const char *pszTargetKey)
8732
8733
0
{
8734
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8735
8736
0
    return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8737
0
}
8738
8739
/************************************************************************/
8740
/*                          GetOGCURN()                                 */
8741
/************************************************************************/
8742
8743
/**
8744
 * \brief Get a OGC URN string describing the CRS, when possible
8745
 *
8746
 * This method assumes that the CRS has a top-level identifier, or is
8747
 * a compound CRS whose horizontal and vertical parts have a top-level
8748
 * identifier.
8749
 *
8750
 * @return a string to free with CPLFree(), or nullptr when no result can be
8751
 * generated
8752
 *
8753
 * @since GDAL 3.5
8754
 */
8755
8756
char *OGRSpatialReference::GetOGCURN() const
8757
8758
0
{
8759
0
    TAKE_OPTIONAL_LOCK();
8760
8761
0
    const char *pszAuthName = GetAuthorityName(nullptr);
8762
0
    const char *pszAuthCode = GetAuthorityCode(nullptr);
8763
0
    if (pszAuthName && pszAuthCode)
8764
0
        return CPLStrdup(
8765
0
            CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8766
0
    if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8767
0
        return nullptr;
8768
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8769
0
    auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8770
0
    char *pszRet = nullptr;
8771
0
    if (horizCRS && vertCRS)
8772
0
    {
8773
0
        auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8774
0
        auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8775
0
        auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8776
0
        auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8777
0
        if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8778
0
        {
8779
0
            pszRet = CPLStrdup(CPLSPrintf(
8780
0
                "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8781
0
                horizAuthCode, vertAuthName, vertAuthCode));
8782
0
        }
8783
0
    }
8784
0
    proj_destroy(horizCRS);
8785
0
    proj_destroy(vertCRS);
8786
0
    return pszRet;
8787
0
}
8788
8789
/************************************************************************/
8790
/*                           StripVertical()                            */
8791
/************************************************************************/
8792
8793
/**
8794
 * \brief Convert a compound cs into a horizontal CS.
8795
 *
8796
 * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8797
 * nodes are stripped resulting and only the horizontal coordinate system
8798
 * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8799
 *
8800
 * If this is not a compound coordinate system then nothing is changed.
8801
 *
8802
 * This method is the same as the C function OSRStripVertical().
8803
 *
8804
 * @since OGR 1.8.0
8805
 */
8806
8807
OGRErr OGRSpatialReference::StripVertical()
8808
8809
4.14k
{
8810
4.14k
    TAKE_OPTIONAL_LOCK();
8811
8812
4.14k
    d->refreshProjObj();
8813
4.14k
    d->demoteFromBoundCRS();
8814
4.14k
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8815
0
    {
8816
0
        d->undoDemoteFromBoundCRS();
8817
0
        return OGRERR_NONE;
8818
0
    }
8819
4.14k
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8820
4.14k
    if (!horizCRS)
8821
0
    {
8822
0
        d->undoDemoteFromBoundCRS();
8823
0
        return OGRERR_FAILURE;
8824
0
    }
8825
8826
4.14k
    bool reuseExistingBoundCRS = false;
8827
4.14k
    if (d->m_pj_bound_crs_target)
8828
10
    {
8829
10
        auto type = proj_get_type(d->m_pj_bound_crs_target);
8830
10
        reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8831
10
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8832
10
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8833
10
    }
8834
8835
4.14k
    if (reuseExistingBoundCRS)
8836
10
    {
8837
10
        auto newBoundCRS = proj_crs_create_bound_crs(
8838
10
            d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8839
10
            d->m_pj_bound_crs_co);
8840
10
        proj_destroy(horizCRS);
8841
10
        d->undoDemoteFromBoundCRS();
8842
10
        d->setPjCRS(newBoundCRS);
8843
10
    }
8844
4.13k
    else
8845
4.13k
    {
8846
4.13k
        d->undoDemoteFromBoundCRS();
8847
4.13k
        d->setPjCRS(horizCRS);
8848
4.13k
    }
8849
8850
4.14k
    return OGRERR_NONE;
8851
4.14k
}
8852
8853
/************************************************************************/
8854
/*                            OSRStripVertical()                             */
8855
/************************************************************************/
8856
/**
8857
 * \brief Convert a compound cs into a horizontal CS.
8858
 *
8859
 * This function is the same as the C++ method
8860
 * OGRSpatialReference::StripVertical().
8861
 */
8862
OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8863
8864
0
{
8865
0
    VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8866
8867
0
    return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8868
0
}
8869
8870
/************************************************************************/
8871
/*                   StripTOWGS84IfKnownDatumAndAllowed()               */
8872
/************************************************************************/
8873
8874
/**
8875
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8876
 *        and this is allowed by the user.
8877
 *
8878
 * The default behavior is to remove TOWGS84 information if the CRS has a
8879
 * known horizontal datum. This can be disabled by setting the
8880
 * OSR_STRIP_TOWGS84 configuration option to NO.
8881
 *
8882
 * @return true if TOWGS84 has been removed.
8883
 * @since OGR 3.1.0
8884
 */
8885
8886
bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8887
28.9k
{
8888
28.9k
    if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8889
28.9k
    {
8890
28.9k
        if (StripTOWGS84IfKnownDatum())
8891
426
        {
8892
426
            CPLDebug("OSR", "TOWGS84 information has been removed. "
8893
426
                            "It can be kept by setting the OSR_STRIP_TOWGS84 "
8894
426
                            "configuration option to NO");
8895
426
            return true;
8896
426
        }
8897
28.9k
    }
8898
28.5k
    return false;
8899
28.9k
}
8900
8901
/************************************************************************/
8902
/*                      StripTOWGS84IfKnownDatum()                      */
8903
/************************************************************************/
8904
8905
/**
8906
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8907
 *
8908
 * @return true if TOWGS84 has been removed.
8909
 * @since OGR 3.1.0
8910
 */
8911
8912
bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8913
8914
28.9k
{
8915
28.9k
    TAKE_OPTIONAL_LOCK();
8916
8917
28.9k
    d->refreshProjObj();
8918
28.9k
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
8919
28.2k
    {
8920
28.2k
        return false;
8921
28.2k
    }
8922
756
    auto ctxt = d->getPROJContext();
8923
756
    auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8924
756
    if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
8925
27
    {
8926
27
        proj_destroy(baseCRS);
8927
27
        return false;
8928
27
    }
8929
8930
    // Known base CRS code ? Return base CRS
8931
729
    const char *pszCode = proj_get_id_code(baseCRS, 0);
8932
729
    if (pszCode)
8933
386
    {
8934
386
        d->setPjCRS(baseCRS);
8935
386
        return true;
8936
386
    }
8937
8938
343
    auto datum = proj_crs_get_datum(ctxt, baseCRS);
8939
343
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
8940
343
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8941
343
    if (datum == nullptr)
8942
0
    {
8943
0
        datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8944
0
    }
8945
343
#endif
8946
343
    if (!datum)
8947
0
    {
8948
0
        proj_destroy(baseCRS);
8949
0
        return false;
8950
0
    }
8951
8952
    // Known datum code ? Return base CRS
8953
343
    pszCode = proj_get_id_code(datum, 0);
8954
343
    if (pszCode)
8955
20
    {
8956
20
        proj_destroy(datum);
8957
20
        d->setPjCRS(baseCRS);
8958
20
        return true;
8959
20
    }
8960
8961
323
    const char *name = proj_get_name(datum);
8962
323
    if (EQUAL(name, "unknown"))
8963
0
    {
8964
0
        proj_destroy(datum);
8965
0
        proj_destroy(baseCRS);
8966
0
        return false;
8967
0
    }
8968
323
    const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8969
323
    PJ_OBJ_LIST *list =
8970
323
        proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
8971
8972
323
    bool knownDatumName = false;
8973
323
    if (list)
8974
323
    {
8975
323
        if (proj_list_get_count(list) == 1)
8976
20
        {
8977
20
            knownDatumName = true;
8978
20
        }
8979
323
        proj_list_destroy(list);
8980
323
    }
8981
8982
323
    proj_destroy(datum);
8983
323
    if (knownDatumName)
8984
20
    {
8985
20
        d->setPjCRS(baseCRS);
8986
20
        return true;
8987
20
    }
8988
303
    proj_destroy(baseCRS);
8989
303
    return false;
8990
323
}
8991
8992
/************************************************************************/
8993
/*                             IsCompound()                             */
8994
/************************************************************************/
8995
8996
/**
8997
 * \brief Check if coordinate system is compound.
8998
 *
8999
 * This method is the same as the C function OSRIsCompound().
9000
 *
9001
 * @return TRUE if this is rooted with a COMPD_CS node.
9002
 */
9003
9004
int OGRSpatialReference::IsCompound() const
9005
9006
47.3k
{
9007
47.3k
    TAKE_OPTIONAL_LOCK();
9008
9009
47.3k
    d->refreshProjObj();
9010
47.3k
    d->demoteFromBoundCRS();
9011
47.3k
    bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9012
47.3k
    d->undoDemoteFromBoundCRS();
9013
47.3k
    return isCompound;
9014
47.3k
}
9015
9016
/************************************************************************/
9017
/*                           OSRIsCompound()                            */
9018
/************************************************************************/
9019
9020
/**
9021
 * \brief Check if the coordinate system is compound.
9022
 *
9023
 * This function is the same as OGRSpatialReference::IsCompound().
9024
 */
9025
int OSRIsCompound(OGRSpatialReferenceH hSRS)
9026
9027
0
{
9028
0
    VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9029
9030
0
    return ToPointer(hSRS)->IsCompound();
9031
0
}
9032
9033
/************************************************************************/
9034
/*                            IsProjected()                             */
9035
/************************************************************************/
9036
9037
/**
9038
 * \brief Check if projected coordinate system.
9039
 *
9040
 * This method is the same as the C function OSRIsProjected().
9041
 *
9042
 * @return TRUE if this contains a PROJCS node indicating a it is a
9043
 * projected coordinate system. Also if it is a CompoundCRS made of a
9044
 * ProjectedCRS
9045
 */
9046
9047
int OGRSpatialReference::IsProjected() const
9048
9049
72.9k
{
9050
72.9k
    TAKE_OPTIONAL_LOCK();
9051
9052
72.9k
    d->refreshProjObj();
9053
72.9k
    d->demoteFromBoundCRS();
9054
72.9k
    bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9055
72.9k
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9056
8.05k
    {
9057
8.05k
        auto horizCRS =
9058
8.05k
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9059
8.05k
        if (horizCRS)
9060
8.05k
        {
9061
8.05k
            auto horizCRSType = proj_get_type(horizCRS);
9062
8.05k
            isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9063
8.05k
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9064
6
            {
9065
6
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9066
6
                if (base)
9067
6
                {
9068
6
                    isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9069
6
                    proj_destroy(base);
9070
6
                }
9071
6
            }
9072
8.05k
            proj_destroy(horizCRS);
9073
8.05k
        }
9074
8.05k
    }
9075
72.9k
    d->undoDemoteFromBoundCRS();
9076
72.9k
    return isProjected;
9077
72.9k
}
9078
9079
/************************************************************************/
9080
/*                           OSRIsProjected()                           */
9081
/************************************************************************/
9082
/**
9083
 * \brief Check if projected coordinate system.
9084
 *
9085
 * This function is the same as OGRSpatialReference::IsProjected().
9086
 */
9087
int OSRIsProjected(OGRSpatialReferenceH hSRS)
9088
9089
0
{
9090
0
    VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9091
9092
0
    return ToPointer(hSRS)->IsProjected();
9093
0
}
9094
9095
/************************************************************************/
9096
/*                            IsGeocentric()                            */
9097
/************************************************************************/
9098
9099
/**
9100
 * \brief Check if geocentric coordinate system.
9101
 *
9102
 * This method is the same as the C function OSRIsGeocentric().
9103
 *
9104
 * @return TRUE if this contains a GEOCCS node indicating a it is a
9105
 * geocentric coordinate system.
9106
 *
9107
 * @since OGR 1.9.0
9108
 */
9109
9110
int OGRSpatialReference::IsGeocentric() const
9111
9112
32.0k
{
9113
32.0k
    TAKE_OPTIONAL_LOCK();
9114
9115
32.0k
    d->refreshProjObj();
9116
32.0k
    d->demoteFromBoundCRS();
9117
32.0k
    bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9118
32.0k
    d->undoDemoteFromBoundCRS();
9119
32.0k
    return isGeocentric;
9120
32.0k
}
9121
9122
/************************************************************************/
9123
/*                           OSRIsGeocentric()                          */
9124
/************************************************************************/
9125
/**
9126
 * \brief Check if geocentric coordinate system.
9127
 *
9128
 * This function is the same as OGRSpatialReference::IsGeocentric().
9129
 *
9130
 * @since OGR 1.9.0
9131
 */
9132
int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9133
9134
0
{
9135
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9136
9137
0
    return ToPointer(hSRS)->IsGeocentric();
9138
0
}
9139
9140
/************************************************************************/
9141
/*                            IsEmpty()                                 */
9142
/************************************************************************/
9143
9144
/**
9145
 * \brief Return if the SRS is not set.
9146
 */
9147
9148
bool OGRSpatialReference::IsEmpty() const
9149
104k
{
9150
104k
    TAKE_OPTIONAL_LOCK();
9151
9152
104k
    d->refreshProjObj();
9153
104k
    return d->m_pj_crs == nullptr;
9154
104k
}
9155
9156
/************************************************************************/
9157
/*                            IsGeographic()                            */
9158
/************************************************************************/
9159
9160
/**
9161
 * \brief Check if geographic coordinate system.
9162
 *
9163
 * This method is the same as the C function OSRIsGeographic().
9164
 *
9165
 * @return TRUE if this spatial reference is geographic ... that is the
9166
 * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9167
 * GeographicCRS
9168
 */
9169
9170
int OGRSpatialReference::IsGeographic() const
9171
9172
47.2k
{
9173
47.2k
    TAKE_OPTIONAL_LOCK();
9174
9175
47.2k
    d->refreshProjObj();
9176
47.2k
    d->demoteFromBoundCRS();
9177
47.2k
    bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9178
47.2k
                  d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9179
47.2k
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9180
9.86k
    {
9181
9.86k
        auto horizCRS =
9182
9.86k
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9183
9.86k
        if (horizCRS)
9184
9.86k
        {
9185
9.86k
            auto horizCRSType = proj_get_type(horizCRS);
9186
9.86k
            isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9187
9.86k
                     horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9188
9.86k
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9189
6
            {
9190
6
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9191
6
                if (base)
9192
6
                {
9193
6
                    horizCRSType = proj_get_type(base);
9194
6
                    isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9195
6
                             horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9196
6
                    proj_destroy(base);
9197
6
                }
9198
6
            }
9199
9.86k
            proj_destroy(horizCRS);
9200
9.86k
        }
9201
9.86k
    }
9202
47.2k
    d->undoDemoteFromBoundCRS();
9203
47.2k
    return isGeog;
9204
47.2k
}
9205
9206
/************************************************************************/
9207
/*                          OSRIsGeographic()                           */
9208
/************************************************************************/
9209
/**
9210
 * \brief Check if geographic coordinate system.
9211
 *
9212
 * This function is the same as OGRSpatialReference::IsGeographic().
9213
 */
9214
int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9215
9216
0
{
9217
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9218
9219
0
    return ToPointer(hSRS)->IsGeographic();
9220
0
}
9221
9222
/************************************************************************/
9223
/*                      IsDerivedGeographic()                           */
9224
/************************************************************************/
9225
9226
/**
9227
 * \brief Check if the CRS is a derived geographic coordinate system.
9228
 * (for example a rotated long/lat grid)
9229
 *
9230
 * This method is the same as the C function OSRIsDerivedGeographic().
9231
 *
9232
 * @since GDAL 3.1.0 and PROJ 6.3.0
9233
 */
9234
9235
int OGRSpatialReference::IsDerivedGeographic() const
9236
9237
29.1k
{
9238
29.1k
    TAKE_OPTIONAL_LOCK();
9239
9240
29.1k
    d->refreshProjObj();
9241
29.1k
    d->demoteFromBoundCRS();
9242
29.1k
    const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9243
29.1k
                        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9244
29.1k
    const bool isDerivedGeographic =
9245
29.1k
        isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9246
29.1k
    d->undoDemoteFromBoundCRS();
9247
29.1k
    return isDerivedGeographic ? TRUE : FALSE;
9248
29.1k
}
9249
9250
/************************************************************************/
9251
/*                      OSRIsDerivedGeographic()                        */
9252
/************************************************************************/
9253
/**
9254
 * \brief Check if the CRS is a derived geographic coordinate system.
9255
 * (for example a rotated long/lat grid)
9256
 *
9257
 * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9258
 */
9259
int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9260
9261
0
{
9262
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9263
9264
0
    return ToPointer(hSRS)->IsDerivedGeographic();
9265
0
}
9266
9267
/************************************************************************/
9268
/*                      IsDerivedProjected()                            */
9269
/************************************************************************/
9270
9271
/**
9272
 * \brief Check if the CRS is a derived projected coordinate system.
9273
 *
9274
 * This method is the same as the C function OSRIsDerivedGeographic().
9275
 *
9276
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9277
 */
9278
9279
int OGRSpatialReference::IsDerivedProjected() const
9280
9281
0
{
9282
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
9283
0
    TAKE_OPTIONAL_LOCK();
9284
0
    d->refreshProjObj();
9285
0
    d->demoteFromBoundCRS();
9286
0
    const bool isDerivedProjected =
9287
0
        d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9288
0
    d->undoDemoteFromBoundCRS();
9289
0
    return isDerivedProjected ? TRUE : FALSE;
9290
#else
9291
    return FALSE;
9292
#endif
9293
0
}
9294
9295
/************************************************************************/
9296
/*                      OSRIsDerivedProjected()                         */
9297
/************************************************************************/
9298
/**
9299
 * \brief Check if the CRS is a derived projected coordinate system.
9300
 *
9301
 * This function is the same as OGRSpatialReference::IsDerivedProjected().
9302
 *
9303
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9304
 */
9305
int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9306
9307
0
{
9308
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9309
9310
0
    return ToPointer(hSRS)->IsDerivedProjected();
9311
0
}
9312
9313
/************************************************************************/
9314
/*                              IsLocal()                               */
9315
/************************************************************************/
9316
9317
/**
9318
 * \brief Check if local coordinate system.
9319
 *
9320
 * This method is the same as the C function OSRIsLocal().
9321
 *
9322
 * @return TRUE if this spatial reference is local ... that is the
9323
 * root is a LOCAL_CS node.
9324
 */
9325
9326
int OGRSpatialReference::IsLocal() const
9327
9328
3.80k
{
9329
3.80k
    TAKE_OPTIONAL_LOCK();
9330
3.80k
    d->refreshProjObj();
9331
3.80k
    return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9332
3.80k
}
9333
9334
/************************************************************************/
9335
/*                          OSRIsLocal()                                */
9336
/************************************************************************/
9337
/**
9338
 * \brief Check if local coordinate system.
9339
 *
9340
 * This function is the same as OGRSpatialReference::IsLocal().
9341
 */
9342
int OSRIsLocal(OGRSpatialReferenceH hSRS)
9343
9344
0
{
9345
0
    VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9346
9347
0
    return ToPointer(hSRS)->IsLocal();
9348
0
}
9349
9350
/************************************************************************/
9351
/*                            IsVertical()                              */
9352
/************************************************************************/
9353
9354
/**
9355
 * \brief Check if vertical coordinate system.
9356
 *
9357
 * This method is the same as the C function OSRIsVertical().
9358
 *
9359
 * @return TRUE if this contains a VERT_CS node indicating a it is a
9360
 * vertical coordinate system. Also if it is a CompoundCRS made of a
9361
 * VerticalCRS
9362
 *
9363
 * @since OGR 1.8.0
9364
 */
9365
9366
int OGRSpatialReference::IsVertical() const
9367
9368
3.77k
{
9369
3.77k
    TAKE_OPTIONAL_LOCK();
9370
3.77k
    d->refreshProjObj();
9371
3.77k
    d->demoteFromBoundCRS();
9372
3.77k
    bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9373
3.77k
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9374
36
    {
9375
36
        auto vertCRS =
9376
36
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9377
36
        if (vertCRS)
9378
36
        {
9379
36
            const auto vertCRSType = proj_get_type(vertCRS);
9380
36
            isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9381
36
            if (vertCRSType == PJ_TYPE_BOUND_CRS)
9382
0
            {
9383
0
                auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9384
0
                if (base)
9385
0
                {
9386
0
                    isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9387
0
                    proj_destroy(base);
9388
0
                }
9389
0
            }
9390
36
            proj_destroy(vertCRS);
9391
36
        }
9392
36
    }
9393
3.77k
    d->undoDemoteFromBoundCRS();
9394
3.77k
    return isVertical;
9395
3.77k
}
9396
9397
/************************************************************************/
9398
/*                           OSRIsVertical()                            */
9399
/************************************************************************/
9400
/**
9401
 * \brief Check if vertical coordinate system.
9402
 *
9403
 * This function is the same as OGRSpatialReference::IsVertical().
9404
 *
9405
 * @since OGR 1.8.0
9406
 */
9407
int OSRIsVertical(OGRSpatialReferenceH hSRS)
9408
9409
0
{
9410
0
    VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9411
9412
0
    return ToPointer(hSRS)->IsVertical();
9413
0
}
9414
9415
/************************************************************************/
9416
/*                            IsDynamic()                               */
9417
/************************************************************************/
9418
9419
/**
9420
 * \brief Check if a CRS is a dynamic CRS.
9421
 *
9422
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9423
 * plate-fixed.
9424
 *
9425
 * This method is the same as the C function OSRIsDynamic().
9426
 *
9427
 * @return true if the CRS is dynamic
9428
 *
9429
 * @since OGR 3.4.0
9430
 *
9431
 * @see HasPointMotionOperation()
9432
 */
9433
9434
bool OGRSpatialReference::IsDynamic() const
9435
9436
11.5k
{
9437
11.5k
    TAKE_OPTIONAL_LOCK();
9438
11.5k
    bool isDynamic = false;
9439
11.5k
    d->refreshProjObj();
9440
11.5k
    d->demoteFromBoundCRS();
9441
11.5k
    auto ctxt = d->getPROJContext();
9442
11.5k
    PJ *horiz = nullptr;
9443
11.5k
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9444
66
    {
9445
66
        horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9446
66
    }
9447
11.4k
    else if (d->m_pj_crs)
9448
11.1k
    {
9449
11.1k
        horiz = proj_clone(ctxt, d->m_pj_crs);
9450
11.1k
    }
9451
11.5k
    if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9452
0
    {
9453
0
        auto baseCRS = proj_get_source_crs(ctxt, horiz);
9454
0
        if (baseCRS)
9455
0
        {
9456
0
            proj_destroy(horiz);
9457
0
            horiz = baseCRS;
9458
0
        }
9459
0
    }
9460
11.5k
    auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9461
11.5k
    if (datum)
9462
10.9k
    {
9463
10.9k
        const auto type = proj_get_type(datum);
9464
10.9k
        isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9465
10.9k
                    type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9466
10.9k
        if (!isDynamic)
9467
5.65k
        {
9468
5.65k
            const char *auth_name = proj_get_id_auth_name(datum, 0);
9469
5.65k
            const char *code = proj_get_id_code(datum, 0);
9470
5.65k
            if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9471
5.65k
                EQUAL(code, "6326"))
9472
0
            {
9473
0
                isDynamic = true;
9474
0
            }
9475
5.65k
        }
9476
10.9k
        proj_destroy(datum);
9477
10.9k
    }
9478
536
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9479
536
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9480
536
    else
9481
536
    {
9482
536
        auto ensemble =
9483
536
            horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9484
536
        if (ensemble)
9485
266
        {
9486
266
            auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9487
266
            if (member)
9488
266
            {
9489
266
                const auto type = proj_get_type(member);
9490
266
                isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9491
266
                            type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9492
266
                proj_destroy(member);
9493
266
            }
9494
266
            proj_destroy(ensemble);
9495
266
        }
9496
536
    }
9497
11.5k
#endif
9498
11.5k
    proj_destroy(horiz);
9499
11.5k
    d->undoDemoteFromBoundCRS();
9500
11.5k
    return isDynamic;
9501
11.5k
}
9502
9503
/************************************************************************/
9504
/*                           OSRIsDynamic()                             */
9505
/************************************************************************/
9506
/**
9507
 * \brief Check if a CRS is a dynamic CRS.
9508
 *
9509
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9510
 * plate-fixed.
9511
 *
9512
 * This function is the same as OGRSpatialReference::IsDynamic().
9513
 *
9514
 * @since OGR 3.4.0
9515
 */
9516
int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9517
9518
0
{
9519
0
    VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9520
9521
0
    return ToPointer(hSRS)->IsDynamic();
9522
0
}
9523
9524
/************************************************************************/
9525
/*                         HasPointMotionOperation()                    */
9526
/************************************************************************/
9527
9528
/**
9529
 * \brief Check if a CRS has at least an associated point motion operation.
9530
 *
9531
 * Some CRS are not formally declared as dynamic, but may behave as such
9532
 * in practice due to the presence of point motion operation, to perform
9533
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9534
 *
9535
 * @return true if the CRS has at least an associated point motion operation.
9536
 *
9537
 * @since OGR 3.8.0 and PROJ 9.4.0
9538
 *
9539
 * @see IsDynamic()
9540
 */
9541
9542
bool OGRSpatialReference::HasPointMotionOperation() const
9543
9544
0
{
9545
0
#if PROJ_VERSION_MAJOR > 9 ||                                                  \
9546
0
    (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9547
0
    TAKE_OPTIONAL_LOCK();
9548
0
    d->refreshProjObj();
9549
0
    d->demoteFromBoundCRS();
9550
0
    auto ctxt = d->getPROJContext();
9551
0
    auto res =
9552
0
        CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9553
0
    d->undoDemoteFromBoundCRS();
9554
0
    return res;
9555
#else
9556
    return false;
9557
#endif
9558
0
}
9559
9560
/************************************************************************/
9561
/*                      OSRHasPointMotionOperation()                    */
9562
/************************************************************************/
9563
9564
/**
9565
 * \brief Check if a CRS has at least an associated point motion operation.
9566
 *
9567
 * Some CRS are not formally declared as dynamic, but may behave as such
9568
 * in practice due to the presence of point motion operation, to perform
9569
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9570
 *
9571
 * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9572
 *
9573
 * @since OGR 3.8.0 and PROJ 9.4.0
9574
 */
9575
int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9576
9577
0
{
9578
0
    VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9579
9580
0
    return ToPointer(hSRS)->HasPointMotionOperation();
9581
0
}
9582
9583
/************************************************************************/
9584
/*                            CloneGeogCS()                             */
9585
/************************************************************************/
9586
9587
/**
9588
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9589
 * object.
9590
 *
9591
 * @return a new SRS, which becomes the responsibility of the caller.
9592
 */
9593
OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9594
9595
25
{
9596
25
    TAKE_OPTIONAL_LOCK();
9597
25
    d->refreshProjObj();
9598
25
    if (d->m_pj_crs)
9599
25
    {
9600
25
        if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9601
0
            return nullptr;
9602
9603
25
        auto geodCRS =
9604
25
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9605
25
        if (geodCRS)
9606
25
        {
9607
25
            OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9608
25
            if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9609
0
            {
9610
0
                PJ *hub_crs =
9611
0
                    proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9612
0
                PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9613
0
                                                     d->m_pj_crs);
9614
0
                auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9615
0
                                                      geodCRS, hub_crs, co);
9616
0
                proj_destroy(geodCRS);
9617
0
                geodCRS = temp;
9618
0
                proj_destroy(hub_crs);
9619
0
                proj_destroy(co);
9620
0
            }
9621
9622
            /* --------------------------------------------------------------------
9623
             */
9624
            /*      We have to reconstruct the GEOGCS node for geocentric */
9625
            /*      coordinate systems. */
9626
            /* --------------------------------------------------------------------
9627
             */
9628
25
            if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9629
0
            {
9630
0
                auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9631
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9632
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9633
0
                if (datum == nullptr)
9634
0
                {
9635
0
                    datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9636
0
                                                        geodCRS);
9637
0
                }
9638
0
#endif
9639
0
                if (datum)
9640
0
                {
9641
0
                    auto cs = proj_create_ellipsoidal_2D_cs(
9642
0
                        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9643
0
                        nullptr, 0);
9644
0
                    auto temp = proj_create_geographic_crs_from_datum(
9645
0
                        d->getPROJContext(), "unnamed", datum, cs);
9646
0
                    proj_destroy(datum);
9647
0
                    proj_destroy(cs);
9648
0
                    proj_destroy(geodCRS);
9649
0
                    geodCRS = temp;
9650
0
                }
9651
0
            }
9652
9653
25
            poNewSRS->d->setPjCRS(geodCRS);
9654
25
            if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9655
0
                poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9656
25
            return poNewSRS;
9657
25
        }
9658
25
    }
9659
0
    return nullptr;
9660
25
}
9661
9662
/************************************************************************/
9663
/*                           OSRCloneGeogCS()                           */
9664
/************************************************************************/
9665
/**
9666
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9667
 * object.
9668
 *
9669
 * This function is the same as OGRSpatialReference::CloneGeogCS().
9670
 */
9671
OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9672
9673
0
{
9674
0
    VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9675
9676
0
    return ToHandle(ToPointer(hSource)->CloneGeogCS());
9677
0
}
9678
9679
/************************************************************************/
9680
/*                            IsSameGeogCS()                            */
9681
/************************************************************************/
9682
9683
/**
9684
 * \brief Do the GeogCS'es match?
9685
 *
9686
 * This method is the same as the C function OSRIsSameGeogCS().
9687
 *
9688
 * @param poOther the SRS being compared against.
9689
 *
9690
 * @return TRUE if they are the same or FALSE otherwise.
9691
 */
9692
9693
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9694
9695
5.79k
{
9696
5.79k
    return IsSameGeogCS(poOther, nullptr);
9697
5.79k
}
9698
9699
/**
9700
 * \brief Do the GeogCS'es match?
9701
 *
9702
 * This method is the same as the C function OSRIsSameGeogCS().
9703
 *
9704
 * @param poOther the SRS being compared against.
9705
 * @param papszOptions options. ignored
9706
 *
9707
 * @return TRUE if they are the same or FALSE otherwise.
9708
 */
9709
9710
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9711
                                      const char *const *papszOptions) const
9712
9713
5.79k
{
9714
5.79k
    TAKE_OPTIONAL_LOCK();
9715
9716
5.79k
    CPL_IGNORE_RET_VAL(papszOptions);
9717
9718
5.79k
    d->refreshProjObj();
9719
5.79k
    poOther->d->refreshProjObj();
9720
5.79k
    if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9721
37
        return FALSE;
9722
5.76k
    if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9723
5.76k
        d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9724
5.76k
        poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9725
5.76k
        poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9726
0
    {
9727
0
        return FALSE;
9728
0
    }
9729
9730
5.76k
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9731
5.76k
    auto otherGeodCRS =
9732
5.76k
        proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9733
5.76k
    if (!geodCRS || !otherGeodCRS)
9734
0
    {
9735
0
        proj_destroy(geodCRS);
9736
0
        proj_destroy(otherGeodCRS);
9737
0
        return FALSE;
9738
0
    }
9739
9740
5.76k
    int ret = proj_is_equivalent_to(
9741
5.76k
        geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9742
9743
5.76k
    proj_destroy(geodCRS);
9744
5.76k
    proj_destroy(otherGeodCRS);
9745
5.76k
    return ret;
9746
5.76k
}
9747
9748
/************************************************************************/
9749
/*                          OSRIsSameGeogCS()                           */
9750
/************************************************************************/
9751
9752
/**
9753
 * \brief Do the GeogCS'es match?
9754
 *
9755
 * This function is the same as OGRSpatialReference::IsSameGeogCS().
9756
 */
9757
int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9758
9759
0
{
9760
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9761
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9762
9763
0
    return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9764
0
}
9765
9766
/************************************************************************/
9767
/*                            IsSameVertCS()                            */
9768
/************************************************************************/
9769
9770
/**
9771
 * \brief Do the VertCS'es match?
9772
 *
9773
 * This method is the same as the C function OSRIsSameVertCS().
9774
 *
9775
 * @param poOther the SRS being compared against.
9776
 *
9777
 * @return TRUE if they are the same or FALSE otherwise.
9778
 */
9779
9780
int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9781
9782
0
{
9783
0
    TAKE_OPTIONAL_LOCK();
9784
9785
    /* -------------------------------------------------------------------- */
9786
    /*      Does the datum name match?                                      */
9787
    /* -------------------------------------------------------------------- */
9788
0
    const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9789
0
    const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9790
9791
0
    if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9792
0
        !EQUAL(pszThisValue, pszOtherValue))
9793
0
        return FALSE;
9794
9795
    /* -------------------------------------------------------------------- */
9796
    /*      Do the units match?                                             */
9797
    /* -------------------------------------------------------------------- */
9798
0
    pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9799
0
    if (pszThisValue == nullptr)
9800
0
        pszThisValue = "1.0";
9801
9802
0
    pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9803
0
    if (pszOtherValue == nullptr)
9804
0
        pszOtherValue = "1.0";
9805
9806
0
    if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9807
0
        return FALSE;
9808
9809
0
    return TRUE;
9810
0
}
9811
9812
/************************************************************************/
9813
/*                          OSRIsSameVertCS()                           */
9814
/************************************************************************/
9815
9816
/**
9817
 * \brief Do the VertCS'es match?
9818
 *
9819
 * This function is the same as OGRSpatialReference::IsSameVertCS().
9820
 */
9821
int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9822
9823
0
{
9824
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9825
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9826
9827
0
    return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9828
0
}
9829
9830
/************************************************************************/
9831
/*                               IsSame()                               */
9832
/************************************************************************/
9833
9834
/**
9835
 * \brief Do these two spatial references describe the same system ?
9836
 *
9837
 * @param poOtherSRS the SRS being compared to.
9838
 *
9839
 * @return TRUE if equivalent or FALSE otherwise.
9840
 */
9841
9842
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9843
9844
0
{
9845
0
    return IsSame(poOtherSRS, nullptr);
9846
0
}
9847
9848
/**
9849
 * \brief Do these two spatial references describe the same system ?
9850
 *
9851
 * This also takes into account the data axis to CRS axis mapping by default
9852
 *
9853
 * @param poOtherSRS the SRS being compared to.
9854
 * @param papszOptions options. NULL or NULL terminated list of options.
9855
 * Currently supported options are:
9856
 * <ul>
9857
 * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9858
 * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9859
 *     Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9860
 * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9861
 * </ul>
9862
 *
9863
 * @return TRUE if equivalent or FALSE otherwise.
9864
 */
9865
9866
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9867
                                const char *const *papszOptions) const
9868
9869
5.88k
{
9870
5.88k
    TAKE_OPTIONAL_LOCK();
9871
9872
5.88k
    d->refreshProjObj();
9873
5.88k
    poOtherSRS->d->refreshProjObj();
9874
5.88k
    if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9875
3.71k
        return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9876
2.16k
    if (!CPLTestBool(CSLFetchNameValueDef(
9877
2.16k
            papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9878
0
    {
9879
0
        if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9880
0
            return false;
9881
0
    }
9882
9883
2.16k
    if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9884
2.16k
                                          "IGNORE_COORDINATE_EPOCH", "NO")))
9885
2.16k
    {
9886
2.16k
        if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9887
0
            return false;
9888
2.16k
    }
9889
9890
2.16k
    bool reboundSelf = false;
9891
2.16k
    bool reboundOther = false;
9892
2.16k
    if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
9893
2.16k
        poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
9894
0
    {
9895
0
        d->demoteFromBoundCRS();
9896
0
        reboundSelf = true;
9897
0
    }
9898
2.16k
    else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
9899
2.16k
             poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
9900
109
    {
9901
109
        poOtherSRS->d->demoteFromBoundCRS();
9902
109
        reboundOther = true;
9903
109
    }
9904
9905
2.16k
    PJ_COMPARISON_CRITERION criterion =
9906
2.16k
        PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
9907
2.16k
    const char *pszCriterion = CSLFetchNameValueDef(
9908
2.16k
        papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
9909
2.16k
    if (EQUAL(pszCriterion, "STRICT"))
9910
0
        criterion = PJ_COMP_STRICT;
9911
2.16k
    else if (EQUAL(pszCriterion, "EQUIVALENT"))
9912
0
        criterion = PJ_COMP_EQUIVALENT;
9913
2.16k
    else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
9914
0
    {
9915
0
        CPLError(CE_Warning, CPLE_NotSupported,
9916
0
                 "Unsupported value for CRITERION: %s", pszCriterion);
9917
0
    }
9918
2.16k
    int ret =
9919
2.16k
        proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
9920
2.16k
    if (reboundSelf)
9921
0
        d->undoDemoteFromBoundCRS();
9922
2.16k
    if (reboundOther)
9923
109
        poOtherSRS->d->undoDemoteFromBoundCRS();
9924
9925
2.16k
    return ret;
9926
2.16k
}
9927
9928
/************************************************************************/
9929
/*                             OSRIsSame()                              */
9930
/************************************************************************/
9931
9932
/**
9933
 * \brief Do these two spatial references describe the same system ?
9934
 *
9935
 * This function is the same as OGRSpatialReference::IsSame().
9936
 */
9937
int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9938
9939
0
{
9940
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9941
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9942
9943
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
9944
0
}
9945
9946
/************************************************************************/
9947
/*                             OSRIsSameEx()                            */
9948
/************************************************************************/
9949
9950
/**
9951
 * \brief Do these two spatial references describe the same system ?
9952
 *
9953
 * This function is the same as OGRSpatialReference::IsSame().
9954
 */
9955
int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
9956
                const char *const *papszOptions)
9957
0
{
9958
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9959
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9960
9961
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
9962
0
}
9963
9964
/************************************************************************/
9965
/*                    convertToOtherProjection()                        */
9966
/************************************************************************/
9967
9968
/**
9969
 * \brief Convert to another equivalent projection
9970
 *
9971
 * Currently implemented:
9972
 * <ul>
9973
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9974
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9975
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9976
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9977
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9978
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9979
 * </ul>
9980
 *
9981
 * @param pszTargetProjection target projection.
9982
 * @param papszOptions lists of options. None supported currently.
9983
 * @return a new SRS, or NULL in case of error.
9984
 *
9985
 * @since GDAL 2.3
9986
 */
9987
OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
9988
    const char *pszTargetProjection,
9989
    CPL_UNUSED const char *const *papszOptions) const
9990
0
{
9991
0
    TAKE_OPTIONAL_LOCK();
9992
9993
0
    if (pszTargetProjection == nullptr)
9994
0
        return nullptr;
9995
0
    int new_code;
9996
0
    if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
9997
0
    {
9998
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
9999
0
    }
10000
0
    else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10001
0
    {
10002
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10003
0
    }
10004
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10005
0
    {
10006
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10007
0
    }
10008
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10009
0
    {
10010
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10011
0
    }
10012
0
    else
10013
0
    {
10014
0
        return nullptr;
10015
0
    }
10016
10017
0
    d->refreshProjObj();
10018
0
    d->demoteFromBoundCRS();
10019
0
    OGRSpatialReference *poNewSRS = nullptr;
10020
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10021
0
    {
10022
0
        auto conv =
10023
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10024
0
        auto new_conv = proj_convert_conversion_to_other_method(
10025
0
            d->getPROJContext(), conv, new_code, nullptr);
10026
0
        proj_destroy(conv);
10027
0
        if (new_conv)
10028
0
        {
10029
0
            auto geodCRS =
10030
0
                proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10031
0
            auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10032
0
                                                     d->m_pj_crs);
10033
0
            if (geodCRS && cs)
10034
0
            {
10035
0
                auto new_proj_crs = proj_create_projected_crs(
10036
0
                    d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10037
0
                    new_conv, cs);
10038
0
                proj_destroy(new_conv);
10039
0
                if (new_proj_crs)
10040
0
                {
10041
0
                    poNewSRS = new OGRSpatialReference();
10042
10043
0
                    if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10044
0
                    {
10045
0
                        auto boundCRS = proj_crs_create_bound_crs(
10046
0
                            d->getPROJContext(), new_proj_crs,
10047
0
                            d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10048
0
                        if (boundCRS)
10049
0
                        {
10050
0
                            proj_destroy(new_proj_crs);
10051
0
                            new_proj_crs = boundCRS;
10052
0
                        }
10053
0
                    }
10054
10055
0
                    poNewSRS->d->setPjCRS(new_proj_crs);
10056
0
                }
10057
0
            }
10058
0
            proj_destroy(geodCRS);
10059
0
            proj_destroy(cs);
10060
0
        }
10061
0
    }
10062
0
    d->undoDemoteFromBoundCRS();
10063
0
    return poNewSRS;
10064
0
}
10065
10066
/************************************************************************/
10067
/*                    OSRConvertToOtherProjection()                     */
10068
/************************************************************************/
10069
10070
/**
10071
 * \brief Convert to another equivalent projection
10072
 *
10073
 * Currently implemented:
10074
 * <ul>
10075
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10076
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10077
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10078
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10079
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10080
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10081
 * </ul>
10082
 *
10083
 * @param hSRS source SRS
10084
 * @param pszTargetProjection target projection.
10085
 * @param papszOptions lists of options. None supported currently.
10086
 * @return a new SRS, or NULL in case of error.
10087
 *
10088
 * @since GDAL 2.3
10089
 */
10090
OGRSpatialReferenceH
10091
OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10092
                            const char *pszTargetProjection,
10093
                            const char *const *papszOptions)
10094
0
{
10095
0
    VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10096
0
    return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10097
0
        pszTargetProjection, papszOptions));
10098
0
}
10099
10100
/************************************************************************/
10101
/*                           OSRFindMatches()                           */
10102
/************************************************************************/
10103
10104
/**
10105
 * \brief Try to identify a match between the passed SRS and a related SRS
10106
 * in a catalog.
10107
 *
10108
 * Matching may be partial, or may fail.
10109
 * Returned entries will be sorted by decreasing match confidence (first
10110
 * entry has the highest match confidence).
10111
 *
10112
 * The exact way matching is done may change in future versions. Starting with
10113
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
10114
 *
10115
 * This function is the same as OGRSpatialReference::FindMatches().
10116
 *
10117
 * @param hSRS SRS to match
10118
 * @param papszOptions NULL terminated list of options or NULL
10119
 * @param pnEntries Output parameter. Number of values in the returned array.
10120
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10121
 * will be allocated to an array of *pnEntries whose values between 0 and 100
10122
 * indicate the confidence in the match. 100 is the highest confidence level.
10123
 * The array must be freed with CPLFree().
10124
 *
10125
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10126
 * with OSRFreeSRSArray()
10127
 *
10128
 * @since GDAL 2.3
10129
 */
10130
OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10131
                                     char **papszOptions, int *pnEntries,
10132
                                     int **ppanMatchConfidence)
10133
0
{
10134
0
    if (pnEntries)
10135
0
        *pnEntries = 0;
10136
0
    if (ppanMatchConfidence)
10137
0
        *ppanMatchConfidence = nullptr;
10138
0
    VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10139
10140
0
    OGRSpatialReference *poSRS = ToPointer(hSRS);
10141
0
    return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10142
0
}
10143
10144
/************************************************************************/
10145
/*                           OSRFreeSRSArray()                          */
10146
/************************************************************************/
10147
10148
/**
10149
 * \brief Free return of OSRIdentifyMatches()
10150
 *
10151
 * @param pahSRS array of SRS (must be NULL terminated)
10152
 * @since GDAL 2.3
10153
 */
10154
void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10155
747
{
10156
747
    if (pahSRS != nullptr)
10157
731
    {
10158
122k
        for (int i = 0; pahSRS[i] != nullptr; ++i)
10159
122k
        {
10160
122k
            OSRRelease(pahSRS[i]);
10161
122k
        }
10162
731
        CPLFree(pahSRS);
10163
731
    }
10164
747
}
10165
10166
/************************************************************************/
10167
/*                         FindBestMatch()                              */
10168
/************************************************************************/
10169
10170
/**
10171
 * \brief Try to identify the best match between the passed SRS and a related
10172
 * SRS in a catalog.
10173
 *
10174
 * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10175
 * of filtering its output.
10176
 * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10177
 * will be considered. If there is a single match, it is returned.
10178
 * If there are several matches, only return the one under the
10179
 * pszPreferredAuthority, if there is a single one under that authority.
10180
 *
10181
 * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10182
 * 100). If set to 0, 90 is used.
10183
 * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10184
 * "EPSG" is used.
10185
 * @param papszOptions NULL terminated list of options or NULL. No option is
10186
 * defined at time of writing.
10187
 *
10188
 * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10189
 *
10190
 * @since GDAL 3.6
10191
 * @see OGRSpatialReference::FindMatches()
10192
 */
10193
OGRSpatialReference *
10194
OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10195
                                   const char *pszPreferredAuthority,
10196
                                   CSLConstList papszOptions) const
10197
772
{
10198
772
    TAKE_OPTIONAL_LOCK();
10199
10200
772
    CPL_IGNORE_RET_VAL(papszOptions);  // ignored for now.
10201
10202
772
    if (nMinimumMatchConfidence == 0)
10203
0
        nMinimumMatchConfidence = 90;
10204
772
    if (pszPreferredAuthority == nullptr)
10205
0
        pszPreferredAuthority = "EPSG";
10206
10207
    // Try to identify the CRS with the database
10208
772
    int nEntries = 0;
10209
772
    int *panConfidence = nullptr;
10210
772
    OGRSpatialReferenceH *pahSRS =
10211
772
        FindMatches(nullptr, &nEntries, &panConfidence);
10212
772
    if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10213
25
    {
10214
25
        std::vector<double> adfTOWGS84(7);
10215
25
        if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10216
25
        {
10217
25
            adfTOWGS84.clear();
10218
25
        }
10219
10220
25
        auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10221
10222
25
        auto poBaseGeogCRS =
10223
25
            std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10224
10225
        // If the base geographic SRS of the SRS is EPSG:4326
10226
        // with TOWGS84[0,0,0,0,0,0], then just use the official
10227
        // SRS code
10228
        // Same with EPSG:4258 (ETRS89), since it's the only known
10229
        // TOWGS84[] style transformation to WGS 84, and given the
10230
        // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10231
        // chance that a non-NULL TOWGS84[] will emerge.
10232
25
        const char *pszAuthorityName = nullptr;
10233
25
        const char *pszAuthorityCode = nullptr;
10234
25
        const char *pszBaseAuthorityName = nullptr;
10235
25
        const char *pszBaseAuthorityCode = nullptr;
10236
25
        if (adfTOWGS84 == std::vector<double>(7) &&
10237
25
            (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10238
25
            EQUAL(pszAuthorityName, "EPSG") &&
10239
25
            (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10240
25
            (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10241
0
                nullptr &&
10242
25
            EQUAL(pszBaseAuthorityName, "EPSG") &&
10243
25
            (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10244
0
                nullptr &&
10245
25
            (EQUAL(pszBaseAuthorityCode, "4326") ||
10246
0
             EQUAL(pszBaseAuthorityCode, "4258")))
10247
0
        {
10248
0
            poSRS->importFromEPSG(atoi(pszAuthorityCode));
10249
0
        }
10250
10251
25
        CPLFree(pahSRS);
10252
25
        CPLFree(panConfidence);
10253
10254
25
        return poSRS;
10255
25
    }
10256
747
    else
10257
747
    {
10258
        // If there are several matches >= nMinimumMatchConfidence, take the
10259
        // only one that is under pszPreferredAuthority
10260
747
        int iBestEntry = -1;
10261
122k
        for (int i = 0; i < nEntries; i++)
10262
122k
        {
10263
122k
            if (panConfidence[i] >= nMinimumMatchConfidence)
10264
0
            {
10265
0
                const char *pszAuthName =
10266
0
                    OGRSpatialReference::FromHandle(pahSRS[i])
10267
0
                        ->GetAuthorityName(nullptr);
10268
0
                if (pszAuthName != nullptr &&
10269
0
                    EQUAL(pszAuthName, pszPreferredAuthority))
10270
0
                {
10271
0
                    if (iBestEntry < 0)
10272
0
                        iBestEntry = i;
10273
0
                    else
10274
0
                    {
10275
0
                        iBestEntry = -1;
10276
0
                        break;
10277
0
                    }
10278
0
                }
10279
0
            }
10280
122k
        }
10281
747
        if (iBestEntry >= 0)
10282
0
        {
10283
0
            auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10284
0
            OSRFreeSRSArray(pahSRS);
10285
0
            CPLFree(panConfidence);
10286
0
            return poRet;
10287
0
        }
10288
747
    }
10289
747
    OSRFreeSRSArray(pahSRS);
10290
747
    CPLFree(panConfidence);
10291
747
    return nullptr;
10292
772
}
10293
10294
/************************************************************************/
10295
/*                             SetTOWGS84()                             */
10296
/************************************************************************/
10297
10298
/**
10299
 * \brief Set the Bursa-Wolf conversion to WGS84.
10300
 *
10301
 * This will create the TOWGS84 node as a child of the DATUM.  It will fail
10302
 * if there is no existing DATUM node. It will replace
10303
 * an existing TOWGS84 node if there is one.
10304
 *
10305
 * The parameters have the same meaning as EPSG transformation 9606
10306
 * (Position Vector 7-param. transformation).
10307
 *
10308
 * This method is the same as the C function OSRSetTOWGS84().
10309
 *
10310
 * @param dfDX X child in meters.
10311
 * @param dfDY Y child in meters.
10312
 * @param dfDZ Z child in meters.
10313
 * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10314
 * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10315
 * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10316
 * @param dfPPM scaling factor (parts per million).
10317
 *
10318
 * @return OGRERR_NONE on success.
10319
 */
10320
10321
OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10322
                                       double dfEX, double dfEY, double dfEZ,
10323
                                       double dfPPM)
10324
10325
983
{
10326
983
    TAKE_OPTIONAL_LOCK();
10327
10328
983
    d->refreshProjObj();
10329
983
    if (d->m_pj_crs == nullptr)
10330
72
    {
10331
72
        return OGRERR_FAILURE;
10332
72
    }
10333
10334
    // Remove existing BoundCRS
10335
911
    if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10336
0
    {
10337
0
        auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10338
0
        if (!baseCRS)
10339
0
            return OGRERR_FAILURE;
10340
0
        d->setPjCRS(baseCRS);
10341
0
    }
10342
10343
911
    PJ_PARAM_DESCRIPTION params[7];
10344
10345
911
    params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10346
911
    params[0].auth_name = "EPSG";
10347
911
    params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10348
911
    params[0].value = dfDX;
10349
911
    params[0].unit_name = "metre";
10350
911
    params[0].unit_conv_factor = 1.0;
10351
911
    params[0].unit_type = PJ_UT_LINEAR;
10352
10353
911
    params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10354
911
    params[1].auth_name = "EPSG";
10355
911
    params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10356
911
    params[1].value = dfDY;
10357
911
    params[1].unit_name = "metre";
10358
911
    params[1].unit_conv_factor = 1.0;
10359
911
    params[1].unit_type = PJ_UT_LINEAR;
10360
10361
911
    params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10362
911
    params[2].auth_name = "EPSG";
10363
911
    params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10364
911
    params[2].value = dfDZ;
10365
911
    params[2].unit_name = "metre";
10366
911
    params[2].unit_conv_factor = 1.0;
10367
911
    params[2].unit_type = PJ_UT_LINEAR;
10368
10369
911
    params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10370
911
    params[3].auth_name = "EPSG";
10371
911
    params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10372
911
    params[3].value = dfEX;
10373
911
    params[3].unit_name = "arc-second";
10374
911
    params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10375
911
    params[3].unit_type = PJ_UT_ANGULAR;
10376
10377
911
    params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10378
911
    params[4].auth_name = "EPSG";
10379
911
    params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10380
911
    params[4].value = dfEY;
10381
911
    params[4].unit_name = "arc-second";
10382
911
    params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10383
911
    params[4].unit_type = PJ_UT_ANGULAR;
10384
10385
911
    params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10386
911
    params[5].auth_name = "EPSG";
10387
911
    params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10388
911
    params[5].value = dfEZ;
10389
911
    params[5].unit_name = "arc-second";
10390
911
    params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10391
911
    params[5].unit_type = PJ_UT_ANGULAR;
10392
10393
911
    params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10394
911
    params[6].auth_name = "EPSG";
10395
911
    params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10396
911
    params[6].value = dfPPM;
10397
911
    params[6].unit_name = "parts per million";
10398
911
    params[6].unit_conv_factor = 1e-6;
10399
911
    params[6].unit_type = PJ_UT_SCALE;
10400
10401
911
    auto sourceCRS =
10402
911
        proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10403
911
    if (!sourceCRS)
10404
5
    {
10405
5
        return OGRERR_FAILURE;
10406
5
    }
10407
10408
906
    const auto sourceType = proj_get_type(sourceCRS);
10409
10410
906
    auto targetCRS = proj_create_from_database(
10411
906
        d->getPROJContext(), "EPSG",
10412
906
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS   ? "4326"
10413
906
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10414
160
                                                  : "4978",
10415
906
        PJ_CATEGORY_CRS, false, nullptr);
10416
906
    if (!targetCRS)
10417
0
    {
10418
0
        proj_destroy(sourceCRS);
10419
0
        return OGRERR_FAILURE;
10420
0
    }
10421
10422
906
    CPLString osMethodCode;
10423
906
    osMethodCode.Printf("%d",
10424
906
                        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10425
906
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10426
906
                        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10427
160
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10428
160
                            : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10429
10430
906
    auto transf = proj_create_transformation(
10431
906
        d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10432
906
        sourceCRS, targetCRS, nullptr,
10433
906
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10434
906
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10435
906
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10436
160
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10437
160
            : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10438
906
        "EPSG", osMethodCode.c_str(), 7, params, -1);
10439
906
    proj_destroy(sourceCRS);
10440
906
    if (!transf)
10441
0
    {
10442
0
        proj_destroy(targetCRS);
10443
0
        return OGRERR_FAILURE;
10444
0
    }
10445
10446
906
    auto newBoundCRS = proj_crs_create_bound_crs(
10447
906
        d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10448
906
    proj_destroy(transf);
10449
906
    proj_destroy(targetCRS);
10450
906
    if (!newBoundCRS)
10451
0
    {
10452
0
        return OGRERR_FAILURE;
10453
0
    }
10454
10455
906
    d->setPjCRS(newBoundCRS);
10456
906
    return OGRERR_NONE;
10457
906
}
10458
10459
/************************************************************************/
10460
/*                           OSRSetTOWGS84()                            */
10461
/************************************************************************/
10462
10463
/**
10464
 * \brief Set the Bursa-Wolf conversion to WGS84.
10465
 *
10466
 * This function is the same as OGRSpatialReference::SetTOWGS84().
10467
 */
10468
OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10469
                     double dfDZ, double dfEX, double dfEY, double dfEZ,
10470
                     double dfPPM)
10471
10472
0
{
10473
0
    VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10474
10475
0
    return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10476
0
                                       dfPPM);
10477
0
}
10478
10479
/************************************************************************/
10480
/*                             GetTOWGS84()                             */
10481
/************************************************************************/
10482
10483
/**
10484
 * \brief Fetch TOWGS84 parameters, if available.
10485
 *
10486
 * The parameters have the same meaning as EPSG transformation 9606
10487
 * (Position Vector 7-param. transformation).
10488
 *
10489
 * @param padfCoeff array into which up to 7 coefficients are placed.
10490
 * @param nCoeffCount size of padfCoeff - defaults to 7.
10491
 *
10492
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10493
 * TOWGS84 node available.
10494
 */
10495
10496
OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10497
10498
25
{
10499
25
    TAKE_OPTIONAL_LOCK();
10500
10501
25
    d->refreshProjObj();
10502
25
    if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10503
25
        return OGRERR_FAILURE;
10504
10505
0
    memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10506
10507
0
    auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10508
0
    int success = proj_coordoperation_get_towgs84_values(
10509
0
        d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10510
0
    proj_destroy(transf);
10511
10512
0
    return success ? OGRERR_NONE : OGRERR_FAILURE;
10513
25
}
10514
10515
/************************************************************************/
10516
/*                           OSRGetTOWGS84()                            */
10517
/************************************************************************/
10518
10519
/**
10520
 * \brief Fetch TOWGS84 parameters, if available.
10521
 *
10522
 * This function is the same as OGRSpatialReference::GetTOWGS84().
10523
 */
10524
OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10525
                     int nCoeffCount)
10526
10527
0
{
10528
0
    VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10529
10530
0
    return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10531
0
}
10532
10533
/************************************************************************/
10534
/*                         IsAngularParameter()                         */
10535
/************************************************************************/
10536
10537
/** Is the passed projection parameter an angular one?
10538
 *
10539
 * @return TRUE or FALSE
10540
 */
10541
10542
/* static */
10543
int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10544
10545
0
{
10546
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10547
0
        STARTS_WITH_CI(pszParameterName, "lati") ||
10548
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10549
0
        STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10550
0
        EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10551
0
        EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10552
0
        return TRUE;
10553
10554
0
    return FALSE;
10555
0
}
10556
10557
/************************************************************************/
10558
/*                        IsLongitudeParameter()                        */
10559
/************************************************************************/
10560
10561
/** Is the passed projection parameter an angular longitude
10562
 * (relative to a prime meridian)?
10563
 *
10564
 * @return TRUE or FALSE
10565
 */
10566
10567
/* static */
10568
int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10569
10570
0
{
10571
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10572
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10573
0
        return TRUE;
10574
10575
0
    return FALSE;
10576
0
}
10577
10578
/************************************************************************/
10579
/*                         IsLinearParameter()                          */
10580
/************************************************************************/
10581
10582
/** Is the passed projection parameter an linear one measured in meters or
10583
 * some similar linear measure.
10584
 *
10585
 * @return TRUE or FALSE
10586
 */
10587
10588
/* static */
10589
int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10590
10591
0
{
10592
0
    if (STARTS_WITH_CI(pszParameterName, "false_") ||
10593
0
        EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10594
0
        return TRUE;
10595
10596
0
    return FALSE;
10597
0
}
10598
10599
/************************************************************************/
10600
/*                            GetNormInfo()                             */
10601
/************************************************************************/
10602
10603
/**
10604
 * \brief Set the internal information for normalizing linear, and angular
10605
 * values.
10606
 */
10607
void OGRSpatialReference::GetNormInfo() const
10608
10609
0
{
10610
0
    TAKE_OPTIONAL_LOCK();
10611
10612
0
    if (d->bNormInfoSet)
10613
0
        return;
10614
10615
    /* -------------------------------------------------------------------- */
10616
    /*      Initialize values.                                              */
10617
    /* -------------------------------------------------------------------- */
10618
0
    d->bNormInfoSet = TRUE;
10619
10620
0
    d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10621
0
    d->dfToMeter = GetLinearUnits(nullptr);
10622
0
    d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10623
0
    if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10624
0
        d->dfToDegrees = 1.0;
10625
0
}
10626
10627
/************************************************************************/
10628
/*                            GetExtension()                            */
10629
/************************************************************************/
10630
10631
/**
10632
 * \brief Fetch extension value.
10633
 *
10634
 * Fetch the value of the named EXTENSION item for the identified
10635
 * target node.
10636
 *
10637
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10638
 * @param pszName the name of the extension being fetched.
10639
 * @param pszDefault the value to return if the extension is not found.
10640
 *
10641
 * @return node value if successful or pszDefault on failure.
10642
 */
10643
10644
const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10645
                                              const char *pszName,
10646
                                              const char *pszDefault) const
10647
10648
0
{
10649
0
    TAKE_OPTIONAL_LOCK();
10650
10651
    /* -------------------------------------------------------------------- */
10652
    /*      Find the target node.                                           */
10653
    /* -------------------------------------------------------------------- */
10654
0
    const OGR_SRSNode *poNode =
10655
0
        pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10656
10657
0
    if (poNode == nullptr)
10658
0
        return nullptr;
10659
10660
    /* -------------------------------------------------------------------- */
10661
    /*      Fetch matching EXTENSION if there is one.                       */
10662
    /* -------------------------------------------------------------------- */
10663
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10664
0
    {
10665
0
        const OGR_SRSNode *poChild = poNode->GetChild(i);
10666
10667
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10668
0
            poChild->GetChildCount() >= 2)
10669
0
        {
10670
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10671
0
                return poChild->GetChild(1)->GetValue();
10672
0
        }
10673
0
    }
10674
10675
0
    return pszDefault;
10676
0
}
10677
10678
/************************************************************************/
10679
/*                            SetExtension()                            */
10680
/************************************************************************/
10681
/**
10682
 * \brief Set extension value.
10683
 *
10684
 * Set the value of the named EXTENSION item for the identified
10685
 * target node.
10686
 *
10687
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10688
 * @param pszName the name of the extension being fetched.
10689
 * @param pszValue the value to set
10690
 *
10691
 * @return OGRERR_NONE on success
10692
 */
10693
10694
OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10695
                                         const char *pszName,
10696
                                         const char *pszValue)
10697
10698
2
{
10699
2
    TAKE_OPTIONAL_LOCK();
10700
10701
    /* -------------------------------------------------------------------- */
10702
    /*      Find the target node.                                           */
10703
    /* -------------------------------------------------------------------- */
10704
2
    OGR_SRSNode *poNode = nullptr;
10705
10706
2
    if (pszTargetKey == nullptr)
10707
0
        poNode = GetRoot();
10708
2
    else
10709
2
        poNode = GetAttrNode(pszTargetKey);
10710
10711
2
    if (poNode == nullptr)
10712
1
        return OGRERR_FAILURE;
10713
10714
    /* -------------------------------------------------------------------- */
10715
    /*      Fetch matching EXTENSION if there is one.                       */
10716
    /* -------------------------------------------------------------------- */
10717
11
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10718
10
    {
10719
10
        OGR_SRSNode *poChild = poNode->GetChild(i);
10720
10721
10
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10722
10
            poChild->GetChildCount() >= 2)
10723
0
        {
10724
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10725
0
            {
10726
0
                poChild->GetChild(1)->SetValue(pszValue);
10727
0
                return OGRERR_NONE;
10728
0
            }
10729
0
        }
10730
10
    }
10731
10732
    /* -------------------------------------------------------------------- */
10733
    /*      Create a new EXTENSION node.                                    */
10734
    /* -------------------------------------------------------------------- */
10735
1
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10736
1
    poAuthNode->AddChild(new OGR_SRSNode(pszName));
10737
1
    poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10738
10739
1
    poNode->AddChild(poAuthNode);
10740
10741
1
    return OGRERR_NONE;
10742
1
}
10743
10744
/************************************************************************/
10745
/*                             OSRCleanup()                             */
10746
/************************************************************************/
10747
10748
static void CleanupSRSWGS84Mutex();
10749
10750
/**
10751
 * \brief Cleanup cached SRS related memory.
10752
 *
10753
 * This function will attempt to cleanup any cache spatial reference
10754
 * related information, such as cached tables of coordinate systems.
10755
 *
10756
 * This function should not be called concurrently with any other GDAL/OGR
10757
 * function. It is meant at being called once before process termination
10758
 * (typically from the main thread). CPLCleanupTLS() might be used to clean
10759
 * thread-specific resources before thread termination.
10760
 */
10761
void OSRCleanup(void)
10762
10763
0
{
10764
0
    OGRCTDumpStatistics();
10765
0
    CSVDeaccess(nullptr);
10766
0
    CleanupSRSWGS84Mutex();
10767
0
    OSRCTCleanCache();
10768
0
    OSRCleanupTLSContext();
10769
0
}
10770
10771
/************************************************************************/
10772
/*                              GetAxesCount()                          */
10773
/************************************************************************/
10774
10775
/**
10776
 * \brief Return the number of axis of the coordinate system of the CRS.
10777
 *
10778
 * @since GDAL 3.0
10779
 */
10780
int OGRSpatialReference::GetAxesCount() const
10781
35.8k
{
10782
35.8k
    TAKE_OPTIONAL_LOCK();
10783
10784
35.8k
    int axisCount = 0;
10785
35.8k
    d->refreshProjObj();
10786
35.8k
    if (d->m_pj_crs == nullptr)
10787
306
    {
10788
306
        return 0;
10789
306
    }
10790
35.5k
    d->demoteFromBoundCRS();
10791
35.5k
    auto ctxt = d->getPROJContext();
10792
35.5k
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10793
179
    {
10794
358
        for (int i = 0;; i++)
10795
537
        {
10796
537
            auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10797
537
            if (!subCRS)
10798
179
                break;
10799
358
            if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10800
0
            {
10801
0
                auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10802
0
                if (baseCRS)
10803
0
                {
10804
0
                    proj_destroy(subCRS);
10805
0
                    subCRS = baseCRS;
10806
0
                }
10807
0
            }
10808
358
            auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10809
358
            if (cs)
10810
358
            {
10811
358
                axisCount += proj_cs_get_axis_count(ctxt, cs);
10812
358
                proj_destroy(cs);
10813
358
            }
10814
358
            proj_destroy(subCRS);
10815
358
        }
10816
179
    }
10817
35.3k
    else
10818
35.3k
    {
10819
35.3k
        auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10820
35.3k
        if (cs)
10821
35.3k
        {
10822
35.3k
            axisCount = proj_cs_get_axis_count(ctxt, cs);
10823
35.3k
            proj_destroy(cs);
10824
35.3k
        }
10825
35.3k
    }
10826
35.5k
    d->undoDemoteFromBoundCRS();
10827
35.5k
    return axisCount;
10828
35.8k
}
10829
10830
/************************************************************************/
10831
/*                           OSRGetAxesCount()                          */
10832
/************************************************************************/
10833
10834
/**
10835
 * \brief Return the number of axis of the coordinate system of the CRS.
10836
 *
10837
 * This method is the equivalent of the C++ method
10838
 * OGRSpatialReference::GetAxesCount()
10839
 *
10840
 * @since GDAL 3.1
10841
 */
10842
int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10843
10844
0
{
10845
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10846
10847
0
    return ToPointer(hSRS)->GetAxesCount();
10848
0
}
10849
10850
/************************************************************************/
10851
/*                              GetAxis()                               */
10852
/************************************************************************/
10853
10854
/**
10855
 * \brief Fetch the orientation of one axis.
10856
 *
10857
 * Fetches the request axis (iAxis - zero based) from the
10858
 * indicated portion of the coordinate system (pszTargetKey) which
10859
 * should be either "GEOGCS" or "PROJCS".
10860
 *
10861
 * No CPLError is issued on routine failures (such as not finding the AXIS).
10862
 *
10863
 * This method is equivalent to the C function OSRGetAxis().
10864
 *
10865
 * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10866
 * "GEOGCS").
10867
 * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10868
 * @param peOrientation location into which to place the fetch orientation, may
10869
 * be NULL.
10870
 * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10871
 * factor. May be NULL. Only set if pszTargetKey == NULL
10872
 *
10873
 * @return the name of the axis or NULL on failure.
10874
 */
10875
10876
const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10877
                                         OGRAxisOrientation *peOrientation,
10878
                                         double *pdfConvUnit) const
10879
10880
0
{
10881
0
    TAKE_OPTIONAL_LOCK();
10882
10883
0
    if (peOrientation != nullptr)
10884
0
        *peOrientation = OAO_Other;
10885
0
    if (pdfConvUnit != nullptr)
10886
0
        *pdfConvUnit = 0;
10887
10888
0
    d->refreshProjObj();
10889
0
    if (d->m_pj_crs == nullptr)
10890
0
    {
10891
0
        return nullptr;
10892
0
    }
10893
10894
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
10895
0
    if (pszTargetKey == nullptr && iAxis <= 2)
10896
0
    {
10897
0
        auto ctxt = d->getPROJContext();
10898
10899
0
        int iAxisModified = iAxis;
10900
10901
0
        d->demoteFromBoundCRS();
10902
10903
0
        PJ *cs = nullptr;
10904
0
        if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10905
0
        {
10906
0
            auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
10907
0
            if (horizCRS)
10908
0
            {
10909
0
                if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
10910
0
                {
10911
0
                    auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
10912
0
                    if (baseCRS)
10913
0
                    {
10914
0
                        proj_destroy(horizCRS);
10915
0
                        horizCRS = baseCRS;
10916
0
                    }
10917
0
                }
10918
0
                cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
10919
0
                proj_destroy(horizCRS);
10920
0
                if (cs)
10921
0
                {
10922
0
                    if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
10923
0
                    {
10924
0
                        iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
10925
0
                        proj_destroy(cs);
10926
0
                        cs = nullptr;
10927
0
                    }
10928
0
                }
10929
0
            }
10930
10931
0
            if (cs == nullptr)
10932
0
            {
10933
0
                auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
10934
0
                if (vertCRS)
10935
0
                {
10936
0
                    if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
10937
0
                    {
10938
0
                        auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
10939
0
                        if (baseCRS)
10940
0
                        {
10941
0
                            proj_destroy(vertCRS);
10942
0
                            vertCRS = baseCRS;
10943
0
                        }
10944
0
                    }
10945
10946
0
                    cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
10947
0
                    proj_destroy(vertCRS);
10948
0
                }
10949
0
            }
10950
0
        }
10951
0
        else
10952
0
        {
10953
0
            cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10954
0
        }
10955
10956
0
        if (cs)
10957
0
        {
10958
0
            const char *pszName = nullptr;
10959
0
            const char *pszOrientation = nullptr;
10960
0
            double dfConvFactor = 0.0;
10961
0
            proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
10962
0
                                  &pszOrientation, &dfConvFactor, nullptr,
10963
0
                                  nullptr, nullptr);
10964
10965
0
            if (pdfConvUnit != nullptr)
10966
0
            {
10967
0
                *pdfConvUnit = dfConvFactor;
10968
0
            }
10969
10970
0
            if (pszName && pszOrientation)
10971
0
            {
10972
0
                d->m_osAxisName[iAxis] = pszName;
10973
0
                if (peOrientation)
10974
0
                {
10975
0
                    if (EQUAL(pszOrientation, "NORTH"))
10976
0
                        *peOrientation = OAO_North;
10977
0
                    else if (EQUAL(pszOrientation, "EAST"))
10978
0
                        *peOrientation = OAO_East;
10979
0
                    else if (EQUAL(pszOrientation, "SOUTH"))
10980
0
                        *peOrientation = OAO_South;
10981
0
                    else if (EQUAL(pszOrientation, "WEST"))
10982
0
                        *peOrientation = OAO_West;
10983
0
                    else if (EQUAL(pszOrientation, "UP"))
10984
0
                        *peOrientation = OAO_Up;
10985
0
                    else if (EQUAL(pszOrientation, "DOWN"))
10986
0
                        *peOrientation = OAO_Down;
10987
0
                }
10988
0
                proj_destroy(cs);
10989
0
                d->undoDemoteFromBoundCRS();
10990
0
                return d->m_osAxisName[iAxis].c_str();
10991
0
            }
10992
0
            proj_destroy(cs);
10993
0
        }
10994
0
        d->undoDemoteFromBoundCRS();
10995
0
    }
10996
10997
    /* -------------------------------------------------------------------- */
10998
    /*      Find the target node.                                           */
10999
    /* -------------------------------------------------------------------- */
11000
0
    const OGR_SRSNode *poNode = nullptr;
11001
11002
0
    if (pszTargetKey == nullptr)
11003
0
        poNode = GetRoot();
11004
0
    else
11005
0
        poNode = GetAttrNode(pszTargetKey);
11006
11007
0
    if (poNode == nullptr)
11008
0
        return nullptr;
11009
11010
    /* -------------------------------------------------------------------- */
11011
    /*      Find desired child AXIS.                                        */
11012
    /* -------------------------------------------------------------------- */
11013
0
    const OGR_SRSNode *poAxis = nullptr;
11014
0
    const int nChildCount = poNode->GetChildCount();
11015
11016
0
    for (int iChild = 0; iChild < nChildCount; iChild++)
11017
0
    {
11018
0
        const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11019
11020
0
        if (!EQUAL(poChild->GetValue(), "AXIS"))
11021
0
            continue;
11022
11023
0
        if (iAxis == 0)
11024
0
        {
11025
0
            poAxis = poChild;
11026
0
            break;
11027
0
        }
11028
0
        iAxis--;
11029
0
    }
11030
11031
0
    if (poAxis == nullptr)
11032
0
        return nullptr;
11033
11034
0
    if (poAxis->GetChildCount() < 2)
11035
0
        return nullptr;
11036
11037
    /* -------------------------------------------------------------------- */
11038
    /*      Extract name and orientation if possible.                       */
11039
    /* -------------------------------------------------------------------- */
11040
0
    if (peOrientation != nullptr)
11041
0
    {
11042
0
        const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11043
11044
0
        if (EQUAL(pszOrientation, "NORTH"))
11045
0
            *peOrientation = OAO_North;
11046
0
        else if (EQUAL(pszOrientation, "EAST"))
11047
0
            *peOrientation = OAO_East;
11048
0
        else if (EQUAL(pszOrientation, "SOUTH"))
11049
0
            *peOrientation = OAO_South;
11050
0
        else if (EQUAL(pszOrientation, "WEST"))
11051
0
            *peOrientation = OAO_West;
11052
0
        else if (EQUAL(pszOrientation, "UP"))
11053
0
            *peOrientation = OAO_Up;
11054
0
        else if (EQUAL(pszOrientation, "DOWN"))
11055
0
            *peOrientation = OAO_Down;
11056
0
        else if (EQUAL(pszOrientation, "OTHER"))
11057
0
            *peOrientation = OAO_Other;
11058
0
        else
11059
0
        {
11060
0
            CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11061
0
                     pszOrientation);
11062
0
        }
11063
0
    }
11064
11065
0
    return poAxis->GetChild(0)->GetValue();
11066
0
}
11067
11068
/************************************************************************/
11069
/*                             OSRGetAxis()                             */
11070
/************************************************************************/
11071
11072
/**
11073
 * \brief Fetch the orientation of one axis.
11074
 *
11075
 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11076
 */
11077
const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11078
                       int iAxis, OGRAxisOrientation *peOrientation)
11079
11080
0
{
11081
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11082
11083
0
    return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11084
0
}
11085
11086
/************************************************************************/
11087
/*                         OSRAxisEnumToName()                          */
11088
/************************************************************************/
11089
11090
/**
11091
 * \brief Return the string representation for the OGRAxisOrientation
11092
 * enumeration.
11093
 *
11094
 * For example "NORTH" for OAO_North.
11095
 *
11096
 * @return an internal string
11097
 */
11098
const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11099
11100
27.0k
{
11101
27.0k
    if (eOrientation == OAO_North)
11102
13.5k
        return "NORTH";
11103
13.5k
    if (eOrientation == OAO_East)
11104
13.5k
        return "EAST";
11105
0
    if (eOrientation == OAO_South)
11106
0
        return "SOUTH";
11107
0
    if (eOrientation == OAO_West)
11108
0
        return "WEST";
11109
0
    if (eOrientation == OAO_Up)
11110
0
        return "UP";
11111
0
    if (eOrientation == OAO_Down)
11112
0
        return "DOWN";
11113
0
    if (eOrientation == OAO_Other)
11114
0
        return "OTHER";
11115
11116
0
    return "UNKNOWN";
11117
0
}
11118
11119
/************************************************************************/
11120
/*                              SetAxes()                               */
11121
/************************************************************************/
11122
11123
/**
11124
 * \brief Set the axes for a coordinate system.
11125
 *
11126
 * Set the names, and orientations of the axes for either a projected
11127
 * (PROJCS) or geographic (GEOGCS) coordinate system.
11128
 *
11129
 * This method is equivalent to the C function OSRSetAxes().
11130
 *
11131
 * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11132
 * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11133
 * @param eXAxisOrientation normally OAO_East.
11134
 * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11135
 * @param eYAxisOrientation normally OAO_North.
11136
 *
11137
 * @return OGRERR_NONE on success or an error code.
11138
 */
11139
11140
OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11141
                                    const char *pszXAxisName,
11142
                                    OGRAxisOrientation eXAxisOrientation,
11143
                                    const char *pszYAxisName,
11144
                                    OGRAxisOrientation eYAxisOrientation)
11145
11146
17.9k
{
11147
17.9k
    TAKE_OPTIONAL_LOCK();
11148
11149
    /* -------------------------------------------------------------------- */
11150
    /*      Find the target node.                                           */
11151
    /* -------------------------------------------------------------------- */
11152
17.9k
    OGR_SRSNode *poNode = nullptr;
11153
11154
17.9k
    if (pszTargetKey == nullptr)
11155
17.9k
        poNode = GetRoot();
11156
0
    else
11157
0
        poNode = GetAttrNode(pszTargetKey);
11158
11159
17.9k
    if (poNode == nullptr)
11160
4.40k
        return OGRERR_FAILURE;
11161
11162
    /* -------------------------------------------------------------------- */
11163
    /*      Strip any existing AXIS children.                               */
11164
    /* -------------------------------------------------------------------- */
11165
40.4k
    while (poNode->FindChild("AXIS") >= 0)
11166
26.9k
        poNode->DestroyChild(poNode->FindChild("AXIS"));
11167
11168
    /* -------------------------------------------------------------------- */
11169
    /*      Insert desired axes                                             */
11170
    /* -------------------------------------------------------------------- */
11171
13.5k
    OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11172
11173
13.5k
    poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11174
13.5k
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11175
11176
13.5k
    poNode->AddChild(poAxis);
11177
11178
13.5k
    poAxis = new OGR_SRSNode("AXIS");
11179
11180
13.5k
    poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11181
13.5k
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11182
11183
13.5k
    poNode->AddChild(poAxis);
11184
11185
13.5k
    return OGRERR_NONE;
11186
17.9k
}
11187
11188
/************************************************************************/
11189
/*                             OSRSetAxes()                             */
11190
/************************************************************************/
11191
/**
11192
 * \brief Set the axes for a coordinate system.
11193
 *
11194
 * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11195
 */
11196
OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11197
                  const char *pszXAxisName,
11198
                  OGRAxisOrientation eXAxisOrientation,
11199
                  const char *pszYAxisName,
11200
                  OGRAxisOrientation eYAxisOrientation)
11201
0
{
11202
0
    VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11203
11204
0
    return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11205
0
                                    eXAxisOrientation, pszYAxisName,
11206
0
                                    eYAxisOrientation);
11207
0
}
11208
11209
/************************************************************************/
11210
/*                       OSRExportToMICoordSys()                        */
11211
/************************************************************************/
11212
/**
11213
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11214
 *
11215
 * This method is the equivalent of the C++ method
11216
 * OGRSpatialReference::exportToMICoordSys
11217
 */
11218
OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11219
11220
0
{
11221
0
    VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11222
11223
0
    *ppszReturn = nullptr;
11224
11225
0
    return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11226
0
}
11227
11228
/************************************************************************/
11229
/*                         exportToMICoordSys()                         */
11230
/************************************************************************/
11231
11232
/**
11233
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11234
 *
11235
 * Note that the returned WKT string should be freed with
11236
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
11237
 *
11238
 * This method is the same as the C function OSRExportToMICoordSys().
11239
 *
11240
 * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11241
 * definition will be assigned.
11242
 *
11243
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11244
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11245
 */
11246
11247
OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11248
11249
0
{
11250
0
    *ppszResult = MITABSpatialRef2CoordSys(this);
11251
0
    if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11252
0
        return OGRERR_NONE;
11253
11254
0
    return OGRERR_FAILURE;
11255
0
}
11256
11257
/************************************************************************/
11258
/*                       OSRImportFromMICoordSys()                      */
11259
/************************************************************************/
11260
/**
11261
 * \brief Import Mapinfo style CoordSys definition.
11262
 *
11263
 * This method is the equivalent of the C++ method
11264
 * OGRSpatialReference::importFromMICoordSys
11265
 */
11266
11267
OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11268
                               const char *pszCoordSys)
11269
11270
0
{
11271
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11272
11273
0
    return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11274
0
}
11275
11276
/************************************************************************/
11277
/*                        importFromMICoordSys()                        */
11278
/************************************************************************/
11279
11280
/**
11281
 * \brief Import Mapinfo style CoordSys definition.
11282
 *
11283
 * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11284
 * definition string.
11285
 *
11286
 * This method is the equivalent of the C function OSRImportFromMICoordSys().
11287
 *
11288
 * @param pszCoordSys Mapinfo style CoordSys definition string.
11289
 *
11290
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11291
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11292
 */
11293
11294
OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11295
11296
0
{
11297
0
    OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11298
11299
0
    if (poResult == nullptr)
11300
0
        return OGRERR_FAILURE;
11301
11302
0
    *this = *poResult;
11303
0
    delete poResult;
11304
11305
0
    return OGRERR_NONE;
11306
0
}
11307
11308
/************************************************************************/
11309
/*                        OSRCalcInvFlattening()                        */
11310
/************************************************************************/
11311
11312
/**
11313
 * \brief Compute inverse flattening from semi-major and semi-minor axis
11314
 *
11315
 * @param dfSemiMajor Semi-major axis length.
11316
 * @param dfSemiMinor Semi-minor axis length.
11317
 *
11318
 * @return inverse flattening, or 0 if both axis are equal.
11319
 * @since GDAL 2.0
11320
 */
11321
11322
double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11323
12.2k
{
11324
12.2k
    if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11325
122
        return 0;
11326
12.1k
    if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11327
457
    {
11328
457
        CPLError(CE_Failure, CPLE_IllegalArg,
11329
457
                 "OSRCalcInvFlattening(): Wrong input values");
11330
457
        return 0;
11331
457
    }
11332
11333
11.7k
    return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11334
12.1k
}
11335
11336
/************************************************************************/
11337
/*                        OSRCalcInvFlattening()                        */
11338
/************************************************************************/
11339
11340
/**
11341
 * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11342
 *
11343
 * @param dfSemiMajor Semi-major axis length.
11344
 * @param dfInvFlattening Inverse flattening or 0 for sphere.
11345
 *
11346
 * @return semi-minor axis
11347
 * @since GDAL 2.0
11348
 */
11349
11350
double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11351
                                         double dfInvFlattening)
11352
0
{
11353
0
    if (fabs(dfInvFlattening) < 0.000000000001)
11354
0
        return dfSemiMajor;
11355
0
    if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11356
0
    {
11357
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11358
0
                 "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11359
0
        return dfSemiMajor;
11360
0
    }
11361
11362
0
    return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11363
0
}
11364
11365
/************************************************************************/
11366
/*                        GetWGS84SRS()                                 */
11367
/************************************************************************/
11368
11369
static OGRSpatialReference *poSRSWGS84 = nullptr;
11370
static CPLMutex *hMutex = nullptr;
11371
11372
/**
11373
 * \brief Returns an instance of a SRS object with WGS84 WKT.
11374
 *
11375
 * Note: the instance will have
11376
 * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11377
 *
11378
 * The reference counter of the returned object is not increased by this
11379
 * operation.
11380
 *
11381
 * @return instance.
11382
 * @since GDAL 2.0
11383
 */
11384
11385
OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11386
0
{
11387
0
    CPLMutexHolderD(&hMutex);
11388
0
    if (poSRSWGS84 == nullptr)
11389
0
    {
11390
0
        poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11391
0
        poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11392
0
    }
11393
0
    return poSRSWGS84;
11394
0
}
11395
11396
/************************************************************************/
11397
/*                        CleanupSRSWGS84Mutex()                       */
11398
/************************************************************************/
11399
11400
static void CleanupSRSWGS84Mutex()
11401
0
{
11402
0
    if (hMutex != nullptr)
11403
0
    {
11404
0
        poSRSWGS84->Release();
11405
0
        poSRSWGS84 = nullptr;
11406
0
        CPLDestroyMutex(hMutex);
11407
0
        hMutex = nullptr;
11408
0
    }
11409
0
}
11410
11411
/************************************************************************/
11412
/*                         OSRImportFromProj4()                         */
11413
/************************************************************************/
11414
/**
11415
 * \brief Import PROJ coordinate string.
11416
 *
11417
 * This function is the same as OGRSpatialReference::importFromProj4().
11418
 */
11419
OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11420
11421
0
{
11422
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11423
11424
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11425
0
}
11426
11427
/************************************************************************/
11428
/*                          importFromProj4()                           */
11429
/************************************************************************/
11430
11431
/**
11432
 * \brief Import PROJ coordinate string.
11433
 *
11434
 * The OGRSpatialReference is initialized from the passed PROJs style
11435
 * coordinate system string.
11436
 *
11437
 * Example:
11438
 *   pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11439
 *
11440
 * It is also possible to import "+init=epsg:n" style definitions. Those are
11441
 * a legacy syntax that should be avoided in the future. In particular they will
11442
 * result in CRS objects whose axis order might not correspond to the official
11443
 * EPSG axis order.
11444
 *
11445
 * This method is the equivalent of the C function OSRImportFromProj4().
11446
 *
11447
 * @param pszProj4 the PROJ style string.
11448
 *
11449
 * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11450
 */
11451
11452
OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11453
11454
0
{
11455
0
    TAKE_OPTIONAL_LOCK();
11456
11457
0
    if (strlen(pszProj4) >= 10000)
11458
0
    {
11459
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11460
0
        return OGRERR_CORRUPT_DATA;
11461
0
    }
11462
11463
    /* -------------------------------------------------------------------- */
11464
    /*      Clear any existing definition.                                  */
11465
    /* -------------------------------------------------------------------- */
11466
0
    Clear();
11467
11468
0
    CPLString osProj4(pszProj4);
11469
0
    if (osProj4.find("type=crs") == std::string::npos)
11470
0
    {
11471
0
        osProj4 += " +type=crs";
11472
0
    }
11473
11474
0
    if (osProj4.find("+init=epsg:") != std::string::npos &&
11475
0
        getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11476
0
    {
11477
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11478
0
                     "+init=epsg:XXXX syntax is deprecated. It might return "
11479
0
                     "a CRS with a non-EPSG compliant axis order.");
11480
0
    }
11481
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11482
0
    d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11483
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11484
0
    return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11485
0
}
11486
11487
/************************************************************************/
11488
/*                          OSRExportToProj4()                          */
11489
/************************************************************************/
11490
/**
11491
 * \brief Export coordinate system in PROJ.4 legacy format.
11492
 *
11493
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11494
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11495
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11496
 * will be missing most of the time. PROJ strings to encode CRS should be
11497
 * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11498
 * is the recommended way.
11499
 *
11500
 * This function is the same as OGRSpatialReference::exportToProj4().
11501
 */
11502
OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11503
                                    char **ppszReturn)
11504
11505
0
{
11506
0
    VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11507
11508
0
    *ppszReturn = nullptr;
11509
11510
0
    return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11511
0
}
11512
11513
/************************************************************************/
11514
/*                           exportToProj4()                            */
11515
/************************************************************************/
11516
11517
/**
11518
 * \brief Export coordinate system in PROJ.4 legacy format.
11519
 *
11520
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11521
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11522
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11523
 * will be missing most of the time. PROJ strings to encode CRS should be
11524
 * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11525
 * representation is the recommended way.
11526
 *
11527
 * Converts the loaded coordinate reference system into PROJ format
11528
 * to the extent possible.  The string returned in ppszProj4 should be
11529
 * deallocated by the caller with CPLFree() when no longer needed.
11530
 *
11531
 * LOCAL_CS coordinate systems are not translatable.  An empty string
11532
 * will be returned along with OGRERR_NONE.
11533
 *
11534
 * Special processing for Transverse Mercator:
11535
 * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11536
 * set to YES, the PROJ definition built from the SRS will use the +approx flag
11537
 * for the tmerc and utm projection methods, rather than the more accurate
11538
 * method.
11539
 *
11540
 * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11541
 * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11542
 * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11543
 * added. This automatic addition may be disabled by setting the
11544
 * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11545
 *
11546
 * This method is the equivalent of the C function OSRExportToProj4().
11547
 *
11548
 * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11549
 * will be assigned.
11550
 *
11551
 * @return OGRERR_NONE on success or an error code on failure.
11552
 */
11553
11554
OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11555
11556
0
{
11557
    // In the past calling this method was thread-safe, even if we never
11558
    // guaranteed it. Now proj_as_proj_string() will cache the result
11559
    // internally, so this is no longer thread-safe.
11560
0
    std::lock_guard oLock(d->m_mutex);
11561
11562
0
    d->refreshProjObj();
11563
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11564
0
    {
11565
0
        *ppszProj4 = CPLStrdup("");
11566
0
        return OGRERR_FAILURE;
11567
0
    }
11568
11569
    // OSR_USE_ETMERC is here just for legacy
11570
0
    bool bForceApproxTMerc = false;
11571
0
    const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11572
0
    if (pszUseETMERC && pszUseETMERC[0])
11573
0
    {
11574
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11575
0
                     "OSR_USE_ETMERC is a legacy configuration option, which "
11576
0
                     "now has only effect when set to NO (YES is the default). "
11577
0
                     "Use OSR_USE_APPROX_TMERC=YES instead");
11578
0
        bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11579
0
    }
11580
0
    else
11581
0
    {
11582
0
        const char *pszUseApproxTMERC =
11583
0
            CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11584
0
        if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11585
0
        {
11586
0
            bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11587
0
        }
11588
0
    }
11589
0
    const char *options[] = {
11590
0
        bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11591
11592
0
    const char *projString = proj_as_proj_string(
11593
0
        d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11594
11595
0
    PJ *boundCRS = nullptr;
11596
0
    if (projString &&
11597
0
        (strstr(projString, "+datum=") == nullptr ||
11598
0
         d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11599
0
        CPLTestBool(
11600
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11601
0
    {
11602
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11603
0
            d->getPROJContext(), d->m_pj_crs, true,
11604
0
            strstr(projString, "+datum=") == nullptr);
11605
0
        if (boundCRS)
11606
0
        {
11607
0
            projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11608
0
                                             PJ_PROJ_4, options);
11609
0
        }
11610
0
    }
11611
11612
0
    if (projString == nullptr)
11613
0
    {
11614
0
        *ppszProj4 = CPLStrdup("");
11615
0
        proj_destroy(boundCRS);
11616
0
        return OGRERR_FAILURE;
11617
0
    }
11618
0
    *ppszProj4 = CPLStrdup(projString);
11619
0
    proj_destroy(boundCRS);
11620
0
    char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11621
0
    if (pszTypeCrs)
11622
0
        *pszTypeCrs = '\0';
11623
0
    return OGRERR_NONE;
11624
0
}
11625
11626
/************************************************************************/
11627
/*                            morphToESRI()                             */
11628
/************************************************************************/
11629
/**
11630
 * \brief Convert in place to ESRI WKT format.
11631
 *
11632
 * The value nodes of this coordinate system are modified in various manners
11633
 * more closely map onto the ESRI concept of WKT format.  This includes
11634
 * renaming a variety of projections and arguments, and stripping out
11635
 * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11636
 *
11637
 * \note Since GDAL 3.0, this function has only user-visible effects at
11638
 * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11639
 * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11640
 *
11641
 * This does the same as the C function OSRMorphToESRI().
11642
 *
11643
 * @return OGRERR_NONE unless something goes badly wrong.
11644
 * @deprecated
11645
 */
11646
11647
OGRErr OGRSpatialReference::morphToESRI()
11648
11649
0
{
11650
0
    TAKE_OPTIONAL_LOCK();
11651
11652
0
    d->refreshProjObj();
11653
0
    d->setMorphToESRI(true);
11654
11655
0
    return OGRERR_NONE;
11656
0
}
11657
11658
/************************************************************************/
11659
/*                           OSRMorphToESRI()                           */
11660
/************************************************************************/
11661
11662
/**
11663
 * \brief Convert in place to ESRI WKT format.
11664
 *
11665
 * This function is the same as the C++ method
11666
 * OGRSpatialReference::morphToESRI().
11667
 */
11668
OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11669
11670
0
{
11671
0
    VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11672
11673
0
    return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11674
0
}
11675
11676
/************************************************************************/
11677
/*                           morphFromESRI()                            */
11678
/************************************************************************/
11679
11680
/**
11681
 * \brief Convert in place from ESRI WKT format.
11682
 *
11683
 * The value notes of this coordinate system are modified in various manners
11684
 * to adhere more closely to the WKT standard.  This mostly involves
11685
 * translating a variety of ESRI names for projections, arguments and
11686
 * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11687
 * translation of EPSG to WKT for the CT specification.
11688
 *
11689
 * \note Since GDAL 3.0, this function is essentially a no-operation, since
11690
 * morphing from ESRI is automatically done by importFromWkt(). Its only
11691
 * effect is to undo the effect of a potential prior call to morphToESRI().
11692
 *
11693
 * This does the same as the C function OSRMorphFromESRI().
11694
 *
11695
 * @return OGRERR_NONE unless something goes badly wrong.
11696
 * @deprecated
11697
 */
11698
11699
OGRErr OGRSpatialReference::morphFromESRI()
11700
11701
0
{
11702
0
    TAKE_OPTIONAL_LOCK();
11703
11704
0
    d->refreshProjObj();
11705
0
    d->setMorphToESRI(false);
11706
11707
0
    return OGRERR_NONE;
11708
0
}
11709
11710
/************************************************************************/
11711
/*                          OSRMorphFromESRI()                          */
11712
/************************************************************************/
11713
11714
/**
11715
 * \brief Convert in place from ESRI WKT format.
11716
 *
11717
 * This function is the same as the C++ method
11718
 * OGRSpatialReference::morphFromESRI().
11719
 */
11720
OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11721
11722
0
{
11723
0
    VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11724
11725
0
    return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11726
0
}
11727
11728
/************************************************************************/
11729
/*                            FindMatches()                             */
11730
/************************************************************************/
11731
11732
/**
11733
 * \brief Try to identify a match between the passed SRS and a related SRS
11734
 * in a catalog.
11735
 *
11736
 * Matching may be partial, or may fail.
11737
 * Returned entries will be sorted by decreasing match confidence (first
11738
 * entry has the highest match confidence).
11739
 *
11740
 * The exact way matching is done may change in future versions. Starting with
11741
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
11742
 *
11743
 * This method is the same as OSRFindMatches().
11744
 *
11745
 * @param papszOptions NULL terminated list of options or NULL
11746
 * @param pnEntries Output parameter. Number of values in the returned array.
11747
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11748
 * will be allocated to an array of *pnEntries whose values between 0 and 100
11749
 * indicate the confidence in the match. 100 is the highest confidence level.
11750
 * The array must be freed with CPLFree().
11751
 *
11752
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11753
 * with OSRFreeSRSArray()
11754
 *
11755
 * @since GDAL 2.3
11756
 *
11757
 * @see OGRSpatialReference::FindBestMatch()
11758
 */
11759
OGRSpatialReferenceH *
11760
OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11761
                                 int **ppanMatchConfidence) const
11762
772
{
11763
772
    TAKE_OPTIONAL_LOCK();
11764
11765
772
    CPL_IGNORE_RET_VAL(papszOptions);
11766
11767
772
    if (pnEntries)
11768
772
        *pnEntries = 0;
11769
772
    if (ppanMatchConfidence)
11770
772
        *ppanMatchConfidence = nullptr;
11771
11772
772
    d->refreshProjObj();
11773
772
    if (!d->m_pj_crs)
11774
16
        return nullptr;
11775
11776
756
    int *panConfidence = nullptr;
11777
756
    auto ctxt = d->getPROJContext();
11778
756
    auto list =
11779
756
        proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11780
756
    if (!list)
11781
0
        return nullptr;
11782
11783
756
    const int nMatches = proj_list_get_count(list);
11784
11785
756
    if (pnEntries)
11786
756
        *pnEntries = static_cast<int>(nMatches);
11787
756
    OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11788
756
        CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11789
756
    if (ppanMatchConfidence)
11790
756
    {
11791
756
        *ppanMatchConfidence =
11792
756
            static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11793
756
    }
11794
11795
756
    bool bSortAgain = false;
11796
11797
122k
    for (int i = 0; i < nMatches; i++)
11798
122k
    {
11799
122k
        PJ *obj = proj_list_get(ctxt, list, i);
11800
122k
        CPLAssert(obj);
11801
122k
        OGRSpatialReference *poSRS = new OGRSpatialReference();
11802
122k
        poSRS->d->setPjCRS(obj);
11803
122k
        pahRet[i] = ToHandle(poSRS);
11804
11805
        // Identify matches that only differ by axis order
11806
122k
        if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11807
122k
            poSRS->GetAxesCount() == 2 &&
11808
122k
            GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11809
0
        {
11810
0
            OGRAxisOrientation eThisAxis0 = OAO_Other;
11811
0
            OGRAxisOrientation eThisAxis1 = OAO_Other;
11812
0
            OGRAxisOrientation eSRSAxis0 = OAO_Other;
11813
0
            OGRAxisOrientation eSRSAxis1 = OAO_Other;
11814
0
            GetAxis(nullptr, 0, &eThisAxis0);
11815
0
            GetAxis(nullptr, 1, &eThisAxis1);
11816
0
            poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11817
0
            poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11818
0
            if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11819
0
                eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11820
0
            {
11821
0
                auto pj_crs_normalized =
11822
0
                    proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11823
0
                if (pj_crs_normalized)
11824
0
                {
11825
0
                    if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11826
0
                                              PJ_COMP_EQUIVALENT))
11827
0
                    {
11828
0
                        bSortAgain = true;
11829
0
                        panConfidence[i] = 90;
11830
0
                        poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11831
0
                    }
11832
0
                    proj_destroy(pj_crs_normalized);
11833
0
                }
11834
0
            }
11835
0
        }
11836
11837
122k
        if (ppanMatchConfidence)
11838
122k
            (*ppanMatchConfidence)[i] = panConfidence[i];
11839
122k
    }
11840
11841
756
    if (bSortAgain)
11842
0
    {
11843
0
        std::vector<int> anIndices;
11844
0
        for (int i = 0; i < nMatches; ++i)
11845
0
            anIndices.push_back(i);
11846
11847
0
        std::stable_sort(anIndices.begin(), anIndices.end(),
11848
0
                         [&panConfidence](int i, int j)
11849
0
                         { return panConfidence[i] > panConfidence[j]; });
11850
11851
0
        OGRSpatialReferenceH *pahRetSorted =
11852
0
            static_cast<OGRSpatialReferenceH *>(
11853
0
                CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11854
0
        for (int i = 0; i < nMatches; ++i)
11855
0
        {
11856
0
            pahRetSorted[i] = pahRet[anIndices[i]];
11857
0
            if (ppanMatchConfidence)
11858
0
                (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11859
0
        }
11860
0
        CPLFree(pahRet);
11861
0
        pahRet = pahRetSorted;
11862
0
    }
11863
11864
756
    pahRet[nMatches] = nullptr;
11865
756
    proj_list_destroy(list);
11866
756
    proj_int_list_destroy(panConfidence);
11867
11868
756
    return pahRet;
11869
756
}
11870
11871
/************************************************************************/
11872
/*                          importFromEPSGA()                           */
11873
/************************************************************************/
11874
11875
/**
11876
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
11877
 * code.
11878
 *
11879
 * This method will initialize the spatial reference based on the
11880
 * passed in EPSG CRS code found in the PROJ database.
11881
 *
11882
 * Since GDAL 3.0, this method is identical to importFromEPSG().
11883
 *
11884
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11885
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
11886
 * such method available for the CRS. This behavior might not always be
11887
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11888
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11889
 * The AddGuessedTOWGS84() method can also be used for that purpose.
11890
 *
11891
 * The method will also by default substitute a deprecated EPSG code by its
11892
 * non-deprecated replacement. If this behavior is not desired, the
11893
 * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
11894
 *
11895
 * This method is the same as the C function OSRImportFromEPSGA().
11896
 *
11897
 * @param nCode a CRS code.
11898
 *
11899
 * @return OGRERR_NONE on success, or an error code on failure.
11900
 */
11901
11902
OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
11903
11904
33.1k
{
11905
33.1k
    TAKE_OPTIONAL_LOCK();
11906
11907
33.1k
    Clear();
11908
11909
33.1k
    const char *pszUseNonDeprecated =
11910
33.1k
        CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
11911
33.1k
    const bool bUseNonDeprecated =
11912
33.1k
        CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
11913
33.1k
    const bool bAddTOWGS84 = CPLTestBool(
11914
33.1k
        CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
11915
33.1k
    auto tlsCache = OSRGetProjTLSCache();
11916
33.1k
    if (tlsCache)
11917
33.1k
    {
11918
33.1k
        auto cachedObj =
11919
33.1k
            tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
11920
33.1k
        if (cachedObj)
11921
23.2k
        {
11922
23.2k
            d->setPjCRS(cachedObj);
11923
23.2k
            return OGRERR_NONE;
11924
23.2k
        }
11925
33.1k
    }
11926
11927
9.92k
    CPLString osCode;
11928
9.92k
    osCode.Printf("%d", nCode);
11929
9.92k
    PJ *obj;
11930
9.92k
    constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
11931
9.92k
    if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
11932
9.48k
    {
11933
9.48k
        obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11934
9.48k
                                        osCode.c_str(), PJ_CATEGORY_CRS, true,
11935
9.48k
                                        nullptr);
11936
9.48k
        if (!obj)
11937
8.72k
        {
11938
8.72k
            return OGRERR_FAILURE;
11939
8.72k
        }
11940
9.48k
    }
11941
444
    else
11942
444
    {
11943
        // Likely to be an ESRI CRS...
11944
444
        CPLErr eLastErrorType = CE_None;
11945
444
        CPLErrorNum eLastErrorNum = CPLE_None;
11946
444
        std::string osLastErrorMsg;
11947
444
        bool bIsESRI = false;
11948
444
        {
11949
444
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
11950
444
            CPLErrorReset();
11951
444
            obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11952
444
                                            osCode.c_str(), PJ_CATEGORY_CRS,
11953
444
                                            true, nullptr);
11954
444
            if (!obj)
11955
444
            {
11956
444
                eLastErrorType = CPLGetLastErrorType();
11957
444
                eLastErrorNum = CPLGetLastErrorNo();
11958
444
                osLastErrorMsg = CPLGetLastErrorMsg();
11959
444
                obj = proj_create_from_database(d->getPROJContext(), "ESRI",
11960
444
                                                osCode.c_str(), PJ_CATEGORY_CRS,
11961
444
                                                true, nullptr);
11962
444
                if (obj)
11963
0
                    bIsESRI = true;
11964
444
            }
11965
444
        }
11966
444
        if (!obj)
11967
444
        {
11968
444
            if (eLastErrorType != CE_None)
11969
444
                CPLError(eLastErrorType, eLastErrorNum, "%s",
11970
444
                         osLastErrorMsg.c_str());
11971
444
            return OGRERR_FAILURE;
11972
444
        }
11973
0
        if (bIsESRI)
11974
0
        {
11975
0
            CPLError(CE_Warning, CPLE_AppDefined,
11976
0
                     "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
11977
0
                     "Assuming ESRI:%d was meant",
11978
0
                     nCode, nCode, nCode);
11979
0
        }
11980
0
    }
11981
11982
757
    if (bUseNonDeprecated && proj_is_deprecated(obj))
11983
89
    {
11984
89
        auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
11985
89
        if (list)
11986
89
        {
11987
89
            const auto count = proj_list_get_count(list);
11988
89
            if (count == 1)
11989
62
            {
11990
62
                auto nonDeprecated =
11991
62
                    proj_list_get(d->getPROJContext(), list, 0);
11992
62
                if (nonDeprecated)
11993
62
                {
11994
62
                    if (pszUseNonDeprecated == nullptr)
11995
62
                    {
11996
62
                        const char *pszNewAuth =
11997
62
                            proj_get_id_auth_name(nonDeprecated, 0);
11998
62
                        const char *pszNewCode =
11999
62
                            proj_get_id_code(nonDeprecated, 0);
12000
62
                        CPLError(CE_Warning, CPLE_AppDefined,
12001
62
                                 "CRS EPSG:%d is deprecated. "
12002
62
                                 "Its non-deprecated replacement %s:%s "
12003
62
                                 "will be used instead. "
12004
62
                                 "To use the original CRS, set the "
12005
62
                                 "OSR_USE_NON_DEPRECATED "
12006
62
                                 "configuration option to NO.",
12007
62
                                 nCode, pszNewAuth ? pszNewAuth : "(null)",
12008
62
                                 pszNewCode ? pszNewCode : "(null)");
12009
62
                    }
12010
62
                    proj_destroy(obj);
12011
62
                    obj = nonDeprecated;
12012
62
                }
12013
62
            }
12014
89
        }
12015
89
        proj_list_destroy(list);
12016
89
    }
12017
12018
757
    if (bAddTOWGS84)
12019
0
    {
12020
0
        auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12021
0
                                                           obj, nullptr);
12022
0
        if (boundCRS)
12023
0
        {
12024
0
            proj_destroy(obj);
12025
0
            obj = boundCRS;
12026
0
        }
12027
0
    }
12028
12029
757
    d->setPjCRS(obj);
12030
12031
757
    if (tlsCache)
12032
757
    {
12033
757
        tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12034
757
                                     obj);
12035
757
    }
12036
12037
757
    return OGRERR_NONE;
12038
9.92k
}
12039
12040
/************************************************************************/
12041
/*                          AddGuessedTOWGS84()                         */
12042
/************************************************************************/
12043
12044
/**
12045
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12046
 * to WGS84.
12047
 *
12048
 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12049
 * to WGS84 when there is one and only one such method available for the CRS.
12050
 * Note: this is more restrictive to how GDAL < 3 worked.
12051
 *
12052
 * This method is the same as the C function OSRAddGuessedTOWGS84().
12053
 *
12054
 * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12055
 * already a transformation to WGS84 or none matching could be found).
12056
 *
12057
 * @since GDAL 3.0.3
12058
 */
12059
OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12060
0
{
12061
0
    TAKE_OPTIONAL_LOCK();
12062
12063
0
    d->refreshProjObj();
12064
0
    if (!d->m_pj_crs)
12065
0
        return OGRERR_FAILURE;
12066
0
    auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12067
0
        d->getPROJContext(), d->m_pj_crs, false, true);
12068
0
    if (!boundCRS)
12069
0
    {
12070
0
        return OGRERR_FAILURE;
12071
0
    }
12072
0
    d->setPjCRS(boundCRS);
12073
0
    return OGRERR_NONE;
12074
0
}
12075
12076
/************************************************************************/
12077
/*                         OSRImportFromEPSGA()                         */
12078
/************************************************************************/
12079
12080
/**
12081
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12082
 * to WGS84.
12083
 *
12084
 * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12085
 *
12086
 * @since GDAL 3.0.3
12087
 */
12088
12089
OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12090
12091
0
{
12092
0
    VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12093
12094
0
    return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12095
0
}
12096
12097
/************************************************************************/
12098
/*                         OSRImportFromEPSGA()                         */
12099
/************************************************************************/
12100
12101
/**
12102
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12103
 * code.
12104
 *
12105
 * This function is the same as OGRSpatialReference::importFromEPSGA().
12106
 */
12107
12108
OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12109
12110
0
{
12111
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12112
12113
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12114
0
}
12115
12116
/************************************************************************/
12117
/*                           importFromEPSG()                           */
12118
/************************************************************************/
12119
12120
/**
12121
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12122
 * code.
12123
 *
12124
 * This method will initialize the spatial reference based on the
12125
 * passed in EPSG CRS code found in the PROJ database.
12126
 *
12127
 * This method is the same as the C function OSRImportFromEPSG().
12128
 *
12129
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12130
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12131
 * such method available for the CRS. This behavior might not always be
12132
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12133
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12134
 *
12135
 * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12136
 *
12137
 * @return OGRERR_NONE on success, or an error code on failure.
12138
 */
12139
12140
OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12141
12142
33.1k
{
12143
33.1k
    return importFromEPSGA(nCode);
12144
33.1k
}
12145
12146
/************************************************************************/
12147
/*                         OSRImportFromEPSG()                          */
12148
/************************************************************************/
12149
12150
/**
12151
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12152
 * code.
12153
 *
12154
 * This function is the same as OGRSpatialReference::importFromEPSG().
12155
 */
12156
12157
OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12158
12159
0
{
12160
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12161
12162
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12163
0
}
12164
12165
/************************************************************************/
12166
/*                        EPSGTreatsAsLatLong()                         */
12167
/************************************************************************/
12168
12169
/**
12170
 * \brief This method returns TRUE if this geographic coordinate
12171
 * system should be treated as having lat/long coordinate ordering.
12172
 *
12173
 * Currently this returns TRUE for all geographic coordinate systems
12174
 * with axes set defining it as lat, long (prior to GDAL 3.10, it
12175
 * also checked that the CRS had belonged to EPSG authority, but this check
12176
 * has now been removed).
12177
 *
12178
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12179
 * geographic CRS imported with importFromEPSG() would cause this method to
12180
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12181
 * is now equivalent to importFromEPSGA().
12182
 *
12183
 * FALSE will be returned for all coordinate systems that are not geographic,
12184
 * or whose axes ordering is not latitude, longitude.
12185
 *
12186
 * This method is the same as the C function OSREPSGTreatsAsLatLong().
12187
 *
12188
 * @return TRUE or FALSE.
12189
 */
12190
12191
int OGRSpatialReference::EPSGTreatsAsLatLong() const
12192
12193
0
{
12194
0
    TAKE_OPTIONAL_LOCK();
12195
12196
0
    if (!IsGeographic())
12197
0
        return FALSE;
12198
12199
0
    d->demoteFromBoundCRS();
12200
12201
0
    bool ret = false;
12202
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12203
0
    {
12204
0
        auto horizCRS =
12205
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12206
0
        if (horizCRS)
12207
0
        {
12208
0
            auto cs =
12209
0
                proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12210
0
            if (cs)
12211
0
            {
12212
0
                const char *pszDirection = nullptr;
12213
0
                if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12214
0
                                          nullptr, &pszDirection, nullptr,
12215
0
                                          nullptr, nullptr, nullptr))
12216
0
                {
12217
0
                    if (EQUAL(pszDirection, "north"))
12218
0
                    {
12219
0
                        ret = true;
12220
0
                    }
12221
0
                }
12222
12223
0
                proj_destroy(cs);
12224
0
            }
12225
12226
0
            proj_destroy(horizCRS);
12227
0
        }
12228
0
    }
12229
0
    else
12230
0
    {
12231
0
        auto cs =
12232
0
            proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12233
0
        if (cs)
12234
0
        {
12235
0
            const char *pszDirection = nullptr;
12236
0
            if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12237
0
                                      nullptr, &pszDirection, nullptr, nullptr,
12238
0
                                      nullptr, nullptr))
12239
0
            {
12240
0
                if (EQUAL(pszDirection, "north"))
12241
0
                {
12242
0
                    ret = true;
12243
0
                }
12244
0
            }
12245
12246
0
            proj_destroy(cs);
12247
0
        }
12248
0
    }
12249
0
    d->undoDemoteFromBoundCRS();
12250
12251
0
    return ret;
12252
0
}
12253
12254
/************************************************************************/
12255
/*                       OSREPSGTreatsAsLatLong()                       */
12256
/************************************************************************/
12257
12258
/**
12259
 * \brief This function returns TRUE if this geographic coordinate
12260
 * system should be treated as having lat/long coordinate ordering.
12261
 *
12262
 * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12263
 */
12264
12265
int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12266
12267
0
{
12268
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12269
12270
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12271
0
}
12272
12273
/************************************************************************/
12274
/*                     EPSGTreatsAsNorthingEasting()                    */
12275
/************************************************************************/
12276
12277
/**
12278
 * \brief This method returns TRUE if this projected coordinate
12279
 * system should be treated as having northing/easting coordinate ordering.
12280
 *
12281
 * Currently this returns TRUE for all projected coordinate systems
12282
 * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12283
 * also checked that the CRS had belonged to EPSG authority, but this check
12284
 * has now been removed).
12285
 *
12286
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12287
 * projected CRS with northing, easting axis order imported with
12288
 * importFromEPSG() would cause this method to
12289
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12290
 * is now equivalent to importFromEPSGA().
12291
 *
12292
 * FALSE will be returned for all coordinate systems that are not projected,
12293
 * or whose axes ordering is not northing, easting.
12294
 *
12295
 * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12296
 *
12297
 * @return TRUE or FALSE.
12298
 *
12299
 * @since OGR 1.10.0
12300
 */
12301
12302
int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12303
12304
0
{
12305
0
    TAKE_OPTIONAL_LOCK();
12306
12307
0
    if (!IsProjected())
12308
0
        return FALSE;
12309
12310
0
    d->demoteFromBoundCRS();
12311
0
    PJ *projCRS;
12312
0
    const auto ctxt = d->getPROJContext();
12313
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12314
0
    {
12315
0
        projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12316
0
        if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12317
0
        {
12318
0
            d->undoDemoteFromBoundCRS();
12319
0
            proj_destroy(projCRS);
12320
0
            return FALSE;
12321
0
        }
12322
0
    }
12323
0
    else
12324
0
    {
12325
0
        projCRS = proj_clone(ctxt, d->m_pj_crs);
12326
0
    }
12327
12328
0
    bool ret = false;
12329
0
    auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12330
0
    proj_destroy(projCRS);
12331
0
    d->undoDemoteFromBoundCRS();
12332
12333
0
    if (cs)
12334
0
    {
12335
0
        ret = isNorthEastAxisOrder(ctxt, cs);
12336
0
        proj_destroy(cs);
12337
0
    }
12338
12339
0
    return ret;
12340
0
}
12341
12342
/************************************************************************/
12343
/*                     OSREPSGTreatsAsNorthingEasting()                 */
12344
/************************************************************************/
12345
12346
/**
12347
 * \brief This function returns TRUE if this projected coordinate
12348
 * system should be treated as having northing/easting coordinate ordering.
12349
 *
12350
 * This function is the same as
12351
 * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12352
 *
12353
 * @since OGR 1.10.0
12354
 */
12355
12356
int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12357
12358
0
{
12359
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12360
12361
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12362
0
}
12363
12364
/************************************************************************/
12365
/*                     ImportFromESRIWisconsinWKT()                     */
12366
/*                                                                      */
12367
/*      Search a ESRI State Plane WKT and import it.                    */
12368
/************************************************************************/
12369
12370
// This is only used by the HFA driver and somewhat dubious we really need that
12371
// Coming from an old ESRI merge
12372
12373
OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12374
                                                       double centralMeridian,
12375
                                                       double latOfOrigin,
12376
                                                       const char *unitsName,
12377
                                                       const char *crsName)
12378
0
{
12379
0
    TAKE_OPTIONAL_LOCK();
12380
12381
0
    if (centralMeridian < -93 || centralMeridian > -87)
12382
0
        return OGRERR_FAILURE;
12383
0
    if (latOfOrigin < 40 || latOfOrigin > 47)
12384
0
        return OGRERR_FAILURE;
12385
12386
    // If the CS name is known.
12387
0
    if (!prjName && !unitsName && crsName)
12388
0
    {
12389
0
        const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12390
0
        PJ_OBJ_LIST *list = proj_create_from_name(
12391
0
            d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12392
0
        if (list)
12393
0
        {
12394
0
            if (proj_list_get_count(list) == 1)
12395
0
            {
12396
0
                auto crs = proj_list_get(d->getPROJContext(), list, 0);
12397
0
                if (crs)
12398
0
                {
12399
0
                    Clear();
12400
0
                    d->setPjCRS(crs);
12401
0
                    proj_list_destroy(list);
12402
0
                    return OGRERR_NONE;
12403
0
                }
12404
0
            }
12405
0
            proj_list_destroy(list);
12406
0
        }
12407
0
        return OGRERR_FAILURE;
12408
0
    }
12409
12410
0
    if (prjName == nullptr || unitsName == nullptr)
12411
0
    {
12412
0
        return OGRERR_FAILURE;
12413
0
    }
12414
12415
0
    const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12416
0
    PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12417
0
                                              "NAD_1983_HARN_WISCRS_", &type, 1,
12418
0
                                              true, 0, nullptr);
12419
0
    if (list)
12420
0
    {
12421
0
        const auto listSize = proj_list_get_count(list);
12422
0
        for (int i = 0; i < listSize; i++)
12423
0
        {
12424
0
            auto crs = proj_list_get(d->getPROJContext(), list, i);
12425
0
            if (!crs)
12426
0
            {
12427
0
                continue;
12428
0
            }
12429
12430
0
            auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12431
0
            if (!conv)
12432
0
            {
12433
0
                proj_destroy(crs);
12434
0
                continue;
12435
0
            }
12436
0
            const char *pszMethodCode = nullptr;
12437
0
            proj_coordoperation_get_method_info(
12438
0
                d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12439
0
            const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12440
0
            if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12441
0
                   nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12442
0
                  (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12443
0
                   nMethodCode ==
12444
0
                       EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12445
0
            {
12446
0
                proj_destroy(crs);
12447
0
                proj_destroy(conv);
12448
0
                continue;
12449
0
            }
12450
12451
0
            auto coordSys =
12452
0
                proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12453
0
            if (!coordSys)
12454
0
            {
12455
0
                proj_destroy(crs);
12456
0
                proj_destroy(conv);
12457
0
                continue;
12458
0
            }
12459
12460
0
            double dfConvFactor = 0.0;
12461
0
            proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12462
0
                                  nullptr, nullptr, &dfConvFactor, nullptr,
12463
0
                                  nullptr, nullptr);
12464
0
            proj_destroy(coordSys);
12465
12466
0
            if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12467
0
                (!EQUAL(unitsName, "meters") &&
12468
0
                 std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12469
0
                     1e-10))
12470
0
            {
12471
0
                proj_destroy(crs);
12472
0
                proj_destroy(conv);
12473
0
                continue;
12474
0
            }
12475
12476
0
            int idx_lat = proj_coordoperation_get_param_index(
12477
0
                d->getPROJContext(), conv,
12478
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12479
0
            double valueLat = -1000;
12480
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12481
0
                                          nullptr, nullptr, nullptr, &valueLat,
12482
0
                                          nullptr, nullptr, nullptr, nullptr,
12483
0
                                          nullptr, nullptr);
12484
0
            int idx_lon = proj_coordoperation_get_param_index(
12485
0
                d->getPROJContext(), conv,
12486
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12487
0
            double valueLong = -1000;
12488
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12489
0
                                          nullptr, nullptr, nullptr, &valueLong,
12490
0
                                          nullptr, nullptr, nullptr, nullptr,
12491
0
                                          nullptr, nullptr);
12492
0
            if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12493
0
                std::fabs(latOfOrigin - valueLat) <= 1e-10)
12494
0
            {
12495
0
                Clear();
12496
0
                d->setPjCRS(crs);
12497
0
                proj_list_destroy(list);
12498
0
                proj_destroy(conv);
12499
0
                return OGRERR_NONE;
12500
0
            }
12501
12502
0
            proj_destroy(crs);
12503
0
            proj_destroy(conv);
12504
0
        }
12505
0
        proj_list_destroy(list);
12506
0
    }
12507
12508
0
    return OGRERR_FAILURE;
12509
0
}
12510
12511
/************************************************************************/
12512
/*                      GetAxisMappingStrategy()                        */
12513
/************************************************************************/
12514
12515
/** \brief Return the data axis to CRS axis mapping strategy.
12516
 *
12517
 * <ul>
12518
 * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12519
 *     lat/long order, the data will still be long/lat ordered. Similarly for
12520
 *     a projected CRS with northing/easting order, the data will still be
12521
 *     easting/northing ordered.
12522
 * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12523
 *     the CRS axis.
12524
 * <li>OAMS_CUSTOM means that the data axis are customly defined with
12525
 *     SetDataAxisToSRSAxisMapping()
12526
 * </ul>
12527
 * @return the data axis to CRS axis mapping strategy.
12528
 * @since GDAL 3.0
12529
 */
12530
OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12531
0
{
12532
0
    TAKE_OPTIONAL_LOCK();
12533
12534
0
    return d->m_axisMappingStrategy;
12535
0
}
12536
12537
/************************************************************************/
12538
/*                      OSRGetAxisMappingStrategy()                     */
12539
/************************************************************************/
12540
12541
/** \brief Return the data axis to CRS axis mapping strategy.
12542
 *
12543
 * See OGRSpatialReference::GetAxisMappingStrategy()
12544
 * @since GDAL 3.0
12545
 */
12546
OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12547
0
{
12548
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12549
12550
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12551
0
}
12552
12553
/************************************************************************/
12554
/*                      SetAxisMappingStrategy()                        */
12555
/************************************************************************/
12556
12557
/** \brief Set the data axis to CRS axis mapping strategy.
12558
 *
12559
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12560
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12561
 * later being the default value when the option is not set) to control the
12562
 * value of the data axis to CRS axis mapping strategy when a
12563
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12564
 * override this default value.
12565
 *
12566
 * See OGRSpatialReference::GetAxisMappingStrategy()
12567
 * @since GDAL 3.0
12568
 */
12569
void OGRSpatialReference::SetAxisMappingStrategy(
12570
    OSRAxisMappingStrategy strategy)
12571
69.4k
{
12572
69.4k
    TAKE_OPTIONAL_LOCK();
12573
12574
69.4k
    d->m_axisMappingStrategy = strategy;
12575
69.4k
    d->refreshAxisMapping();
12576
69.4k
}
12577
12578
/************************************************************************/
12579
/*                      OSRSetAxisMappingStrategy()                     */
12580
/************************************************************************/
12581
12582
/** \brief Set the data axis to CRS axis mapping strategy.
12583
 *
12584
 * See OGRSpatialReference::SetAxisMappingStrategy()
12585
 * @since GDAL 3.0
12586
 */
12587
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12588
                               OSRAxisMappingStrategy strategy)
12589
0
{
12590
0
    VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12591
12592
0
    OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12593
0
}
12594
12595
/************************************************************************/
12596
/*                      GetDataAxisToSRSAxisMapping()                   */
12597
/************************************************************************/
12598
12599
/** \brief Return the data axis to SRS axis mapping.
12600
 *
12601
 * The number of elements of the vector will be the number of axis of the CRS.
12602
 * Values start at 1.
12603
 *
12604
 * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12605
 * for the first axis of the CRS.
12606
 *
12607
 * @since GDAL 3.0
12608
 */
12609
const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12610
29.1k
{
12611
29.1k
    TAKE_OPTIONAL_LOCK();
12612
12613
29.1k
    return d->m_axisMapping;
12614
29.1k
}
12615
12616
/************************************************************************/
12617
/*                     OSRGetDataAxisToSRSAxisMapping()                 */
12618
/************************************************************************/
12619
12620
/** \brief Return the data axis to SRS axis mapping.
12621
 *
12622
 * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12623
 *
12624
 * @since GDAL 3.0
12625
 */
12626
const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12627
                                          int *pnCount)
12628
0
{
12629
0
    VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12630
0
    VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12631
12632
0
    const auto &v =
12633
0
        OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12634
0
    *pnCount = static_cast<int>(v.size());
12635
0
    return v.data();
12636
0
}
12637
12638
/************************************************************************/
12639
/*                      SetDataAxisToSRSAxisMapping()                   */
12640
/************************************************************************/
12641
12642
/** \brief Set a custom data axis to CRS axis mapping.
12643
 *
12644
 * The number of elements of the mapping vector should be the number of axis
12645
 * of the CRS (as returned by GetAxesCount()) (although this method does not
12646
 * check that, beyond checking there are at least 2 elements, so that this
12647
 * method and setting the CRS can be done in any order).
12648
 * This is taken into account by OGRCoordinateTransformation to transform the
12649
 * order of coordinates to the order expected by the CRS before
12650
 * transformation, and back to the data order after transformation.
12651
 *
12652
 * The mapping[i] value (one based) represents the data axis number for the i(th)
12653
 * axis of the CRS. A negative value can also be used to ask for a sign
12654
 * reversal during coordinate transformation (to deal with northing vs southing,
12655
 * easting vs westing, heights vs depths).
12656
 *
12657
 * When used with OGRCoordinateTransformation,
12658
 * - the only valid values for mapping[0] (data axis number for the first axis
12659
 *   of the CRS) are 1, 2, -1, -2.
12660
 * - the only valid values for mapping[1] (data axis number for the second axis
12661
 *   of the CRS) are 1, 2, -1, -2.
12662
 *  - the only valid values mapping[2] are 3 or -3.
12663
 * Note: this method does not validate the values of mapping[].
12664
 *
12665
 * mapping=[2,1] typically expresses the inversion of axis between the data
12666
 * axis and the CRS axis for a 2D CRS.
12667
 *
12668
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12669
 *
12670
 * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12671
 *
12672
 * @param mapping The new data axis to CRS axis mapping.
12673
 *
12674
 * @since GDAL 3.0
12675
 * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12676
 */
12677
OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12678
    const std::vector<int> &mapping)
12679
0
{
12680
0
    TAKE_OPTIONAL_LOCK();
12681
12682
0
    if (mapping.size() < 2)
12683
0
        return OGRERR_FAILURE;
12684
0
    d->m_axisMappingStrategy = OAMS_CUSTOM;
12685
0
    d->m_axisMapping = mapping;
12686
0
    return OGRERR_NONE;
12687
0
}
12688
12689
/************************************************************************/
12690
/*                     OSRSetDataAxisToSRSAxisMapping()                 */
12691
/************************************************************************/
12692
12693
/** \brief Set a custom data axis to CRS axis mapping.
12694
 *
12695
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12696
 *
12697
 * This is the same as the C++ method
12698
 * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12699
 *
12700
 * @since GDAL 3.1
12701
 */
12702
OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12703
                                      int nMappingSize, const int *panMapping)
12704
0
{
12705
0
    VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12706
0
    VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12707
0
                      OGRERR_FAILURE);
12708
12709
0
    if (nMappingSize < 0)
12710
0
        return OGRERR_FAILURE;
12711
12712
0
    std::vector<int> mapping(nMappingSize);
12713
0
    if (nMappingSize)
12714
0
        memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12715
0
    return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12716
0
        mapping);
12717
0
}
12718
12719
/************************************************************************/
12720
/*                               GetAreaOfUse()                         */
12721
/************************************************************************/
12722
12723
/** \brief Return the area of use of the CRS.
12724
 *
12725
 * This method is the same as the OSRGetAreaOfUse() function.
12726
 *
12727
 * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12728
 * longitude, expressed in degree. Might be NULL. If the returned value is
12729
 * -1000, the bounding box is unknown.
12730
 * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12731
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12732
 * the bounding box is unknown.
12733
 * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12734
 * longitude, expressed in degree. Might be NULL. If the returned value is
12735
 * -1000, the bounding box is unknown.
12736
 * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12737
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12738
 * the bounding box is unknown.
12739
 * @param ppszAreaName Pointer to a string to receive the name of the area of
12740
 * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12741
 * invalidated by further calls.
12742
 * @return true in case of success
12743
 * @since GDAL 3.0
12744
 */
12745
bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12746
                                       double *pdfSouthLatitudeDeg,
12747
                                       double *pdfEastLongitudeDeg,
12748
                                       double *pdfNorthLatitudeDeg,
12749
                                       const char **ppszAreaName) const
12750
0
{
12751
0
    TAKE_OPTIONAL_LOCK();
12752
12753
0
    d->refreshProjObj();
12754
0
    if (!d->m_pj_crs)
12755
0
    {
12756
0
        return false;
12757
0
    }
12758
0
    d->demoteFromBoundCRS();
12759
0
    const char *pszAreaName = nullptr;
12760
0
    int bSuccess = proj_get_area_of_use(
12761
0
        d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12762
0
        pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12763
0
        &pszAreaName);
12764
0
    d->undoDemoteFromBoundCRS();
12765
0
    d->m_osAreaName = pszAreaName ? pszAreaName : "";
12766
0
    if (ppszAreaName)
12767
0
        *ppszAreaName = d->m_osAreaName.c_str();
12768
0
    return CPL_TO_BOOL(bSuccess);
12769
0
}
12770
12771
/************************************************************************/
12772
/*                               GetAreaOfUse()                         */
12773
/************************************************************************/
12774
12775
/** \brief Return the area of use of the CRS.
12776
 *
12777
 * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12778
 *
12779
 * @since GDAL 3.0
12780
 */
12781
int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12782
                    double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12783
                    double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12784
0
{
12785
0
    VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12786
12787
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12788
0
        pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12789
0
        pdfNorthLatitudeDeg, ppszAreaName);
12790
0
}
12791
12792
/************************************************************************/
12793
/*                     OSRGetCRSInfoListFromDatabase()                  */
12794
/************************************************************************/
12795
12796
/** \brief Enumerate CRS objects from the database.
12797
 *
12798
 * The returned object is an array of OSRCRSInfo* pointers, whose last
12799
 * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12800
 *
12801
 * @param pszAuthName Authority name, used to restrict the search.
12802
 * Or NULL for all authorities.
12803
 * @param params Additional criteria. Must be set to NULL for now.
12804
 * @param pnOutResultCount Output parameter pointing to an integer to receive
12805
 * the size of the result list. Might be NULL
12806
 * @return an array of OSRCRSInfo* pointers to be freed with
12807
 * OSRDestroyCRSInfoList(), or NULL in case of error.
12808
 *
12809
 * @since GDAL 3.0
12810
 */
12811
OSRCRSInfo **
12812
OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12813
                              CPL_UNUSED const OSRCRSListParameters *params,
12814
                              int *pnOutResultCount)
12815
0
{
12816
0
    int nResultCount = 0;
12817
0
    auto projList = proj_get_crs_info_list_from_database(
12818
0
        OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12819
0
    if (pnOutResultCount)
12820
0
        *pnOutResultCount = nResultCount;
12821
0
    if (!projList)
12822
0
    {
12823
0
        return nullptr;
12824
0
    }
12825
0
    auto res = new OSRCRSInfo *[nResultCount + 1];
12826
0
    for (int i = 0; i < nResultCount; i++)
12827
0
    {
12828
0
        res[i] = new OSRCRSInfo;
12829
0
        res[i]->pszAuthName = projList[i]->auth_name
12830
0
                                  ? CPLStrdup(projList[i]->auth_name)
12831
0
                                  : nullptr;
12832
0
        res[i]->pszCode =
12833
0
            projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12834
0
        res[i]->pszName =
12835
0
            projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12836
0
        res[i]->eType = OSR_CRS_TYPE_OTHER;
12837
0
        switch (projList[i]->type)
12838
0
        {
12839
0
            case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12840
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12841
0
                break;
12842
0
            case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12843
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12844
0
                break;
12845
0
            case PJ_TYPE_GEOCENTRIC_CRS:
12846
0
                res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12847
0
                break;
12848
0
            case PJ_TYPE_PROJECTED_CRS:
12849
0
                res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12850
0
                break;
12851
0
            case PJ_TYPE_VERTICAL_CRS:
12852
0
                res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12853
0
                break;
12854
0
            case PJ_TYPE_COMPOUND_CRS:
12855
0
                res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12856
0
                break;
12857
0
            default:
12858
0
                break;
12859
0
        }
12860
0
        res[i]->bDeprecated = projList[i]->deprecated;
12861
0
        res[i]->bBboxValid = projList[i]->bbox_valid;
12862
0
        res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12863
0
        res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12864
0
        res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12865
0
        res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12866
0
        res[i]->pszAreaName = projList[i]->area_name
12867
0
                                  ? CPLStrdup(projList[i]->area_name)
12868
0
                                  : nullptr;
12869
0
        res[i]->pszProjectionMethod =
12870
0
            projList[i]->projection_method_name
12871
0
                ? CPLStrdup(projList[i]->projection_method_name)
12872
0
                : nullptr;
12873
0
    }
12874
0
    res[nResultCount] = nullptr;
12875
0
    proj_crs_info_list_destroy(projList);
12876
0
    return res;
12877
0
}
12878
12879
/************************************************************************/
12880
/*                        OSRDestroyCRSInfoList()                       */
12881
/************************************************************************/
12882
12883
/** \brief Destroy the result returned by
12884
 * OSRGetCRSInfoListFromDatabase().
12885
 *
12886
 * @since GDAL 3.0
12887
 */
12888
void OSRDestroyCRSInfoList(OSRCRSInfo **list)
12889
0
{
12890
0
    if (list)
12891
0
    {
12892
0
        for (int i = 0; list[i] != nullptr; i++)
12893
0
        {
12894
0
            CPLFree(list[i]->pszAuthName);
12895
0
            CPLFree(list[i]->pszCode);
12896
0
            CPLFree(list[i]->pszName);
12897
0
            CPLFree(list[i]->pszAreaName);
12898
0
            CPLFree(list[i]->pszProjectionMethod);
12899
0
            delete list[i];
12900
0
        }
12901
0
        delete[] list;
12902
0
    }
12903
0
}
12904
12905
/************************************************************************/
12906
/*                   OSRGetAuthorityListFromDatabase()                  */
12907
/************************************************************************/
12908
12909
/** \brief Return the list of CRS authorities used in the PROJ database.
12910
 *
12911
 * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
12912
 *
12913
 * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
12914
 *
12915
 * @return nullptr in case of error, or a NULL terminated list of strings to
12916
 * free with CSLDestroy()
12917
 * @since GDAL 3.10
12918
 */
12919
char **OSRGetAuthorityListFromDatabase()
12920
0
{
12921
0
    PROJ_STRING_LIST list =
12922
0
        proj_get_authorities_from_database(OSRGetProjTLSContext());
12923
0
    if (!list)
12924
0
    {
12925
0
        return nullptr;
12926
0
    }
12927
0
    int count = 0;
12928
0
    while (list[count])
12929
0
        ++count;
12930
0
    char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
12931
0
    for (int i = 0; i < count; ++i)
12932
0
        res[i] = CPLStrdup(list[i]);
12933
0
    proj_string_list_destroy(list);
12934
0
    return res;
12935
0
}
12936
12937
/************************************************************************/
12938
/*                    UpdateCoordinateSystemFromGeogCRS()               */
12939
/************************************************************************/
12940
12941
/*! @cond Doxygen_Suppress */
12942
/** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
12943
 *
12944
 * @since GDAL 3.1
12945
 */
12946
void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
12947
2
{
12948
2
    TAKE_OPTIONAL_LOCK();
12949
12950
2
    d->refreshProjObj();
12951
2
    if (!d->m_pj_crs)
12952
0
        return;
12953
2
    if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
12954
0
        return;
12955
2
    if (GetAxesCount() == 3)
12956
0
        return;
12957
2
    auto ctxt = d->getPROJContext();
12958
2
    auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
12959
2
    if (!baseCRS)
12960
0
        return;
12961
2
    auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
12962
2
    if (!baseCRSCS)
12963
0
    {
12964
0
        proj_destroy(baseCRS);
12965
0
        return;
12966
0
    }
12967
2
    if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
12968
2
    {
12969
2
        proj_destroy(baseCRSCS);
12970
2
        proj_destroy(baseCRS);
12971
2
        return;
12972
2
    }
12973
0
    auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
12974
0
    if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
12975
0
    {
12976
0
        proj_destroy(baseCRSCS);
12977
0
        proj_destroy(baseCRS);
12978
0
        proj_destroy(projCS);
12979
0
        return;
12980
0
    }
12981
12982
0
    PJ_AXIS_DESCRIPTION axis[3];
12983
0
    for (int i = 0; i < 3; i++)
12984
0
    {
12985
0
        const char *name = nullptr;
12986
0
        const char *abbreviation = nullptr;
12987
0
        const char *direction = nullptr;
12988
0
        double unit_conv_factor = 0;
12989
0
        const char *unit_name = nullptr;
12990
0
        proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
12991
0
                              &abbreviation, &direction, &unit_conv_factor,
12992
0
                              &unit_name, nullptr, nullptr);
12993
0
        axis[i].name = CPLStrdup(name);
12994
0
        axis[i].abbreviation = CPLStrdup(abbreviation);
12995
0
        axis[i].direction = CPLStrdup(direction);
12996
0
        axis[i].unit_name = CPLStrdup(unit_name);
12997
0
        axis[i].unit_conv_factor = unit_conv_factor;
12998
0
        axis[i].unit_type = PJ_UT_LINEAR;
12999
0
    }
13000
0
    proj_destroy(baseCRSCS);
13001
0
    proj_destroy(projCS);
13002
0
    auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13003
0
    for (int i = 0; i < 3; i++)
13004
0
    {
13005
0
        CPLFree(axis[i].name);
13006
0
        CPLFree(axis[i].abbreviation);
13007
0
        CPLFree(axis[i].direction);
13008
0
        CPLFree(axis[i].unit_name);
13009
0
    }
13010
0
    if (!cs)
13011
0
    {
13012
0
        proj_destroy(baseCRS);
13013
0
        return;
13014
0
    }
13015
0
    auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13016
0
    auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13017
0
                                         conversion, cs);
13018
0
    proj_destroy(baseCRS);
13019
0
    proj_destroy(conversion);
13020
0
    proj_destroy(cs);
13021
0
    d->setPjCRS(crs);
13022
0
}
13023
13024
/*! @endcond */
13025
13026
/************************************************************************/
13027
/*                             PromoteTo3D()                            */
13028
/************************************************************************/
13029
13030
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13031
 *
13032
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
13033
 * units.
13034
 *
13035
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13036
 * be used.
13037
 * @return OGRERR_NONE if no error occurred.
13038
 * @since GDAL 3.1 and PROJ 6.3
13039
 */
13040
OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13041
0
{
13042
0
    TAKE_OPTIONAL_LOCK();
13043
13044
0
    d->refreshProjObj();
13045
0
    if (!d->m_pj_crs)
13046
0
        return OGRERR_FAILURE;
13047
0
    auto newPj =
13048
0
        proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13049
0
    if (!newPj)
13050
0
        return OGRERR_FAILURE;
13051
0
    d->setPjCRS(newPj);
13052
0
    return OGRERR_NONE;
13053
0
}
13054
13055
/************************************************************************/
13056
/*                             OSRPromoteTo3D()                         */
13057
/************************************************************************/
13058
13059
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13060
 *
13061
 * See OGRSpatialReference::PromoteTo3D()
13062
 *
13063
 * @since GDAL 3.1 and PROJ 6.3
13064
 */
13065
OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13066
0
{
13067
0
    VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13068
13069
0
    return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13070
0
}
13071
13072
/************************************************************************/
13073
/*                             DemoteTo2D()                             */
13074
/************************************************************************/
13075
13076
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13077
 *
13078
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13079
 * be used.
13080
 * @return OGRERR_NONE if no error occurred.
13081
 * @since GDAL 3.2 and PROJ 6.3
13082
 */
13083
OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13084
0
{
13085
0
    TAKE_OPTIONAL_LOCK();
13086
13087
0
    d->refreshProjObj();
13088
0
    if (!d->m_pj_crs)
13089
0
        return OGRERR_FAILURE;
13090
0
    auto newPj =
13091
0
        proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13092
0
    if (!newPj)
13093
0
        return OGRERR_FAILURE;
13094
0
    d->setPjCRS(newPj);
13095
0
    return OGRERR_NONE;
13096
0
}
13097
13098
/************************************************************************/
13099
/*                             OSRDemoteTo2D()                          */
13100
/************************************************************************/
13101
13102
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13103
 *
13104
 * See OGRSpatialReference::DemoteTo2D()
13105
 *
13106
 * @since GDAL 3.2 and PROJ 6.3
13107
 */
13108
OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13109
0
{
13110
0
    VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13111
13112
0
    return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13113
0
}
13114
13115
/************************************************************************/
13116
/*                           GetEPSGGeogCS()                            */
13117
/************************************************************************/
13118
13119
/** Try to establish what the EPSG code for this coordinate systems
13120
 * GEOGCS might be.  Returns -1 if no reasonable guess can be made.
13121
 *
13122
 * @return EPSG code
13123
 */
13124
13125
int OGRSpatialReference::GetEPSGGeogCS() const
13126
13127
0
{
13128
0
    TAKE_OPTIONAL_LOCK();
13129
13130
    /* -------------------------------------------------------------------- */
13131
    /*      Check axis order.                                               */
13132
    /* -------------------------------------------------------------------- */
13133
0
    auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13134
0
    if (!poGeogCRS)
13135
0
        return -1;
13136
13137
0
    bool ret = false;
13138
0
    poGeogCRS->d->demoteFromBoundCRS();
13139
0
    auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13140
0
                                             poGeogCRS->d->m_pj_crs);
13141
0
    poGeogCRS->d->undoDemoteFromBoundCRS();
13142
0
    if (cs)
13143
0
    {
13144
0
        const char *pszDirection = nullptr;
13145
0
        if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13146
0
                                  &pszDirection, nullptr, nullptr, nullptr,
13147
0
                                  nullptr))
13148
0
        {
13149
0
            if (EQUAL(pszDirection, "north"))
13150
0
            {
13151
0
                ret = true;
13152
0
            }
13153
0
        }
13154
13155
0
        proj_destroy(cs);
13156
0
    }
13157
0
    if (!ret)
13158
0
        return -1;
13159
13160
    /* -------------------------------------------------------------------- */
13161
    /*      Do we already have it?                                          */
13162
    /* -------------------------------------------------------------------- */
13163
0
    const char *pszAuthName = GetAuthorityName("GEOGCS");
13164
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13165
0
        return atoi(GetAuthorityCode("GEOGCS"));
13166
13167
    /* -------------------------------------------------------------------- */
13168
    /*      Get the datum and geogcs names.                                 */
13169
    /* -------------------------------------------------------------------- */
13170
13171
0
    const char *pszGEOGCS = GetAttrValue("GEOGCS");
13172
0
    const char *pszDatum = GetAttrValue("DATUM");
13173
13174
    // We can only operate on coordinate systems with a geogcs.
13175
0
    OGRSpatialReference oSRSTmp;
13176
0
    if (pszGEOGCS == nullptr || pszDatum == nullptr)
13177
0
    {
13178
        // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13179
        // export to WKT1, so try to extract the geographic CRS through PROJ
13180
        // API with CopyGeogCSFrom() and get the nodes' values from it.
13181
0
        oSRSTmp.CopyGeogCSFrom(this);
13182
0
        pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13183
0
        pszDatum = oSRSTmp.GetAttrValue("DATUM");
13184
0
        if (pszGEOGCS == nullptr || pszDatum == nullptr)
13185
0
        {
13186
0
            return -1;
13187
0
        }
13188
0
    }
13189
13190
    // Lookup geographic CRS name
13191
0
    const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13192
0
    PJ_OBJ_LIST *list = proj_create_from_name(
13193
0
        d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13194
0
    if (list)
13195
0
    {
13196
0
        const auto listSize = proj_list_get_count(list);
13197
0
        if (listSize == 1)
13198
0
        {
13199
0
            auto crs = proj_list_get(d->getPROJContext(), list, 0);
13200
0
            if (crs)
13201
0
            {
13202
0
                pszAuthName = proj_get_id_auth_name(crs, 0);
13203
0
                const char *pszCode = proj_get_id_code(crs, 0);
13204
0
                if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13205
0
                {
13206
0
                    const int nCode = atoi(pszCode);
13207
0
                    proj_destroy(crs);
13208
0
                    proj_list_destroy(list);
13209
0
                    return nCode;
13210
0
                }
13211
0
                proj_destroy(crs);
13212
0
            }
13213
0
        }
13214
0
        proj_list_destroy(list);
13215
0
    }
13216
13217
    /* -------------------------------------------------------------------- */
13218
    /*      Is this a "well known" geographic coordinate system?            */
13219
    /* -------------------------------------------------------------------- */
13220
0
    const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13221
0
                      strstr(pszDatum, "WGS") ||
13222
0
                      strstr(pszGEOGCS, "World Geodetic System") ||
13223
0
                      strstr(pszGEOGCS, "World_Geodetic_System") ||
13224
0
                      strstr(pszDatum, "World Geodetic System") ||
13225
0
                      strstr(pszDatum, "World_Geodetic_System");
13226
13227
0
    const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13228
0
                      strstr(pszDatum, "NAD") ||
13229
0
                      strstr(pszGEOGCS, "North American") ||
13230
0
                      strstr(pszGEOGCS, "North_American") ||
13231
0
                      strstr(pszDatum, "North American") ||
13232
0
                      strstr(pszDatum, "North_American");
13233
13234
0
    if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13235
0
        return 4326;
13236
13237
0
    if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13238
0
        return 4322;
13239
13240
    // This is questionable as there are several 'flavors' of NAD83 that
13241
    // are not the same as 4269
13242
0
    if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13243
0
        return 4269;
13244
13245
0
    if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13246
0
        return 4267;
13247
13248
    /* -------------------------------------------------------------------- */
13249
    /*      If we know the datum, associate the most likely GCS with        */
13250
    /*      it.                                                             */
13251
    /* -------------------------------------------------------------------- */
13252
0
    const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13253
0
    pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13254
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13255
0
        GetPrimeMeridian() == 0.0)
13256
0
    {
13257
0
        const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13258
13259
0
        if (nDatum >= 6000 && nDatum <= 6999)
13260
0
            return nDatum - 2000;
13261
0
    }
13262
13263
0
    return -1;
13264
0
}
13265
13266
/************************************************************************/
13267
/*                          SetCoordinateEpoch()                        */
13268
/************************************************************************/
13269
13270
/** Set the coordinate epoch, as decimal year.
13271
 *
13272
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13273
 * change with time. To be unambiguous the coordinates must always be qualified
13274
 * with the epoch at which they are valid. The coordinate epoch is not
13275
 * necessarily the epoch at which the observation was collected.
13276
 *
13277
 * Pedantically the coordinate epoch of an observation belongs to the
13278
 * observation, and not to the CRS, however it is often more practical to
13279
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13280
 * CRS (see IsDynamic())
13281
 *
13282
 * This method is the same as the OSRSetCoordinateEpoch() function.
13283
 *
13284
 * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13285
 * @since OGR 3.4
13286
 */
13287
13288
void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13289
0
{
13290
0
    d->m_coordinateEpoch = dfCoordinateEpoch;
13291
0
}
13292
13293
/************************************************************************/
13294
/*                      OSRSetCoordinateEpoch()                         */
13295
/************************************************************************/
13296
13297
/** \brief Set the coordinate epoch, as decimal year.
13298
 *
13299
 * See OGRSpatialReference::SetCoordinateEpoch()
13300
 *
13301
 * @since OGR 3.4
13302
 */
13303
void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13304
0
{
13305
0
    VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13306
13307
0
    return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13308
0
        dfCoordinateEpoch);
13309
0
}
13310
13311
/************************************************************************/
13312
/*                          GetCoordinateEpoch()                        */
13313
/************************************************************************/
13314
13315
/** Return the coordinate epoch, as decimal year.
13316
 *
13317
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13318
 * change with time. To be unambiguous the coordinates must always be qualified
13319
 * with the epoch at which they are valid. The coordinate epoch is not
13320
 * necessarily the epoch at which the observation was collected.
13321
 *
13322
 * Pedantically the coordinate epoch of an observation belongs to the
13323
 * observation, and not to the CRS, however it is often more practical to
13324
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13325
 * CRS (see IsDynamic())
13326
 *
13327
 * This method is the same as the OSRGetCoordinateEpoch() function.
13328
 *
13329
 * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13330
 *                         if not set, or relevant.
13331
 * @since OGR 3.4
13332
 */
13333
13334
double OGRSpatialReference::GetCoordinateEpoch() const
13335
25.9k
{
13336
25.9k
    return d->m_coordinateEpoch;
13337
25.9k
}
13338
13339
/************************************************************************/
13340
/*                      OSRGetCoordinateEpoch()                        */
13341
/************************************************************************/
13342
13343
/** \brief Get the coordinate epoch, as decimal year.
13344
 *
13345
 * See OGRSpatialReference::GetCoordinateEpoch()
13346
 *
13347
 * @since OGR 3.4
13348
 */
13349
double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13350
0
{
13351
0
    VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13352
13353
0
    return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13354
0
}