Coverage Report

Created: 2025-08-28 06:57

/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
bool GDALThreadLocalDatasetCacheIsInDestruction();
49
50
// Exists since 8.0.1
51
#ifndef PROJ_AT_LEAST_VERSION
52
#define PROJ_COMPUTE_VERSION(maj, min, patch)                                  \
53
    ((maj)*10000 + (min)*100 + (patch))
54
#define PROJ_VERSION_NUMBER                                                    \
55
    PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR,               \
56
                         PROJ_VERSION_PATCH)
57
#define PROJ_AT_LEAST_VERSION(maj, min, patch)                                 \
58
    (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
59
#endif
60
61
0
#define STRINGIFY(s) #s
62
0
#define XSTRINGIFY(s) STRINGIFY(s)
63
64
struct OGRSpatialReference::Private
65
{
66
    struct Listener : public OGR_SRSNode::Listener
67
    {
68
        OGRSpatialReference::Private *m_poObj = nullptr;
69
70
0
        explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71
0
        {
72
0
        }
73
74
        Listener(const Listener &) = delete;
75
        Listener &operator=(const Listener &) = delete;
76
77
        void notifyChange(OGR_SRSNode *) override;
78
    };
79
80
    OGRSpatialReference *m_poSelf = nullptr;
81
    PJ *m_pj_crs = nullptr;
82
83
    // Temporary state used for object construction
84
    PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85
    CPLString m_osPrimeMeridianName{};
86
    CPLString m_osAngularUnits{};
87
    CPLString m_osLinearUnits{};
88
    CPLString m_osAxisName[3]{};
89
90
    std::vector<std::string> m_wktImportWarnings{};
91
    std::vector<std::string> m_wktImportErrors{};
92
    CPLString m_osAreaName{};
93
    CPLString m_celestialBodyName{};
94
95
    bool m_bIsThreadSafe = false;
96
    bool m_bNodesChanged = false;
97
    bool m_bNodesWKT2 = false;
98
    OGR_SRSNode *m_poRoot = nullptr;
99
100
    double dfFromGreenwich = 0.0;
101
    double dfToMeter = 0.0;
102
    double dfToDegrees = 0.0;
103
    double m_dfAngularUnitToRadian = 0.0;
104
105
    int nRefCount = 1;
106
    int bNormInfoSet = FALSE;
107
108
    PJ *m_pj_geod_base_crs_temp = nullptr;
109
    PJ *m_pj_proj_crs_cs_temp = nullptr;
110
111
    bool m_pj_crs_modified_during_demote = false;
112
    PJ *m_pj_bound_crs_target = nullptr;
113
    PJ *m_pj_bound_crs_co = nullptr;
114
    PJ *m_pj_crs_backup = nullptr;
115
    OGR_SRSNode *m_poRootBackup = nullptr;
116
117
    bool m_bMorphToESRI = false;
118
    bool m_bHasCenterLong = false;
119
120
    std::shared_ptr<Listener> m_poListener{};
121
122
    std::recursive_mutex m_mutex{};
123
124
    OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
125
    std::vector<int> m_axisMapping{1, 2, 3};
126
127
    double m_coordinateEpoch = 0;  // as decimal year
128
129
    explicit Private(OGRSpatialReference *poSelf);
130
    ~Private();
131
    Private(const Private &) = delete;
132
    Private &operator=(const Private &) = delete;
133
134
    void SetThreadSafe()
135
0
    {
136
0
        m_bIsThreadSafe = true;
137
0
    }
138
139
    void clear();
140
    void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
141
    void setRoot(OGR_SRSNode *poRoot);
142
    void refreshProjObj();
143
    void nodesChanged();
144
    void refreshRootFromProjObj();
145
    void invalidateNodes();
146
147
    void setMorphToESRI(bool b);
148
149
    PJ *getGeodBaseCRS();
150
    PJ *getProjCRSCoordSys();
151
152
    const char *getProjCRSName();
153
    OGRErr replaceConversionAndUnref(PJ *conv);
154
155
    void demoteFromBoundCRS();
156
    void undoDemoteFromBoundCRS();
157
158
    PJ_CONTEXT *getPROJContext()
159
0
    {
160
0
        return OSRGetProjTLSContext();
161
0
    }
162
163
    const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
164
165
    void refreshAxisMapping();
166
167
    // This structures enables locking during calls to OGRSpatialReference
168
    // public methods. Locking is only needed for instances of
169
    // OGRSpatialReference that have been asked to be thread-safe at
170
    // construction.
171
    // The lock is not just for a single call to OGRSpatialReference::Private,
172
    // but for the series of calls done by a OGRSpatialReference method.
173
    // We need a recursive mutex, because some OGRSpatialReference methods
174
    // may call other ones.
175
    struct OptionalLockGuard
176
    {
177
        Private &m_private;
178
179
0
        explicit OptionalLockGuard(Private *p) : m_private(*p)
180
0
        {
181
0
            if (m_private.m_bIsThreadSafe)
182
0
                m_private.m_mutex.lock();
183
0
        }
184
185
        ~OptionalLockGuard()
186
0
        {
187
0
            if (m_private.m_bIsThreadSafe)
188
0
                m_private.m_mutex.unlock();
189
0
        }
190
    };
191
192
    inline OptionalLockGuard GetOptionalLockGuard()
193
0
    {
194
0
        return OptionalLockGuard(this);
195
0
    }
196
};
197
198
void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
199
0
{
200
0
    m_poObj->nodesChanged();
201
0
}
202
203
#define TAKE_OPTIONAL_LOCK()                                                   \
204
0
    auto lock = d->GetOptionalLockGuard();                                     \
205
0
    CPL_IGNORE_RET_VAL(lock)
206
207
static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
208
0
{
209
0
    const char *pszDefaultAMS =
210
0
        CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
211
0
    if (pszDefaultAMS)
212
0
    {
213
0
        if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
214
0
            return OAMS_AUTHORITY_COMPLIANT;
215
0
        else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
216
0
            return OAMS_TRADITIONAL_GIS_ORDER;
217
0
        else
218
0
        {
219
0
            CPLError(CE_Failure, CPLE_AppDefined,
220
0
                     "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
221
0
                     pszDefaultAMS);
222
0
        }
223
0
    }
224
0
    return OAMS_AUTHORITY_COMPLIANT;
225
0
}
226
227
OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
228
0
    : m_poSelf(poSelf),
229
0
      m_poListener(std::shared_ptr<Listener>(new Listener(this)))
230
0
{
231
    // Get the default value for m_axisMappingStrategy from the
232
    // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
233
0
    m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
234
0
}
235
236
OGRSpatialReference::Private::~Private()
237
0
{
238
    // In case we destroy the object not in the thread that created it,
239
    // we need to reassign the PROJ context. Having the context bundled inside
240
    // PJ* deeply sucks...
241
0
    PJ_CONTEXT *pj_context_to_destroy = nullptr;
242
0
    PJ_CONTEXT *ctxt;
243
0
    if (GDALThreadLocalDatasetCacheIsInDestruction())
244
0
    {
245
0
        pj_context_to_destroy = proj_context_create();
246
0
        ctxt = pj_context_to_destroy;
247
0
    }
248
0
    else
249
0
    {
250
0
        ctxt = getPROJContext();
251
0
    }
252
253
0
    proj_assign_context(m_pj_crs, ctxt);
254
0
    proj_destroy(m_pj_crs);
255
256
0
    proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
257
0
    proj_destroy(m_pj_geod_base_crs_temp);
258
259
0
    proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
260
0
    proj_destroy(m_pj_proj_crs_cs_temp);
261
262
0
    proj_assign_context(m_pj_bound_crs_target, ctxt);
263
0
    proj_destroy(m_pj_bound_crs_target);
264
265
0
    proj_assign_context(m_pj_bound_crs_co, ctxt);
266
0
    proj_destroy(m_pj_bound_crs_co);
267
268
0
    proj_assign_context(m_pj_crs_backup, ctxt);
269
0
    proj_destroy(m_pj_crs_backup);
270
271
0
    delete m_poRootBackup;
272
0
    delete m_poRoot;
273
0
    proj_context_destroy(pj_context_to_destroy);
274
0
}
275
276
void OGRSpatialReference::Private::clear()
277
0
{
278
0
    proj_assign_context(m_pj_crs, getPROJContext());
279
0
    proj_destroy(m_pj_crs);
280
0
    m_pj_crs = nullptr;
281
282
0
    delete m_poRoot;
283
0
    m_poRoot = nullptr;
284
0
    m_bNodesChanged = false;
285
286
0
    m_wktImportWarnings.clear();
287
0
    m_wktImportErrors.clear();
288
289
0
    m_pj_crs_modified_during_demote = false;
290
0
    m_pjType = PJ_TYPE_UNKNOWN;
291
0
    m_osPrimeMeridianName.clear();
292
0
    m_osAngularUnits.clear();
293
0
    m_osLinearUnits.clear();
294
295
0
    bNormInfoSet = FALSE;
296
0
    dfFromGreenwich = 1.0;
297
0
    dfToMeter = 1.0;
298
0
    dfToDegrees = 1.0;
299
0
    m_dfAngularUnitToRadian = 0.0;
300
301
0
    m_bMorphToESRI = false;
302
0
    m_bHasCenterLong = false;
303
304
0
    m_coordinateEpoch = 0.0;
305
0
}
306
307
void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
308
0
{
309
0
    m_poRoot = poRoot;
310
0
    if (m_poRoot)
311
0
    {
312
0
        m_poRoot->RegisterListener(m_poListener);
313
0
    }
314
0
    nodesChanged();
315
0
}
316
317
void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
318
                                            bool doRefreshAxisMapping)
319
0
{
320
0
    auto ctxt = getPROJContext();
321
322
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
323
0
    if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
324
0
    {
325
0
        const double dfEpoch =
326
0
            proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
327
0
        if (!std::isnan(dfEpoch))
328
0
        {
329
0
            m_poSelf->SetCoordinateEpoch(dfEpoch);
330
0
        }
331
0
        auto crs = proj_get_source_crs(ctxt, pj_crsIn);
332
0
        proj_destroy(pj_crsIn);
333
0
        pj_crsIn = crs;
334
0
    }
335
0
#endif
336
337
0
    proj_assign_context(m_pj_crs, ctxt);
338
0
    proj_destroy(m_pj_crs);
339
0
    m_pj_crs = pj_crsIn;
340
0
    if (m_pj_crs)
341
0
    {
342
0
        m_pjType = proj_get_type(m_pj_crs);
343
0
    }
344
0
    if (m_pj_crs_backup)
345
0
    {
346
0
        m_pj_crs_modified_during_demote = true;
347
0
    }
348
0
    invalidateNodes();
349
0
    if (doRefreshAxisMapping)
350
0
    {
351
0
        refreshAxisMapping();
352
0
    }
353
0
}
354
355
void OGRSpatialReference::Private::refreshProjObj()
356
0
{
357
0
    if (m_bNodesChanged && m_poRoot)
358
0
    {
359
0
        char *pszWKT = nullptr;
360
0
        m_poRoot->exportToWkt(&pszWKT);
361
0
        auto poRootBackup = m_poRoot;
362
0
        m_poRoot = nullptr;
363
0
        const double dfCoordinateEpochBackup = m_coordinateEpoch;
364
0
        clear();
365
0
        m_coordinateEpoch = dfCoordinateEpochBackup;
366
0
        m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
367
368
0
        const char *const options[] = {
369
0
            "STRICT=NO",
370
0
#if PROJ_AT_LEAST_VERSION(9, 1, 0)
371
0
            "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
372
0
#endif
373
0
            nullptr
374
0
        };
375
0
        PROJ_STRING_LIST warnings = nullptr;
376
0
        PROJ_STRING_LIST errors = nullptr;
377
0
        setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
378
0
                                      &warnings, &errors));
379
0
        for (auto iter = warnings; iter && *iter; ++iter)
380
0
        {
381
0
            m_wktImportWarnings.push_back(*iter);
382
0
        }
383
0
        for (auto iter = errors; iter && *iter; ++iter)
384
0
        {
385
0
            m_wktImportErrors.push_back(*iter);
386
0
        }
387
0
        proj_string_list_destroy(warnings);
388
0
        proj_string_list_destroy(errors);
389
390
0
        CPLFree(pszWKT);
391
392
0
        m_poRoot = poRootBackup;
393
0
        m_bNodesChanged = false;
394
0
    }
395
0
}
396
397
void OGRSpatialReference::Private::refreshRootFromProjObj()
398
0
{
399
0
    CPLAssert(m_poRoot == nullptr);
400
401
0
    if (m_pj_crs)
402
0
    {
403
0
        CPLStringList aosOptions;
404
0
        if (!m_bMorphToESRI)
405
0
        {
406
0
            aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
407
0
            aosOptions.SetNameValue("MULTILINE", "NO");
408
0
        }
409
0
        aosOptions.SetNameValue("STRICT", "NO");
410
411
0
        const char *pszWKT;
412
0
        {
413
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
414
0
            pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
415
0
                                 m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
416
0
                                 aosOptions.List());
417
0
            m_bNodesWKT2 = false;
418
0
        }
419
0
        if (!m_bMorphToESRI && pszWKT == nullptr)
420
0
        {
421
0
            pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
422
0
                                 aosOptions.List());
423
0
            m_bNodesWKT2 = true;
424
0
        }
425
0
        if (pszWKT)
426
0
        {
427
0
            auto root = new OGR_SRSNode();
428
0
            setRoot(root);
429
0
            root->importFromWkt(&pszWKT);
430
0
            m_bNodesChanged = false;
431
0
        }
432
0
    }
433
0
}
434
435
static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
436
0
{
437
0
    const char *pszName1 = nullptr;
438
0
    const char *pszDirection1 = nullptr;
439
0
    proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
440
0
                          nullptr, nullptr, nullptr, nullptr);
441
0
    const char *pszName2 = nullptr;
442
0
    const char *pszDirection2 = nullptr;
443
0
    proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
444
0
                          nullptr, nullptr, nullptr, nullptr);
445
0
    if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
446
0
        EQUAL(pszDirection2, "east"))
447
0
    {
448
0
        return true;
449
0
    }
450
0
    if (pszDirection1 && pszDirection2 &&
451
0
        ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
452
0
         (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
453
0
        pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
454
0
        STARTS_WITH_CI(pszName2, "easting"))
455
0
    {
456
0
        return true;
457
0
    }
458
0
    return false;
459
0
}
460
461
void OGRSpatialReference::Private::refreshAxisMapping()
462
0
{
463
0
    if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
464
0
        return;
465
466
0
    bool doUndoDemote = false;
467
0
    if (m_pj_crs_backup == nullptr)
468
0
    {
469
0
        doUndoDemote = true;
470
0
        demoteFromBoundCRS();
471
0
    }
472
0
    const auto ctxt = getPROJContext();
473
0
    PJ *horizCRS = nullptr;
474
0
    int axisCount = 0;
475
0
    if (m_pjType == PJ_TYPE_VERTICAL_CRS)
476
0
    {
477
0
        axisCount = 1;
478
0
    }
479
0
    else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
480
0
    {
481
0
        horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
482
0
        if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
483
0
        {
484
0
            auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
485
0
            if (baseCRS)
486
0
            {
487
0
                proj_destroy(horizCRS);
488
0
                horizCRS = baseCRS;
489
0
            }
490
0
        }
491
492
0
        auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
493
0
        if (vertCRS)
494
0
        {
495
0
            if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
496
0
            {
497
0
                auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
498
0
                if (baseCRS)
499
0
                {
500
0
                    proj_destroy(vertCRS);
501
0
                    vertCRS = baseCRS;
502
0
                }
503
0
            }
504
505
0
            auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
506
0
            if (cs)
507
0
            {
508
0
                axisCount += proj_cs_get_axis_count(ctxt, cs);
509
0
                proj_destroy(cs);
510
0
            }
511
0
            proj_destroy(vertCRS);
512
0
        }
513
0
    }
514
0
    else
515
0
    {
516
0
        horizCRS = m_pj_crs;
517
0
    }
518
519
0
    bool bSwitchForGisFriendlyOrder = false;
520
0
    if (horizCRS)
521
0
    {
522
0
        auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
523
0
        if (cs)
524
0
        {
525
0
            int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
526
0
            axisCount += nHorizCSAxisCount;
527
0
            if (nHorizCSAxisCount >= 2)
528
0
            {
529
0
                bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
530
0
            }
531
0
            proj_destroy(cs);
532
0
        }
533
0
    }
534
0
    if (horizCRS != m_pj_crs)
535
0
    {
536
0
        proj_destroy(horizCRS);
537
0
    }
538
0
    if (doUndoDemote)
539
0
    {
540
0
        undoDemoteFromBoundCRS();
541
0
    }
542
543
0
    m_axisMapping.resize(axisCount);
544
0
    if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
545
0
        !bSwitchForGisFriendlyOrder)
546
0
    {
547
0
        for (int i = 0; i < axisCount; i++)
548
0
        {
549
0
            m_axisMapping[i] = i + 1;
550
0
        }
551
0
    }
552
0
    else
553
0
    {
554
0
        m_axisMapping[0] = 2;
555
0
        m_axisMapping[1] = 1;
556
0
        if (axisCount == 3)
557
0
        {
558
0
            m_axisMapping[2] = 3;
559
0
        }
560
0
    }
561
0
}
562
563
void OGRSpatialReference::Private::nodesChanged()
564
0
{
565
0
    m_bNodesChanged = true;
566
0
}
567
568
void OGRSpatialReference::Private::invalidateNodes()
569
0
{
570
0
    delete m_poRoot;
571
0
    m_poRoot = nullptr;
572
0
    m_bNodesChanged = false;
573
0
}
574
575
void OGRSpatialReference::Private::setMorphToESRI(bool b)
576
0
{
577
0
    invalidateNodes();
578
0
    m_bMorphToESRI = b;
579
0
}
580
581
void OGRSpatialReference::Private::demoteFromBoundCRS()
582
0
{
583
0
    CPLAssert(m_pj_bound_crs_target == nullptr);
584
0
    CPLAssert(m_pj_bound_crs_co == nullptr);
585
0
    CPLAssert(m_poRootBackup == nullptr);
586
0
    CPLAssert(m_pj_crs_backup == nullptr);
587
588
0
    m_pj_crs_modified_during_demote = false;
589
590
0
    if (m_pjType == PJ_TYPE_BOUND_CRS)
591
0
    {
592
0
        auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
593
0
        m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
594
0
        m_pj_bound_crs_co =
595
0
            proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
596
597
0
        m_poRootBackup = m_poRoot;
598
0
        m_poRoot = nullptr;
599
0
        m_pj_crs_backup = m_pj_crs;
600
0
        m_pj_crs = baseCRS;
601
0
        m_pjType = proj_get_type(m_pj_crs);
602
0
    }
603
0
}
604
605
void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
606
0
{
607
0
    if (m_pj_bound_crs_target)
608
0
    {
609
0
        CPLAssert(m_poRoot == nullptr);
610
0
        CPLAssert(m_pj_crs);
611
0
        if (!m_pj_crs_modified_during_demote)
612
0
        {
613
0
            proj_destroy(m_pj_crs);
614
0
            m_pj_crs = m_pj_crs_backup;
615
0
            m_pjType = proj_get_type(m_pj_crs);
616
0
            m_poRoot = m_poRootBackup;
617
0
        }
618
0
        else
619
0
        {
620
0
            delete m_poRootBackup;
621
0
            m_poRootBackup = nullptr;
622
0
            proj_destroy(m_pj_crs_backup);
623
0
            m_pj_crs_backup = nullptr;
624
0
            setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
625
0
                                               m_pj_bound_crs_target,
626
0
                                               m_pj_bound_crs_co),
627
0
                     false);
628
0
        }
629
0
    }
630
631
0
    m_poRootBackup = nullptr;
632
0
    m_pj_crs_backup = nullptr;
633
0
    proj_destroy(m_pj_bound_crs_target);
634
0
    m_pj_bound_crs_target = nullptr;
635
0
    proj_destroy(m_pj_bound_crs_co);
636
0
    m_pj_bound_crs_co = nullptr;
637
0
    m_pj_crs_modified_during_demote = false;
638
0
}
639
640
const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
641
    const char *pszTargetKey)
642
0
{
643
0
    if (pszTargetKey)
644
0
    {
645
0
        demoteFromBoundCRS();
646
0
        if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
647
0
             m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
648
0
            EQUAL(pszTargetKey, "GEOGCS"))
649
0
        {
650
0
            pszTargetKey = nullptr;
651
0
        }
652
0
        else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
653
0
                 EQUAL(pszTargetKey, "GEOCCS"))
654
0
        {
655
0
            pszTargetKey = nullptr;
656
0
        }
657
0
        else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
658
0
                 EQUAL(pszTargetKey, "PROJCS"))
659
0
        {
660
0
            pszTargetKey = nullptr;
661
0
        }
662
0
        else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
663
0
                 EQUAL(pszTargetKey, "VERT_CS"))
664
0
        {
665
0
            pszTargetKey = nullptr;
666
0
        }
667
0
        undoDemoteFromBoundCRS();
668
0
    }
669
0
    return pszTargetKey;
670
0
}
671
672
PJ *OGRSpatialReference::Private::getGeodBaseCRS()
673
0
{
674
0
    if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
675
0
        m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
676
0
    {
677
0
        return m_pj_crs;
678
0
    }
679
680
0
    auto ctxt = getPROJContext();
681
0
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
682
0
    {
683
0
        proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
684
0
        proj_destroy(m_pj_geod_base_crs_temp);
685
0
        m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
686
0
        return m_pj_geod_base_crs_temp;
687
0
    }
688
689
0
    proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
690
0
    proj_destroy(m_pj_geod_base_crs_temp);
691
0
    auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
692
0
                                            nullptr, 0);
693
0
    m_pj_geod_base_crs_temp = proj_create_geographic_crs(
694
0
        ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
695
0
        SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
696
0
        SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
697
0
    proj_destroy(cs);
698
699
0
    return m_pj_geod_base_crs_temp;
700
0
}
701
702
PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
703
0
{
704
0
    auto ctxt = getPROJContext();
705
0
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
706
0
    {
707
0
        proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
708
0
        proj_destroy(m_pj_proj_crs_cs_temp);
709
0
        m_pj_proj_crs_cs_temp =
710
0
            proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
711
0
        return m_pj_proj_crs_cs_temp;
712
0
    }
713
714
0
    proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
715
0
    proj_destroy(m_pj_proj_crs_cs_temp);
716
0
    m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
717
0
        ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
718
0
    return m_pj_proj_crs_cs_temp;
719
0
}
720
721
const char *OGRSpatialReference::Private::getProjCRSName()
722
0
{
723
0
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
724
0
    {
725
0
        return proj_get_name(m_pj_crs);
726
0
    }
727
728
0
    return "unnamed";
729
0
}
730
731
OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
732
0
{
733
0
    refreshProjObj();
734
735
0
    demoteFromBoundCRS();
736
737
0
    auto projCRS =
738
0
        proj_create_projected_crs(getPROJContext(), getProjCRSName(),
739
0
                                  getGeodBaseCRS(), conv, getProjCRSCoordSys());
740
0
    proj_destroy(conv);
741
742
0
    setPjCRS(projCRS);
743
744
0
    undoDemoteFromBoundCRS();
745
0
    return OGRERR_NONE;
746
0
}
747
748
/************************************************************************/
749
/*                           ToPointer()                                */
750
/************************************************************************/
751
752
static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
753
0
{
754
0
    return OGRSpatialReference::FromHandle(hSRS);
755
0
}
756
757
/************************************************************************/
758
/*                           ToHandle()                                 */
759
/************************************************************************/
760
761
static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
762
0
{
763
0
    return OGRSpatialReference::ToHandle(poSRS);
764
0
}
765
766
/************************************************************************/
767
/*                           OGRsnPrintDouble()                         */
768
/************************************************************************/
769
770
void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
771
772
void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
773
774
0
{
775
0
    CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
776
777
0
    const size_t nLen = strlen(pszStrBuf);
778
779
    // The following hack is intended to truncate some "precision" in cases
780
    // that appear to be roundoff error.
781
0
    if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
782
0
                      strcmp(pszStrBuf + nLen - 6, "000001") == 0))
783
0
    {
784
0
        CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
785
0
    }
786
787
    // Force to user periods regardless of locale.
788
0
    if (strchr(pszStrBuf, ',') != nullptr)
789
0
    {
790
0
        char *const pszDelim = strchr(pszStrBuf, ',');
791
0
        *pszDelim = '.';
792
0
    }
793
0
}
794
795
/************************************************************************/
796
/*                        OGRSpatialReference()                         */
797
/************************************************************************/
798
799
/**
800
 * \brief Constructor.
801
 *
802
 * This constructor takes an optional string argument which if passed
803
 * should be a WKT representation of an SRS.  Passing this is equivalent
804
 * to not passing it, and then calling importFromWkt() with the WKT string.
805
 *
806
 * Note that newly created objects are given a reference count of one.
807
 *
808
 * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
809
 * object are assumed to be in the order of the axis of the CRS definition
810
 (which
811
 * for example means latitude first, longitude second for geographic CRS
812
 belonging
813
 * to the EPSG authority). It is possible to define a data axis to CRS axis
814
 * mapping strategy with the SetAxisMappingStrategy() method.
815
 *
816
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
817
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
818
 later
819
 * being the default value when the option is not set) to control the value of
820
 the
821
 * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
822
 * created. Calling SetAxisMappingStrategy() will override this default value.
823
824
 * The C function OSRNewSpatialReference() does the same thing as this
825
 * constructor.
826
 *
827
 * @param pszWKT well known text definition to which the object should
828
 * be initialized, or NULL (the default).
829
 */
830
831
OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
832
0
    : d(new Private(this))
833
0
{
834
0
    if (pszWKT != nullptr)
835
0
        importFromWkt(pszWKT);
836
0
}
837
838
/************************************************************************/
839
/*                       OSRNewSpatialReference()                       */
840
/************************************************************************/
841
842
/**
843
 * \brief Constructor.
844
 *
845
 * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
846
 * object are assumed to be in the order of the axis of the CRS definition
847
 * (which for example means latitude first, longitude second for geographic CRS
848
 * belonging to the EPSG authority). It is possible to define a data axis to CRS
849
 * axis mapping strategy with the SetAxisMappingStrategy() method.
850
 *
851
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
852
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
853
 * later being the default value when the option is not set) to control the
854
 * value of the data axis to CRS axis mapping strategy when a
855
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
856
 * override this default value.
857
 *
858
 * This function is the same as OGRSpatialReference::OGRSpatialReference()
859
 */
860
OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
861
862
0
{
863
0
    OGRSpatialReference *poSRS = new OGRSpatialReference();
864
865
0
    if (pszWKT != nullptr && strlen(pszWKT) > 0)
866
0
    {
867
0
        if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
868
0
        {
869
0
            delete poSRS;
870
0
            poSRS = nullptr;
871
0
        }
872
0
    }
873
874
0
    return ToHandle(poSRS);
875
0
}
876
877
/************************************************************************/
878
/*                        OGRSpatialReference()                         */
879
/************************************************************************/
880
881
/** Copy constructor. See also Clone().
882
 * @param oOther other spatial reference
883
 */
884
OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
885
0
    : d(new Private(this))
886
0
{
887
0
    *this = oOther;
888
0
}
889
890
/************************************************************************/
891
/*                        OGRSpatialReference()                         */
892
/************************************************************************/
893
894
/** Move constructor.
895
 * @param oOther other spatial reference
896
 */
897
OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
898
0
    : d(std::move(oOther.d))
899
0
{
900
0
}
901
902
/************************************************************************/
903
/*                        ~OGRSpatialReference()                        */
904
/************************************************************************/
905
906
/**
907
 * \brief OGRSpatialReference destructor.
908
 *
909
 * The C function OSRDestroySpatialReference() does the same thing as this
910
 * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
911
 *
912
 * @deprecated
913
 */
914
915
OGRSpatialReference::~OGRSpatialReference()
916
917
0
{
918
0
}
919
920
/************************************************************************/
921
/*                      DestroySpatialReference()                       */
922
/************************************************************************/
923
924
/**
925
 * \brief OGRSpatialReference destructor.
926
 *
927
 * This static method will destroy a OGRSpatialReference.  It is
928
 * equivalent to calling delete on the object, but it ensures that the
929
 * deallocation is properly executed within the OGR libraries heap on
930
 * platforms where this can matter (win32).
931
 *
932
 * This function is the same as OSRDestroySpatialReference()
933
 *
934
 * @param poSRS the object to delete
935
 *
936
 * @since GDAL 1.7.0
937
 */
938
939
void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
940
0
{
941
0
    delete poSRS;
942
0
}
943
944
/************************************************************************/
945
/*                     OSRDestroySpatialReference()                     */
946
/************************************************************************/
947
948
/**
949
 * \brief OGRSpatialReference destructor.
950
 *
951
 * This function is the same as OGRSpatialReference::~OGRSpatialReference()
952
 * and OGRSpatialReference::DestroySpatialReference()
953
 *
954
 * @param hSRS the object to delete
955
 */
956
void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
957
958
0
{
959
0
    delete ToPointer(hSRS);
960
0
}
961
962
/************************************************************************/
963
/*                               Clear()                                */
964
/************************************************************************/
965
966
/**
967
 * \brief Wipe current definition.
968
 *
969
 * Returns OGRSpatialReference to a state with no definition, as it
970
 * exists when first created.  It does not affect reference counts.
971
 */
972
973
void OGRSpatialReference::Clear()
974
975
0
{
976
0
    d->clear();
977
0
}
978
979
/************************************************************************/
980
/*                             operator=()                              */
981
/************************************************************************/
982
983
/** Assignment operator.
984
 * @param oSource SRS to assign to *this
985
 * @return *this
986
 */
987
OGRSpatialReference &
988
OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
989
990
0
{
991
0
    if (&oSource != this)
992
0
    {
993
0
        Clear();
994
#ifdef CPPCHECK
995
        // Otherwise cppcheck would protest that nRefCount isn't modified
996
        d->nRefCount = (d->nRefCount + 1) - 1;
997
#endif
998
999
0
        oSource.d->refreshProjObj();
1000
0
        if (oSource.d->m_pj_crs)
1001
0
            d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1002
0
        if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1003
0
            SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1004
0
        else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1005
0
            SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1006
1007
0
        d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1008
0
    }
1009
1010
0
    return *this;
1011
0
}
1012
1013
/************************************************************************/
1014
/*                             operator=()                              */
1015
/************************************************************************/
1016
1017
/** Move assignment operator.
1018
 * @param oSource SRS to assign to *this
1019
 * @return *this
1020
 */
1021
OGRSpatialReference &
1022
OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1023
1024
0
{
1025
0
    if (&oSource != this)
1026
0
    {
1027
0
        d = std::move(oSource.d);
1028
0
    }
1029
1030
0
    return *this;
1031
0
}
1032
1033
/************************************************************************/
1034
/*                      AssignAndSetThreadSafe()                        */
1035
/************************************************************************/
1036
1037
/** Assignment method, with thread-safety.
1038
 *
1039
 * Same as an assignment operator, but asking also that the *this instance
1040
 * becomes thread-safe.
1041
 *
1042
 * @param oSource SRS to assign to *this
1043
 * @return *this
1044
 * @since 3.10
1045
 */
1046
1047
OGRSpatialReference &
1048
OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1049
0
{
1050
0
    *this = oSource;
1051
0
    d->SetThreadSafe();
1052
0
    return *this;
1053
0
}
1054
1055
/************************************************************************/
1056
/*                             Reference()                              */
1057
/************************************************************************/
1058
1059
/**
1060
 * \brief Increments the reference count by one.
1061
 *
1062
 * The reference count is used keep track of the number of OGRGeometry objects
1063
 * referencing this SRS.
1064
 *
1065
 * The method does the same thing as the C function OSRReference().
1066
 *
1067
 * @return the updated reference count.
1068
 */
1069
1070
int OGRSpatialReference::Reference()
1071
1072
0
{
1073
0
    return CPLAtomicInc(&d->nRefCount);
1074
0
}
1075
1076
/************************************************************************/
1077
/*                            OSRReference()                            */
1078
/************************************************************************/
1079
1080
/**
1081
 * \brief Increments the reference count by one.
1082
 *
1083
 * This function is the same as OGRSpatialReference::Reference()
1084
 */
1085
int OSRReference(OGRSpatialReferenceH hSRS)
1086
1087
0
{
1088
0
    VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1089
1090
0
    return ToPointer(hSRS)->Reference();
1091
0
}
1092
1093
/************************************************************************/
1094
/*                            Dereference()                             */
1095
/************************************************************************/
1096
1097
/**
1098
 * \brief Decrements the reference count by one.
1099
 *
1100
 * The method does the same thing as the C function OSRDereference().
1101
 *
1102
 * @return the updated reference count.
1103
 */
1104
1105
int OGRSpatialReference::Dereference()
1106
1107
0
{
1108
0
    if (d->nRefCount <= 0)
1109
0
        CPLDebug("OSR",
1110
0
                 "Dereference() called on an object with refcount %d,"
1111
0
                 "likely already destroyed!",
1112
0
                 d->nRefCount);
1113
0
    return CPLAtomicDec(&d->nRefCount);
1114
0
}
1115
1116
/************************************************************************/
1117
/*                           OSRDereference()                           */
1118
/************************************************************************/
1119
1120
/**
1121
 * \brief Decrements the reference count by one.
1122
 *
1123
 * This function is the same as OGRSpatialReference::Dereference()
1124
 */
1125
int OSRDereference(OGRSpatialReferenceH hSRS)
1126
1127
0
{
1128
0
    VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1129
1130
0
    return ToPointer(hSRS)->Dereference();
1131
0
}
1132
1133
/************************************************************************/
1134
/*                         GetReferenceCount()                          */
1135
/************************************************************************/
1136
1137
/**
1138
 * \brief Fetch current reference count.
1139
 *
1140
 * @return the current reference count.
1141
 */
1142
int OGRSpatialReference::GetReferenceCount() const
1143
0
{
1144
0
    return d->nRefCount;
1145
0
}
1146
1147
/************************************************************************/
1148
/*                              Release()                               */
1149
/************************************************************************/
1150
1151
/**
1152
 * \brief Decrements the reference count by one, and destroy if zero.
1153
 *
1154
 * The method does the same thing as the C function OSRRelease().
1155
 */
1156
1157
void OGRSpatialReference::Release()
1158
1159
0
{
1160
0
    if (Dereference() <= 0)
1161
0
        delete this;
1162
0
}
1163
1164
/************************************************************************/
1165
/*                             OSRRelease()                             */
1166
/************************************************************************/
1167
1168
/**
1169
 * \brief Decrements the reference count by one, and destroy if zero.
1170
 *
1171
 * This function is the same as OGRSpatialReference::Release()
1172
 */
1173
void OSRRelease(OGRSpatialReferenceH hSRS)
1174
1175
0
{
1176
0
    VALIDATE_POINTER0(hSRS, "OSRRelease");
1177
1178
0
    ToPointer(hSRS)->Release();
1179
0
}
1180
1181
OGR_SRSNode *OGRSpatialReference::GetRoot()
1182
0
{
1183
0
    TAKE_OPTIONAL_LOCK();
1184
1185
0
    if (!d->m_poRoot)
1186
0
    {
1187
0
        d->refreshRootFromProjObj();
1188
0
    }
1189
0
    return d->m_poRoot;
1190
0
}
1191
1192
const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1193
0
{
1194
0
    TAKE_OPTIONAL_LOCK();
1195
1196
0
    if (!d->m_poRoot)
1197
0
    {
1198
0
        d->refreshRootFromProjObj();
1199
0
    }
1200
0
    return d->m_poRoot;
1201
0
}
1202
1203
/************************************************************************/
1204
/*                              SetRoot()                               */
1205
/************************************************************************/
1206
1207
/**
1208
 * \brief Set the root SRS node.
1209
 *
1210
 * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1211
 * as part of assigning the new root.  Ownership of the passed OGR_SRSNode is
1212
 * is assumed by the OGRSpatialReference.
1213
 *
1214
 * @param poNewRoot object to assign as root.
1215
 */
1216
1217
void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1218
1219
0
{
1220
0
    if (d->m_poRoot != poNewRoot)
1221
0
    {
1222
0
        delete d->m_poRoot;
1223
0
        d->setRoot(poNewRoot);
1224
0
    }
1225
0
}
1226
1227
/************************************************************************/
1228
/*                            GetAttrNode()                             */
1229
/************************************************************************/
1230
1231
/**
1232
 * \brief Find named node in tree.
1233
 *
1234
 * This method does a pre-order traversal of the node tree searching for
1235
 * a node with this exact value (case insensitive), and returns it.  Leaf
1236
 * nodes are not considered, under the assumption that they are just
1237
 * attribute value nodes.
1238
 *
1239
 * If a node appears more than once in the tree (such as UNIT for instance),
1240
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1241
 * more specific.
1242
 *
1243
 * @param pszNodePath the name of the node to search for.  May contain multiple
1244
 * components such as "GEOGCS|UNIT".
1245
 *
1246
 * @return a pointer to the node found, or NULL if none.
1247
 */
1248
1249
OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1250
1251
0
{
1252
0
    if (strchr(pszNodePath, '|') == nullptr)
1253
0
    {
1254
        // Fast path
1255
0
        OGR_SRSNode *poNode = GetRoot();
1256
0
        if (poNode)
1257
0
            poNode = poNode->GetNode(pszNodePath);
1258
0
        return poNode;
1259
0
    }
1260
1261
0
    char **papszPathTokens =
1262
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1263
1264
0
    if (CSLCount(papszPathTokens) < 1)
1265
0
    {
1266
0
        CSLDestroy(papszPathTokens);
1267
0
        return nullptr;
1268
0
    }
1269
1270
0
    OGR_SRSNode *poNode = GetRoot();
1271
0
    for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1272
0
    {
1273
0
        poNode = poNode->GetNode(papszPathTokens[i]);
1274
0
    }
1275
1276
0
    CSLDestroy(papszPathTokens);
1277
1278
0
    return poNode;
1279
0
}
1280
1281
/**
1282
 * \brief Find named node in tree.
1283
 *
1284
 * This method does a pre-order traversal of the node tree searching for
1285
 * a node with this exact value (case insensitive), and returns it.  Leaf
1286
 * nodes are not considered, under the assumption that they are just
1287
 * attribute value nodes.
1288
 *
1289
 * If a node appears more than once in the tree (such as UNIT for instance),
1290
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1291
 * more specific.
1292
 *
1293
 * @param pszNodePath the name of the node to search for.  May contain multiple
1294
 * components such as "GEOGCS|UNIT".
1295
 *
1296
 * @return a pointer to the node found, or NULL if none.
1297
 */
1298
1299
const OGR_SRSNode *
1300
OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1301
1302
0
{
1303
0
    OGR_SRSNode *poNode =
1304
0
        const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1305
1306
0
    return poNode;
1307
0
}
1308
1309
/************************************************************************/
1310
/*                            GetAttrValue()                            */
1311
/************************************************************************/
1312
1313
/**
1314
 * \brief Fetch indicated attribute of named node.
1315
 *
1316
 * This method uses GetAttrNode() to find the named node, and then extracts
1317
 * the value of the indicated child.  Thus a call to GetAttrValue("UNIT",1)
1318
 * would return the second child of the UNIT node, which is normally the
1319
 * length of the linear unit in meters.
1320
 *
1321
 * This method does the same thing as the C function OSRGetAttrValue().
1322
 *
1323
 * @param pszNodeName the tree node to look for (case insensitive).
1324
 * @param iAttr the child of the node to fetch (zero based).
1325
 *
1326
 * @return the requested value, or NULL if it fails for any reason.
1327
 */
1328
1329
const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1330
                                              int iAttr) const
1331
1332
0
{
1333
0
    const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1334
0
    if (poNode == nullptr)
1335
0
    {
1336
0
        if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1337
0
        {
1338
0
            return GetAttrValue("METHOD", iAttr);
1339
0
        }
1340
0
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1341
0
        {
1342
0
            return GetAttrValue("PROJCRS|METHOD", iAttr);
1343
0
        }
1344
0
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1345
0
        {
1346
0
            return GetAttrValue("PROJCRS", iAttr);
1347
0
        }
1348
0
        return nullptr;
1349
0
    }
1350
1351
0
    if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1352
0
        return nullptr;
1353
1354
0
    return poNode->GetChild(iAttr)->GetValue();
1355
0
}
1356
1357
/************************************************************************/
1358
/*                          OSRGetAttrValue()                           */
1359
/************************************************************************/
1360
1361
/**
1362
 * \brief Fetch indicated attribute of named node.
1363
 *
1364
 * This function is the same as OGRSpatialReference::GetAttrValue()
1365
 */
1366
const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1367
                                        const char *pszKey, int iChild)
1368
1369
0
{
1370
0
    VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1371
1372
0
    return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1373
0
}
1374
1375
/************************************************************************/
1376
/*                             GetName()                                */
1377
/************************************************************************/
1378
1379
/**
1380
 * \brief Return the CRS name.
1381
 *
1382
 * The returned value is only short lived and should not be used after other
1383
 * calls to methods on this object.
1384
 *
1385
 * @since GDAL 3.0
1386
 */
1387
1388
const char *OGRSpatialReference::GetName() const
1389
0
{
1390
0
    TAKE_OPTIONAL_LOCK();
1391
1392
0
    d->refreshProjObj();
1393
0
    if (!d->m_pj_crs)
1394
0
        return nullptr;
1395
0
    const char *pszName = proj_get_name(d->m_pj_crs);
1396
#if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1397
    if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1398
    {
1399
        // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1400
        PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1401
        if (baseCRS)
1402
        {
1403
            pszName = proj_get_name(baseCRS);
1404
            // pszName still remains valid after proj_destroy(), since
1405
            // d->m_pj_crs keeps a reference to the base CRS C++ object.
1406
            proj_destroy(baseCRS);
1407
        }
1408
    }
1409
#endif
1410
0
    return pszName;
1411
0
}
1412
1413
/************************************************************************/
1414
/*                           OSRGetName()                               */
1415
/************************************************************************/
1416
1417
/**
1418
 * \brief Return the CRS name.
1419
 *
1420
 * The returned value is only short lived and should not be used after other
1421
 * calls to methods on this object.
1422
 *
1423
 * @since GDAL 3.0
1424
 */
1425
const char *OSRGetName(OGRSpatialReferenceH hSRS)
1426
1427
0
{
1428
0
    VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1429
1430
0
    return ToPointer(hSRS)->GetName();
1431
0
}
1432
1433
/************************************************************************/
1434
/*                       GetCelestialBodyName()                         */
1435
/************************************************************************/
1436
1437
/**
1438
 * \brief Return the name of the celestial body of this CRS.
1439
 *
1440
 * e.g. "Earth" for an Earth CRS
1441
 *
1442
 * The returned value is only short lived and should not be used after other
1443
 * calls to methods on this object.
1444
 *
1445
 * @since GDAL 3.12 and PROJ 8.1
1446
 */
1447
1448
const char *OGRSpatialReference::GetCelestialBodyName() const
1449
0
{
1450
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
1451
1452
0
    TAKE_OPTIONAL_LOCK();
1453
1454
0
    d->refreshProjObj();
1455
0
    if (!d->m_pj_crs)
1456
0
        return nullptr;
1457
0
    d->demoteFromBoundCRS();
1458
0
    const char *name =
1459
0
        proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1460
0
    if (name)
1461
0
    {
1462
0
        d->m_celestialBodyName = name;
1463
0
    }
1464
0
    d->undoDemoteFromBoundCRS();
1465
0
    return d->m_celestialBodyName.c_str();
1466
#else
1467
    if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1468
        0.05 * SRS_WGS84_SEMIMAJOR)
1469
        return "Earth";
1470
    const char *pszAuthName = GetAuthorityName(nullptr);
1471
    if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1472
        return "Earth";
1473
    return nullptr;
1474
#endif
1475
0
}
1476
1477
/************************************************************************/
1478
/*                       OSRGetCelestialBodyName()                      */
1479
/************************************************************************/
1480
1481
/**
1482
 * \brief Return the name of the celestial body of this CRS.
1483
 *
1484
 * e.g. "Earth" for an Earth CRS
1485
 *
1486
 * The returned value is only short lived and should not be used after other
1487
 * calls to methods on this object.
1488
 *
1489
 * @since GDAL 3.12 and PROJ 8.1
1490
 */
1491
1492
const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1493
1494
0
{
1495
0
    VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1496
1497
0
    return ToPointer(hSRS)->GetCelestialBodyName();
1498
0
}
1499
1500
/************************************************************************/
1501
/*                               Clone()                                */
1502
/************************************************************************/
1503
1504
/**
1505
 * \brief Make a duplicate of this OGRSpatialReference.
1506
 *
1507
 * This method is the same as the C function OSRClone().
1508
 *
1509
 * @return a new SRS, which becomes the responsibility of the caller.
1510
 */
1511
1512
OGRSpatialReference *OGRSpatialReference::Clone() const
1513
1514
0
{
1515
0
    OGRSpatialReference *poNewRef = new OGRSpatialReference();
1516
1517
0
    TAKE_OPTIONAL_LOCK();
1518
1519
0
    d->refreshProjObj();
1520
0
    if (d->m_pj_crs != nullptr)
1521
0
        poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1522
0
    if (d->m_bHasCenterLong && d->m_poRoot)
1523
0
    {
1524
0
        poNewRef->d->setRoot(d->m_poRoot->Clone());
1525
0
    }
1526
0
    poNewRef->d->m_axisMapping = d->m_axisMapping;
1527
0
    poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1528
0
    poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1529
0
    return poNewRef;
1530
0
}
1531
1532
/************************************************************************/
1533
/*                              OSRClone()                              */
1534
/************************************************************************/
1535
1536
/**
1537
 * \brief Make a duplicate of this OGRSpatialReference.
1538
 *
1539
 * This function is the same as OGRSpatialReference::Clone()
1540
 */
1541
OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1542
1543
0
{
1544
0
    VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1545
1546
0
    return ToHandle(ToPointer(hSRS)->Clone());
1547
0
}
1548
1549
/************************************************************************/
1550
/*                            dumpReadable()                            */
1551
/************************************************************************/
1552
1553
/** Dump pretty wkt to stdout, mostly for debugging.
1554
 */
1555
void OGRSpatialReference::dumpReadable()
1556
1557
0
{
1558
0
    char *pszPrettyWkt = nullptr;
1559
1560
0
    const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1561
0
    exportToWkt(&pszPrettyWkt, apszOptions);
1562
0
    printf("%s\n", pszPrettyWkt); /*ok*/
1563
0
    CPLFree(pszPrettyWkt);
1564
0
}
1565
1566
/************************************************************************/
1567
/*                         exportToPrettyWkt()                          */
1568
/************************************************************************/
1569
1570
/**
1571
 * Convert this SRS into a nicely formatted WKT 1 string for display to a
1572
 * person.
1573
 *
1574
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1575
 * Issues</a> page for implementation details of WKT 1 in OGR.
1576
 *
1577
 * Note that the returned WKT string should be freed with
1578
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1579
 *
1580
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1581
 * option. Valid values are the one of the FORMAT option of
1582
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1583
 *
1584
 * This method is the same as the C function OSRExportToPrettyWkt().
1585
 *
1586
 * @param ppszResult the resulting string is returned in this pointer.
1587
 * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1588
 *   stripped off.
1589
 *
1590
 * @return OGRERR_NONE if successful.
1591
 */
1592
1593
OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1594
                                              int bSimplify) const
1595
1596
0
{
1597
0
    CPLStringList aosOptions;
1598
0
    aosOptions.SetNameValue("MULTILINE", "YES");
1599
0
    if (bSimplify)
1600
0
    {
1601
0
        aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1602
0
    }
1603
0
    return exportToWkt(ppszResult, aosOptions.List());
1604
0
}
1605
1606
/************************************************************************/
1607
/*                        OSRExportToPrettyWkt()                        */
1608
/************************************************************************/
1609
1610
/**
1611
 * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1612
 * person.
1613
 *
1614
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1615
 * option. Valid values are the one of the FORMAT option of
1616
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1617
 *
1618
 * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1619
 */
1620
1621
OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1622
                                        char **ppszReturn, int bSimplify)
1623
1624
0
{
1625
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1626
1627
0
    *ppszReturn = nullptr;
1628
1629
0
    return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1630
0
}
1631
1632
/************************************************************************/
1633
/*                            exportToWkt()                             */
1634
/************************************************************************/
1635
1636
/**
1637
 * \brief Convert this SRS into WKT 1 format.
1638
 *
1639
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1640
 * Issues</a> page for implementation details of WKT 1 in OGR.
1641
 *
1642
 * Note that the returned WKT string should be freed with
1643
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1644
 *
1645
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1646
 * option. Valid values are the one of the FORMAT option of
1647
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1648
 *
1649
 * This method is the same as the C function OSRExportToWkt().
1650
 *
1651
 * @param ppszResult the resulting string is returned in this pointer.
1652
 *
1653
 * @return OGRERR_NONE if successful.
1654
 */
1655
1656
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1657
1658
0
{
1659
0
    return exportToWkt(ppszResult, nullptr);
1660
0
}
1661
1662
/************************************************************************/
1663
/*                GDAL_proj_crs_create_bound_crs_to_WGS84()             */
1664
/************************************************************************/
1665
1666
static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1667
                                                   bool onlyIfEPSGCode,
1668
                                                   bool canModifyHorizPart)
1669
0
{
1670
0
    PJ *ret = nullptr;
1671
0
    if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1672
0
    {
1673
0
        auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1674
0
        auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1675
0
        if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1676
0
            vertCRS &&
1677
0
            (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1678
0
        {
1679
0
            auto boundHoriz =
1680
0
                canModifyHorizPart
1681
0
                    ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1682
0
                    : proj_clone(ctx, horizCRS);
1683
0
            auto boundVert =
1684
0
                proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1685
0
            if (boundHoriz && boundVert)
1686
0
            {
1687
0
                ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1688
0
                                               boundHoriz, boundVert);
1689
0
            }
1690
0
            proj_destroy(boundHoriz);
1691
0
            proj_destroy(boundVert);
1692
0
        }
1693
0
        proj_destroy(horizCRS);
1694
0
        proj_destroy(vertCRS);
1695
0
    }
1696
0
    else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1697
0
             (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1698
0
    {
1699
0
        ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1700
0
    }
1701
0
    return ret;
1702
0
}
1703
1704
/************************************************************************/
1705
/*                            exportToWkt()                             */
1706
/************************************************************************/
1707
1708
/**
1709
 * Convert this SRS into a WKT string.
1710
 *
1711
 * Note that the returned WKT string should be freed with
1712
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1713
 *
1714
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1715
 * Issues</a> page for implementation details of WKT 1 in OGR.
1716
 *
1717
 * @param ppszResult the resulting string is returned in this pointer.
1718
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1719
 * supported options are
1720
 * <ul>
1721
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1722
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1723
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1724
 *     node is returned.
1725
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1726
 *     node is returned.
1727
 *     WKT1 is an alias of WKT1_GDAL.
1728
 *     WKT2 will default to the latest revision implemented (currently
1729
 *     WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1730
 * </li>
1731
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1732
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1733
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1734
 * height (for example for use with LAS 1.4 WKT1).
1735
 * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1736
 * </ul>
1737
 *
1738
 * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1739
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1740
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1741
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1742
 * TOWGS84[] node may be added.
1743
 *
1744
 * @return OGRERR_NONE if successful.
1745
 * @since GDAL 3.0
1746
 */
1747
1748
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1749
                                        const char *const *papszOptions) const
1750
0
{
1751
    // In the past calling this method was thread-safe, even if we never
1752
    // guaranteed it. Now proj_as_wkt() will cache the result internally,
1753
    // so this is no longer thread-safe.
1754
0
    std::lock_guard oLock(d->m_mutex);
1755
1756
0
    d->refreshProjObj();
1757
0
    if (!d->m_pj_crs)
1758
0
    {
1759
0
        *ppszResult = CPLStrdup("");
1760
0
        return OGRERR_FAILURE;
1761
0
    }
1762
1763
0
    if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1764
0
    {
1765
0
        return d->m_poRoot->exportToWkt(ppszResult);
1766
0
    }
1767
1768
0
    auto ctxt = d->getPROJContext();
1769
0
    auto wktFormat = PJ_WKT1_GDAL;
1770
0
    const char *pszFormat =
1771
0
        CSLFetchNameValueDef(papszOptions, "FORMAT",
1772
0
                             CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1773
0
    if (EQUAL(pszFormat, "DEFAULT"))
1774
0
        pszFormat = "";
1775
1776
0
    if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1777
0
    {
1778
0
        wktFormat = PJ_WKT1_ESRI;
1779
0
    }
1780
0
    else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1781
0
             EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1782
0
    {
1783
0
        wktFormat = PJ_WKT1_GDAL;
1784
0
    }
1785
0
    else if (EQUAL(pszFormat, "WKT2_2015"))
1786
0
    {
1787
0
        wktFormat = PJ_WKT2_2015;
1788
0
    }
1789
0
    else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1790
0
             EQUAL(pszFormat, "WKT2_2019"))
1791
0
    {
1792
0
        wktFormat = PJ_WKT2_2018;
1793
0
    }
1794
0
    else if (pszFormat[0] == '\0')
1795
0
    {
1796
        // cppcheck-suppress knownConditionTrueFalse
1797
0
        if (IsDerivedGeographic())
1798
0
        {
1799
0
            wktFormat = PJ_WKT2_2018;
1800
0
        }
1801
0
        else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1802
0
                 GetAxesCount() == 3)
1803
0
        {
1804
0
            wktFormat = PJ_WKT2_2018;
1805
0
        }
1806
0
    }
1807
0
    else
1808
0
    {
1809
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1810
0
        *ppszResult = CPLStrdup("");
1811
0
        return OGRERR_FAILURE;
1812
0
    }
1813
1814
0
    CPLStringList aosOptions;
1815
0
    if (wktFormat != PJ_WKT1_ESRI)
1816
0
    {
1817
0
        aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1818
0
    }
1819
0
    aosOptions.SetNameValue(
1820
0
        "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1821
1822
0
    const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1823
0
        papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1824
0
    if (pszAllowEllpsHeightAsVertCS)
1825
0
    {
1826
0
        aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1827
0
                                pszAllowEllpsHeightAsVertCS);
1828
0
    }
1829
1830
0
    PJ *boundCRS = nullptr;
1831
0
    if (wktFormat == PJ_WKT1_GDAL &&
1832
0
        CPLTestBool(CSLFetchNameValueDef(
1833
0
            papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1834
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1835
0
    {
1836
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1837
0
            d->getPROJContext(), d->m_pj_crs, true, true);
1838
0
    }
1839
1840
0
    CPLErrorAccumulator oErrorAccumulator;
1841
0
    const char *pszWKT;
1842
0
    {
1843
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1844
0
        CPL_IGNORE_RET_VAL(oAccumulator);
1845
0
        pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1846
0
                             aosOptions.List());
1847
0
    }
1848
0
    for (const auto &oError : oErrorAccumulator.GetErrors())
1849
0
    {
1850
0
        if (pszFormat[0] == '\0' &&
1851
0
            (oError.msg.find("Unsupported conversion method") !=
1852
0
                 std::string::npos ||
1853
0
             oError.msg.find("can only be exported to WKT2") !=
1854
0
                 std::string::npos ||
1855
0
             oError.msg.find("can only be exported since WKT2:2019") !=
1856
0
                 std::string::npos))
1857
0
        {
1858
0
            CPLErrorReset();
1859
            // If we cannot export in the default mode (WKT1), retry with WKT2
1860
0
            pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1861
0
                                 PJ_WKT2_2018, aosOptions.List());
1862
0
            break;
1863
0
        }
1864
0
        CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1865
0
    }
1866
1867
0
    if (!pszWKT)
1868
0
    {
1869
0
        *ppszResult = CPLStrdup("");
1870
0
        proj_destroy(boundCRS);
1871
0
        return OGRERR_FAILURE;
1872
0
    }
1873
1874
0
    if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1875
0
    {
1876
0
        OGR_SRSNode oRoot;
1877
0
        oRoot.importFromWkt(&pszWKT);
1878
0
        oRoot.StripNodes("AXIS");
1879
0
        if (EQUAL(pszFormat, "SFSQL"))
1880
0
        {
1881
0
            oRoot.StripNodes("TOWGS84");
1882
0
        }
1883
0
        oRoot.StripNodes("AUTHORITY");
1884
0
        oRoot.StripNodes("EXTENSION");
1885
0
        OGRErr eErr;
1886
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1887
0
            eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1888
0
        else
1889
0
            eErr = oRoot.exportToWkt(ppszResult);
1890
0
        proj_destroy(boundCRS);
1891
0
        return eErr;
1892
0
    }
1893
1894
0
    *ppszResult = CPLStrdup(pszWKT);
1895
1896
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1897
    if (wktFormat == PJ_WKT2_2018)
1898
    {
1899
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1900
        // related to a wrong EPSG code assigned to UTM South conversions
1901
        char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1902
        if (pszPtr)
1903
        {
1904
            pszPtr += strlen("CONVERSION[\"UTM zone ");
1905
            const int nZone = atoi(pszPtr);
1906
            while (*pszPtr >= '0' && *pszPtr <= '9')
1907
                ++pszPtr;
1908
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1909
                pszPtr[1] == '"' && pszPtr[2] == ',')
1910
            {
1911
                pszPtr += 3;
1912
                int nLevel = 0;
1913
                bool bInString = false;
1914
                // Find the ID node corresponding to this CONVERSION node
1915
                while (*pszPtr)
1916
                {
1917
                    if (bInString)
1918
                    {
1919
                        if (*pszPtr == '"' && pszPtr[1] == '"')
1920
                        {
1921
                            ++pszPtr;
1922
                        }
1923
                        else if (*pszPtr == '"')
1924
                        {
1925
                            bInString = false;
1926
                        }
1927
                    }
1928
                    else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1929
                    {
1930
                        if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1931
                                                              17000 + nZone)))
1932
                        {
1933
                            CPLAssert(pszPtr[11] == '7');
1934
                            CPLAssert(pszPtr[12] == '0');
1935
                            pszPtr[11] = '6';
1936
                            pszPtr[12] = '1';
1937
                        }
1938
                        break;
1939
                    }
1940
                    else if (*pszPtr == '"')
1941
                    {
1942
                        bInString = true;
1943
                    }
1944
                    else if (*pszPtr == '[')
1945
                    {
1946
                        ++nLevel;
1947
                    }
1948
                    else if (*pszPtr == ']')
1949
                    {
1950
                        --nLevel;
1951
                    }
1952
1953
                    ++pszPtr;
1954
                }
1955
            }
1956
        }
1957
    }
1958
#endif
1959
1960
0
    proj_destroy(boundCRS);
1961
0
    return OGRERR_NONE;
1962
0
}
1963
1964
/************************************************************************/
1965
/*                            exportToWkt()                             */
1966
/************************************************************************/
1967
1968
/**
1969
 * Convert this SRS into a WKT string.
1970
 *
1971
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1972
 * Issues</a> page for implementation details of WKT 1 in OGR.
1973
 *
1974
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1975
 * supported options are
1976
 * <ul>
1977
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1978
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1979
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1980
 *     node is returned.
1981
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1982
 *     node is returned.
1983
 *     WKT1 is an alias of WKT1_GDAL.
1984
 *     WKT2 will default to the latest revision implemented (currently
1985
 *     WKT2_2019)
1986
 * </li>
1987
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1988
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1989
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1990
 * height (for example for use with LAS 1.4 WKT1).
1991
 * Requires PROJ 7.2.1.</li>
1992
 * </ul>
1993
 *
1994
 * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1995
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1996
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1997
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1998
 * TOWGS84[] node may be added.
1999
 *
2000
 * @return a non-empty string if successful.
2001
 * @since GDAL 3.9
2002
 */
2003
2004
std::string
2005
OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2006
0
{
2007
0
    std::string osWKT;
2008
0
    char *pszWKT = nullptr;
2009
0
    if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2010
0
        osWKT = pszWKT;
2011
0
    CPLFree(pszWKT);
2012
0
    return osWKT;
2013
0
}
2014
2015
/************************************************************************/
2016
/*                           OSRExportToWkt()                           */
2017
/************************************************************************/
2018
2019
/**
2020
 * \brief Convert this SRS into WKT 1 format.
2021
 *
2022
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2023
 * Issues</a> page for implementation details of WKT in OGR.
2024
 *
2025
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2026
 * option. Valid values are the one of the FORMAT option of
2027
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2028
 *
2029
 * This function is the same as OGRSpatialReference::exportToWkt().
2030
 */
2031
2032
OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2033
2034
0
{
2035
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2036
2037
0
    *ppszReturn = nullptr;
2038
2039
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn);
2040
0
}
2041
2042
/************************************************************************/
2043
/*                          OSRExportToWktEx()                          */
2044
/************************************************************************/
2045
2046
/**
2047
 * \brief Convert this SRS into WKT format.
2048
 *
2049
 * This function is the same as OGRSpatialReference::exportToWkt(char **
2050
 * ppszResult,const char* const* papszOptions ) const
2051
 *
2052
 * @since GDAL 3.0
2053
 */
2054
2055
OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2056
                        const char *const *papszOptions)
2057
0
{
2058
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2059
2060
0
    *ppszReturn = nullptr;
2061
2062
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2063
0
}
2064
2065
/************************************************************************/
2066
/*                       exportToPROJJSON()                             */
2067
/************************************************************************/
2068
2069
/**
2070
 * Convert this SRS into a PROJJSON string.
2071
 *
2072
 * Note that the returned JSON string should be freed with
2073
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
2074
 *
2075
 * @param ppszResult the resulting string is returned in this pointer.
2076
 * @param papszOptions NULL terminated list of options, or NULL. Currently
2077
 * supported options are
2078
 * <ul>
2079
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
2080
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2081
 * on).</li>
2082
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2083
 * disable it.</li>
2084
 * </ul>
2085
 *
2086
 * @return OGRERR_NONE if successful.
2087
 * @since GDAL 3.1 and PROJ 6.2
2088
 */
2089
2090
OGRErr OGRSpatialReference::exportToPROJJSON(
2091
    char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2092
0
{
2093
0
    TAKE_OPTIONAL_LOCK();
2094
2095
0
    d->refreshProjObj();
2096
0
    if (!d->m_pj_crs)
2097
0
    {
2098
0
        *ppszResult = nullptr;
2099
0
        return OGRERR_FAILURE;
2100
0
    }
2101
2102
0
    const char *pszPROJJSON =
2103
0
        proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2104
2105
0
    if (!pszPROJJSON)
2106
0
    {
2107
0
        *ppszResult = CPLStrdup("");
2108
0
        return OGRERR_FAILURE;
2109
0
    }
2110
2111
0
    *ppszResult = CPLStrdup(pszPROJJSON);
2112
2113
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2114
    {
2115
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2116
        // related to a wrong EPSG code assigned to UTM South conversions
2117
        char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2118
        if (pszPtr)
2119
        {
2120
            pszPtr += strlen("\"name\": \"UTM zone ");
2121
            const int nZone = atoi(pszPtr);
2122
            while (*pszPtr >= '0' && *pszPtr <= '9')
2123
                ++pszPtr;
2124
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2125
            {
2126
                pszPtr += 2;
2127
                int nLevel = 0;
2128
                bool bInString = false;
2129
                // Find the id node corresponding to this conversion node
2130
                while (*pszPtr)
2131
                {
2132
                    if (bInString)
2133
                    {
2134
                        if (*pszPtr == '\\')
2135
                        {
2136
                            ++pszPtr;
2137
                        }
2138
                        else if (*pszPtr == '"')
2139
                        {
2140
                            bInString = false;
2141
                        }
2142
                    }
2143
                    else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2144
                    {
2145
                        const char *pszNextEndCurl = strchr(pszPtr, '}');
2146
                        const char *pszAuthEPSG =
2147
                            strstr(pszPtr, "\"authority\": \"EPSG\"");
2148
                        char *pszCode = strstr(
2149
                            pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2150
                        if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2151
                            pszNextEndCurl - pszAuthEPSG > 0 &&
2152
                            pszNextEndCurl - pszCode > 0)
2153
                        {
2154
                            CPLAssert(pszCode[9] == '7');
2155
                            CPLAssert(pszCode[10] == '0');
2156
                            pszCode[9] = '6';
2157
                            pszCode[10] = '1';
2158
                        }
2159
                        break;
2160
                    }
2161
                    else if (*pszPtr == '"')
2162
                    {
2163
                        bInString = true;
2164
                    }
2165
                    else if (*pszPtr == '{' || *pszPtr == '[')
2166
                    {
2167
                        ++nLevel;
2168
                    }
2169
                    else if (*pszPtr == '}' || *pszPtr == ']')
2170
                    {
2171
                        --nLevel;
2172
                    }
2173
2174
                    ++pszPtr;
2175
                }
2176
            }
2177
        }
2178
    }
2179
#endif
2180
2181
0
    return OGRERR_NONE;
2182
0
}
2183
2184
/************************************************************************/
2185
/*                          OSRExportToPROJJSON()                       */
2186
/************************************************************************/
2187
2188
/**
2189
 * \brief Convert this SRS into PROJJSON format.
2190
 *
2191
 * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2192
 *
2193
 * @since GDAL 3.1 and PROJ 6.2
2194
 */
2195
2196
OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2197
                           const char *const *papszOptions)
2198
0
{
2199
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2200
2201
0
    *ppszReturn = nullptr;
2202
2203
0
    return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2204
0
}
2205
2206
/************************************************************************/
2207
/*                           importFromWkt()                            */
2208
/************************************************************************/
2209
2210
/**
2211
 * \brief Import from WKT string.
2212
 *
2213
 * This method will wipe the existing SRS definition, and
2214
 * reassign it based on the contents of the passed WKT string.  Only as
2215
 * much of the input string as needed to construct this SRS is consumed from
2216
 * the input string, and the input string pointer
2217
 * is then updated to point to the remaining (unused) input.
2218
 *
2219
 * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2220
 * the CRS contained in it will be used to fill the OGRSpatialReference object,
2221
 * and the coordinate epoch potentially present used as the coordinate epoch
2222
 * property of the OGRSpatialReference object.
2223
 *
2224
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2225
 * Issues</a> page for implementation details of WKT in OGR.
2226
 *
2227
 * This method is the same as the C function OSRImportFromWkt().
2228
 *
2229
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2230
 * point to remaining unused input text.
2231
 *
2232
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2233
 * fails for any reason.
2234
 * @since GDAL 2.3
2235
 */
2236
2237
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2238
2239
0
{
2240
0
    return importFromWkt(ppszInput, nullptr);
2241
0
}
2242
2243
/************************************************************************/
2244
/*                           importFromWkt()                            */
2245
/************************************************************************/
2246
2247
/*! @cond Doxygen_Suppress */
2248
2249
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2250
                                          CSLConstList papszOptions)
2251
2252
0
{
2253
0
    return importFromWkt(&pszInput, papszOptions);
2254
0
}
2255
2256
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2257
                                          CSLConstList papszOptions)
2258
2259
0
{
2260
0
    TAKE_OPTIONAL_LOCK();
2261
2262
0
    if (!ppszInput || !*ppszInput)
2263
0
        return OGRERR_FAILURE;
2264
2265
0
    if (strlen(*ppszInput) > 100 * 1000 &&
2266
0
        CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2267
0
    {
2268
0
        CPLError(CE_Failure, CPLE_NotSupported,
2269
0
                 "Suspiciously large input for importFromWkt(). Rejecting it. "
2270
0
                 "You can remove this limitation by definition the "
2271
0
                 "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2272
0
        return OGRERR_FAILURE;
2273
0
    }
2274
2275
0
    Clear();
2276
2277
0
    bool canCache = false;
2278
0
    auto tlsCache = OSRGetProjTLSCache();
2279
0
    std::string osWkt;
2280
0
    if (**ppszInput)
2281
0
    {
2282
0
        osWkt = *ppszInput;
2283
0
        auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2284
0
        if (cachedObj)
2285
0
        {
2286
0
            d->setPjCRS(cachedObj);
2287
0
        }
2288
0
        else
2289
0
        {
2290
0
            CPLStringList aosOptions(papszOptions);
2291
0
            if (aosOptions.FetchNameValue("STRICT") == nullptr)
2292
0
                aosOptions.SetNameValue("STRICT", "NO");
2293
0
            PROJ_STRING_LIST warnings = nullptr;
2294
0
            PROJ_STRING_LIST errors = nullptr;
2295
0
            auto ctxt = d->getPROJContext();
2296
0
            auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2297
0
                                           &warnings, &errors);
2298
0
            d->setPjCRS(pj);
2299
2300
0
            for (auto iter = warnings; iter && *iter; ++iter)
2301
0
            {
2302
0
                d->m_wktImportWarnings.push_back(*iter);
2303
0
            }
2304
0
            for (auto iter = errors; iter && *iter; ++iter)
2305
0
            {
2306
0
                d->m_wktImportErrors.push_back(*iter);
2307
0
                if (!d->m_pj_crs)
2308
0
                {
2309
0
                    CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2310
0
                }
2311
0
            }
2312
0
            if (warnings == nullptr && errors == nullptr)
2313
0
            {
2314
0
                canCache = true;
2315
0
            }
2316
0
            proj_string_list_destroy(warnings);
2317
0
            proj_string_list_destroy(errors);
2318
0
        }
2319
0
    }
2320
0
    if (!d->m_pj_crs)
2321
0
        return OGRERR_CORRUPT_DATA;
2322
2323
    // Only accept CRS objects
2324
0
    if (!proj_is_crs(d->m_pj_crs))
2325
0
    {
2326
0
        Clear();
2327
0
        return OGRERR_CORRUPT_DATA;
2328
0
    }
2329
2330
0
    if (canCache)
2331
0
    {
2332
0
        tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2333
0
    }
2334
2335
0
    if (strstr(*ppszInput, "CENTER_LONG"))
2336
0
    {
2337
0
        auto poRoot = new OGR_SRSNode();
2338
0
        d->setRoot(poRoot);
2339
0
        const char *pszTmp = *ppszInput;
2340
0
        poRoot->importFromWkt(&pszTmp);
2341
0
        d->m_bHasCenterLong = true;
2342
0
    }
2343
2344
    // TODO? we don't really update correctly since we assume that the
2345
    // passed string is only WKT.
2346
0
    *ppszInput += strlen(*ppszInput);
2347
0
    return OGRERR_NONE;
2348
2349
#if no_longer_implemented_for_now
2350
    /* -------------------------------------------------------------------- */
2351
    /*      The following seems to try and detect and unconsumed            */
2352
    /*      VERTCS[] coordinate system definition (ESRI style) and to       */
2353
    /*      import and attach it to the existing root.  Likely we will      */
2354
    /*      need to extend this somewhat to bring it into an acceptable     */
2355
    /*      OGRSpatialReference organization at some point.                 */
2356
    /* -------------------------------------------------------------------- */
2357
    if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2358
    {
2359
        if (((*ppszInput)[0]) == ',')
2360
            (*ppszInput)++;
2361
        OGR_SRSNode *poNewChild = new OGR_SRSNode();
2362
        poRoot->AddChild(poNewChild);
2363
        return poNewChild->importFromWkt(ppszInput);
2364
    }
2365
#endif
2366
0
}
2367
2368
/*! @endcond */
2369
2370
/**
2371
 * \brief Import from WKT string.
2372
 *
2373
 * This method will wipe the existing SRS definition, and
2374
 * reassign it based on the contents of the passed WKT string.  Only as
2375
 * much of the input string as needed to construct this SRS is consumed from
2376
 * the input string, and the input string pointer
2377
 * is then updated to point to the remaining (unused) input.
2378
 *
2379
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2380
 * Issues</a> page for implementation details of WKT in OGR.
2381
 *
2382
 * This method is the same as the C function OSRImportFromWkt().
2383
 *
2384
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2385
 * point to remaining unused input text.
2386
 *
2387
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2388
 * fails for any reason.
2389
 * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2390
 * char*)
2391
 */
2392
2393
OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2394
2395
0
{
2396
0
    return importFromWkt(const_cast<const char **>(ppszInput));
2397
0
}
2398
2399
/**
2400
 * \brief Import from WKT string.
2401
 *
2402
 * This method will wipe the existing SRS definition, and
2403
 * reassign it based on the contents of the passed WKT string.  Only as
2404
 * much of the input string as needed to construct this SRS is consumed from
2405
 * the input string, and the input string pointer
2406
 * is then updated to point to the remaining (unused) input.
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
 * @param pszInput Input WKT
2412
 *
2413
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2414
 * fails for any reason.
2415
 * @since GDAL 2.3
2416
 */
2417
2418
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2419
0
{
2420
0
    return importFromWkt(&pszInput);
2421
0
}
2422
2423
/************************************************************************/
2424
/*                              Validate()                              */
2425
/************************************************************************/
2426
2427
/**
2428
 * \brief Validate CRS imported with importFromWkt() or with modified with
2429
 * direct node manipulations. Otherwise the CRS should be always valid.
2430
 *
2431
 * This method attempts to verify that the spatial reference system is
2432
 * well formed, and consists of known tokens.  The validation is not
2433
 * comprehensive.
2434
 *
2435
 * This method is the same as the C function OSRValidate().
2436
 *
2437
 * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2438
 * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2439
 * but contains non-standard PROJECTION[] values.
2440
 */
2441
2442
OGRErr OGRSpatialReference::Validate() const
2443
2444
0
{
2445
0
    TAKE_OPTIONAL_LOCK();
2446
2447
0
    for (const auto &str : d->m_wktImportErrors)
2448
0
    {
2449
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2450
0
    }
2451
0
    for (const auto &str : d->m_wktImportWarnings)
2452
0
    {
2453
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2454
0
    }
2455
0
    if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2456
0
    {
2457
0
        return OGRERR_CORRUPT_DATA;
2458
0
    }
2459
0
    if (!d->m_wktImportWarnings.empty())
2460
0
    {
2461
0
        return OGRERR_UNSUPPORTED_SRS;
2462
0
    }
2463
0
    return OGRERR_NONE;
2464
0
}
2465
2466
/************************************************************************/
2467
/*                            OSRValidate()                             */
2468
/************************************************************************/
2469
/**
2470
 * \brief Validate SRS tokens.
2471
 *
2472
 * This function is the same as the C++ method OGRSpatialReference::Validate().
2473
 */
2474
OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2475
2476
0
{
2477
0
    VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2478
2479
0
    return OGRSpatialReference::FromHandle(hSRS)->Validate();
2480
0
}
2481
2482
/************************************************************************/
2483
/*                          OSRImportFromWkt()                          */
2484
/************************************************************************/
2485
2486
/**
2487
 * \brief Import from WKT string.
2488
 *
2489
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2490
 * Issues</a> page for implementation details of WKT in OGR.
2491
 *
2492
 * This function is the same as OGRSpatialReference::importFromWkt().
2493
 */
2494
2495
OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2496
2497
0
{
2498
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2499
2500
0
    return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2501
0
}
2502
2503
/************************************************************************/
2504
/*                              SetNode()                               */
2505
/************************************************************************/
2506
2507
/**
2508
 * \brief Set attribute value in spatial reference.
2509
 *
2510
 * Missing intermediate nodes in the path will be created if not already
2511
 * in existence.  If the attribute has no children one will be created and
2512
 * assigned the value otherwise the zeroth child will be assigned the value.
2513
 *
2514
 * This method does the same as the C function OSRSetAttrValue().
2515
 *
2516
 * @param pszNodePath full path to attribute to be set.  For instance
2517
 * "PROJCS|GEOGCS|UNIT".
2518
 *
2519
 * @param pszNewNodeValue value to be assigned to node, such as "meter".
2520
 * This may be NULL if you just want to force creation of the intermediate
2521
 * path.
2522
 *
2523
 * @return OGRERR_NONE on success.
2524
 */
2525
2526
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2527
                                    const char *pszNewNodeValue)
2528
2529
0
{
2530
0
    TAKE_OPTIONAL_LOCK();
2531
2532
0
    char **papszPathTokens =
2533
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2534
2535
0
    if (CSLCount(papszPathTokens) < 1)
2536
0
    {
2537
0
        CSLDestroy(papszPathTokens);
2538
0
        return OGRERR_FAILURE;
2539
0
    }
2540
2541
0
    if (GetRoot() == nullptr ||
2542
0
        !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2543
0
    {
2544
0
        if (EQUAL(papszPathTokens[0], "PROJCS") &&
2545
0
            CSLCount(papszPathTokens) == 1)
2546
0
        {
2547
0
            CSLDestroy(papszPathTokens);
2548
0
            return SetProjCS(pszNewNodeValue);
2549
0
        }
2550
0
        else
2551
0
        {
2552
0
            SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2553
0
        }
2554
0
    }
2555
2556
0
    OGR_SRSNode *poNode = GetRoot();
2557
0
    for (int i = 1; papszPathTokens[i] != nullptr; i++)
2558
0
    {
2559
0
        int j = 0;  // Used after for.
2560
2561
0
        for (; j < poNode->GetChildCount(); j++)
2562
0
        {
2563
0
            if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2564
0
            {
2565
0
                poNode = poNode->GetChild(j);
2566
0
                j = -1;
2567
0
                break;
2568
0
            }
2569
0
        }
2570
2571
0
        if (j != -1)
2572
0
        {
2573
0
            OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2574
0
            poNode->AddChild(poNewNode);
2575
0
            poNode = poNewNode;
2576
0
        }
2577
0
    }
2578
2579
0
    CSLDestroy(papszPathTokens);
2580
2581
0
    if (pszNewNodeValue != nullptr)
2582
0
    {
2583
0
        if (poNode->GetChildCount() > 0)
2584
0
            poNode->GetChild(0)->SetValue(pszNewNodeValue);
2585
0
        else
2586
0
            poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2587
0
    };
2588
0
    return OGRERR_NONE;
2589
0
}
2590
2591
/************************************************************************/
2592
/*                          OSRSetAttrValue()                           */
2593
/************************************************************************/
2594
2595
/**
2596
 * \brief Set attribute value in spatial reference.
2597
 *
2598
 * This function is the same as OGRSpatialReference::SetNode()
2599
 */
2600
OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2601
                                   const char *pszPath, const char *pszValue)
2602
2603
0
{
2604
0
    VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2605
2606
0
    return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2607
0
}
2608
2609
/************************************************************************/
2610
/*                              SetNode()                               */
2611
/************************************************************************/
2612
2613
/**
2614
 * \brief Set attribute value in spatial reference.
2615
 *
2616
 * Missing intermediate nodes in the path will be created if not already
2617
 * in existence.  If the attribute has no children one will be created and
2618
 * assigned the value otherwise the zeroth child will be assigned the value.
2619
 *
2620
 * This method does the same as the C function OSRSetAttrValue().
2621
 *
2622
 * @param pszNodePath full path to attribute to be set.  For instance
2623
 * "PROJCS|GEOGCS|UNIT".
2624
 *
2625
 * @param dfValue value to be assigned to node.
2626
 *
2627
 * @return OGRERR_NONE on success.
2628
 */
2629
2630
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2631
2632
0
{
2633
0
    char szValue[64] = {'\0'};
2634
2635
0
    if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2636
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2637
0
    else
2638
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2639
2640
0
    return SetNode(pszNodePath, szValue);
2641
0
}
2642
2643
/************************************************************************/
2644
/*                          SetAngularUnits()                           */
2645
/************************************************************************/
2646
2647
/**
2648
 * \brief Set the angular units for the geographic coordinate system.
2649
 *
2650
 * This method creates a UNIT subnode with the specified values as a
2651
 * child of the GEOGCS node.
2652
 *
2653
 * This method does the same as the C function OSRSetAngularUnits().
2654
 *
2655
 * @param pszUnitsName the units name to be used.  Some preferred units
2656
 * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2657
 *
2658
 * @param dfInRadians the value to multiple by an angle in the indicated
2659
 * units to transform to radians.  Some standard conversion factors can
2660
 * be found in ogr_srs_api.h.
2661
 *
2662
 * @return OGRERR_NONE on success.
2663
 */
2664
2665
OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2666
                                            double dfInRadians)
2667
2668
0
{
2669
0
    TAKE_OPTIONAL_LOCK();
2670
2671
0
    d->bNormInfoSet = FALSE;
2672
2673
0
    d->refreshProjObj();
2674
0
    if (!d->m_pj_crs)
2675
0
        return OGRERR_FAILURE;
2676
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2677
0
    if (!geodCRS)
2678
0
        return OGRERR_FAILURE;
2679
0
    proj_destroy(geodCRS);
2680
0
    d->demoteFromBoundCRS();
2681
0
    d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2682
0
                                               pszUnitsName, dfInRadians,
2683
0
                                               nullptr, nullptr));
2684
0
    d->undoDemoteFromBoundCRS();
2685
2686
0
    d->m_osAngularUnits = pszUnitsName;
2687
0
    d->m_dfAngularUnitToRadian = dfInRadians;
2688
2689
0
    return OGRERR_NONE;
2690
0
}
2691
2692
/************************************************************************/
2693
/*                         OSRSetAngularUnits()                         */
2694
/************************************************************************/
2695
2696
/**
2697
 * \brief Set the angular units for the geographic coordinate system.
2698
 *
2699
 * This function is the same as OGRSpatialReference::SetAngularUnits()
2700
 */
2701
OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2702
                          double dfInRadians)
2703
2704
0
{
2705
0
    VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2706
2707
0
    return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2708
0
}
2709
2710
/************************************************************************/
2711
/*                          GetAngularUnits()                           */
2712
/************************************************************************/
2713
2714
/**
2715
 * \brief Fetch angular geographic coordinate system units.
2716
 *
2717
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2718
 * will be assumed.  This method only checks directly under the GEOGCS node
2719
 * for units.
2720
 *
2721
 * This method does the same thing as the C function OSRGetAngularUnits().
2722
 *
2723
 * @param ppszName a pointer to be updated with the pointer to the units name.
2724
 * The returned value remains internal to the OGRSpatialReference and should
2725
 * not be freed, or modified.  It may be invalidated on the next
2726
 * OGRSpatialReference call.
2727
 *
2728
 * @return the value to multiply by angular distances to transform them to
2729
 * radians.
2730
 * @since GDAL 2.3.0
2731
 */
2732
2733
double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2734
2735
0
{
2736
0
    TAKE_OPTIONAL_LOCK();
2737
2738
0
    d->refreshProjObj();
2739
2740
0
    if (!d->m_osAngularUnits.empty())
2741
0
    {
2742
0
        if (ppszName != nullptr)
2743
0
            *ppszName = d->m_osAngularUnits.c_str();
2744
0
        return d->m_dfAngularUnitToRadian;
2745
0
    }
2746
2747
0
    do
2748
0
    {
2749
0
        if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2750
0
        {
2751
0
            break;
2752
0
        }
2753
2754
0
        auto geodCRS =
2755
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2756
0
        if (!geodCRS)
2757
0
        {
2758
0
            break;
2759
0
        }
2760
0
        auto coordSys =
2761
0
            proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2762
0
        proj_destroy(geodCRS);
2763
0
        if (!coordSys)
2764
0
        {
2765
0
            break;
2766
0
        }
2767
0
        if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2768
0
            PJ_CS_TYPE_ELLIPSOIDAL)
2769
0
        {
2770
0
            proj_destroy(coordSys);
2771
0
            break;
2772
0
        }
2773
2774
0
        double dfConvFactor = 0.0;
2775
0
        const char *pszUnitName = nullptr;
2776
0
        if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2777
0
                                   nullptr, nullptr, &dfConvFactor,
2778
0
                                   &pszUnitName, nullptr, nullptr))
2779
0
        {
2780
0
            proj_destroy(coordSys);
2781
0
            break;
2782
0
        }
2783
2784
0
        d->m_osAngularUnits = pszUnitName;
2785
2786
0
        proj_destroy(coordSys);
2787
0
        d->m_dfAngularUnitToRadian = dfConvFactor;
2788
0
    } while (false);
2789
2790
0
    if (d->m_osAngularUnits.empty())
2791
0
    {
2792
0
        d->m_osAngularUnits = "degree";
2793
0
        d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2794
0
    }
2795
2796
0
    if (ppszName != nullptr)
2797
0
        *ppszName = d->m_osAngularUnits.c_str();
2798
0
    return d->m_dfAngularUnitToRadian;
2799
0
}
2800
2801
/**
2802
 * \brief Fetch angular geographic coordinate system units.
2803
 *
2804
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2805
 * will be assumed.  This method only checks directly under the GEOGCS node
2806
 * for units.
2807
 *
2808
 * This method does the same thing as the C function OSRGetAngularUnits().
2809
 *
2810
 * @param ppszName a pointer to be updated with the pointer to the units name.
2811
 * The returned value remains internal to the OGRSpatialReference and should
2812
 * not be freed, or modified.  It may be invalidated on the next
2813
 * OGRSpatialReference call.
2814
 *
2815
 * @return the value to multiply by angular distances to transform them to
2816
 * radians.
2817
 * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2818
 */
2819
2820
double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2821
2822
0
{
2823
0
    return GetAngularUnits(const_cast<const char **>(ppszName));
2824
0
}
2825
2826
/************************************************************************/
2827
/*                         OSRGetAngularUnits()                         */
2828
/************************************************************************/
2829
2830
/**
2831
 * \brief Fetch angular geographic coordinate system units.
2832
 *
2833
 * This function is the same as OGRSpatialReference::GetAngularUnits()
2834
 */
2835
double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2836
2837
0
{
2838
0
    VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2839
2840
0
    return ToPointer(hSRS)->GetAngularUnits(
2841
0
        const_cast<const char **>(ppszName));
2842
0
}
2843
2844
/************************************************************************/
2845
/*                 SetLinearUnitsAndUpdateParameters()                  */
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 or LOCAL_CS node.   It works the same as the
2853
 * SetLinearUnits() method, but it also updates all existing linear
2854
 * projection parameter values from the old units to the new units.
2855
 *
2856
 * @param pszName the units name to be used.  Some preferred units
2857
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2858
 * and SRS_UL_US_FOOT.
2859
 *
2860
 * @param dfInMeters the value to multiple by a length in the indicated
2861
 * units to transform to meters.  Some standard conversion factors can
2862
 * be found in ogr_srs_api.h.
2863
 *
2864
 * @param pszUnitAuthority Unit authority name. Or nullptr
2865
 *
2866
 * @param pszUnitCode Unit code. Or nullptr
2867
 *
2868
 * @return OGRERR_NONE on success.
2869
 */
2870
2871
OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2872
    const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2873
    const char *pszUnitCode)
2874
2875
0
{
2876
0
    TAKE_OPTIONAL_LOCK();
2877
2878
0
    if (dfInMeters <= 0.0)
2879
0
        return OGRERR_FAILURE;
2880
2881
0
    d->refreshProjObj();
2882
0
    if (!d->m_pj_crs)
2883
0
        return OGRERR_FAILURE;
2884
2885
0
    d->demoteFromBoundCRS();
2886
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2887
0
    {
2888
0
        d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2889
0
            d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2890
0
            pszUnitAuthority, pszUnitCode, true));
2891
0
    }
2892
0
    d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2893
0
                                              pszName, dfInMeters,
2894
0
                                              pszUnitAuthority, pszUnitCode));
2895
0
    d->undoDemoteFromBoundCRS();
2896
2897
0
    d->m_osLinearUnits = pszName;
2898
0
    d->dfToMeter = dfInMeters;
2899
2900
0
    return OGRERR_NONE;
2901
0
}
2902
2903
/************************************************************************/
2904
/*                OSRSetLinearUnitsAndUpdateParameters()                */
2905
/************************************************************************/
2906
2907
/**
2908
 * \brief Set the linear units for the projection.
2909
 *
2910
 * This function is the same as
2911
 *   OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2912
 */
2913
OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2914
                                            const char *pszUnits,
2915
                                            double dfInMeters)
2916
2917
0
{
2918
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2919
0
                      OGRERR_FAILURE);
2920
2921
0
    return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2922
0
                                                              dfInMeters);
2923
0
}
2924
2925
/************************************************************************/
2926
/*                           SetLinearUnits()                           */
2927
/************************************************************************/
2928
2929
/**
2930
 * \brief Set the linear units for the projection.
2931
 *
2932
 * This method creates a UNIT subnode with the specified values as a
2933
 * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2934
 * Geographic 3D CRS the vertical axis units will be set.
2935
 *
2936
 * This method does the same as the C function OSRSetLinearUnits().
2937
 *
2938
 * @param pszUnitsName the units name to be used.  Some preferred units
2939
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2940
 * and SRS_UL_US_FOOT.
2941
 *
2942
 * @param dfInMeters the value to multiple by a length in the indicated
2943
 * units to transform to meters.  Some standard conversion factors can
2944
 * be found in ogr_srs_api.h.
2945
 *
2946
 * @return OGRERR_NONE on success.
2947
 */
2948
2949
OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2950
                                           double dfInMeters)
2951
2952
0
{
2953
0
    return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2954
0
}
2955
2956
/************************************************************************/
2957
/*                         OSRSetLinearUnits()                          */
2958
/************************************************************************/
2959
2960
/**
2961
 * \brief Set the linear units for the projection.
2962
 *
2963
 * This function is the same as OGRSpatialReference::SetLinearUnits()
2964
 */
2965
OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2966
                         double dfInMeters)
2967
2968
0
{
2969
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2970
2971
0
    return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2972
0
}
2973
2974
/************************************************************************/
2975
/*                        SetTargetLinearUnits()                        */
2976
/************************************************************************/
2977
2978
/**
2979
 * \brief Set the linear units for the projection.
2980
 *
2981
 * This method creates a UNIT subnode with the specified values as a
2982
 * child of the target node.
2983
 *
2984
 * This method does the same as the C function OSRSetTargetLinearUnits().
2985
 *
2986
 * @param pszTargetKey the keyword to set the linear units for.
2987
 * i.e. "PROJCS" or "VERT_CS"
2988
 *
2989
 * @param pszUnitsName the units name to be used.  Some preferred units
2990
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2991
 * and SRS_UL_US_FOOT.
2992
 *
2993
 * @param dfInMeters the value to multiple by a length in the indicated
2994
 * units to transform to meters.  Some standard conversion factors can
2995
 * be found in ogr_srs_api.h.
2996
 *
2997
 * @param pszUnitAuthority Unit authority name. Or nullptr
2998
 *
2999
 * @param pszUnitCode Unit code. Or nullptr
3000
 *
3001
 * @return OGRERR_NONE on success.
3002
 *
3003
 * @since OGR 1.9.0
3004
 */
3005
3006
OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3007
                                                 const char *pszUnitsName,
3008
                                                 double dfInMeters,
3009
                                                 const char *pszUnitAuthority,
3010
                                                 const char *pszUnitCode)
3011
3012
0
{
3013
0
    TAKE_OPTIONAL_LOCK();
3014
3015
0
    if (dfInMeters <= 0.0)
3016
0
        return OGRERR_FAILURE;
3017
3018
0
    d->refreshProjObj();
3019
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3020
0
    if (pszTargetKey == nullptr)
3021
0
    {
3022
0
        if (!d->m_pj_crs)
3023
0
            return OGRERR_FAILURE;
3024
3025
0
        d->demoteFromBoundCRS();
3026
0
        if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3027
0
        {
3028
0
            d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3029
0
                d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3030
0
                pszUnitAuthority, pszUnitCode, false));
3031
0
        }
3032
0
        d->setPjCRS(proj_crs_alter_cs_linear_unit(
3033
0
            d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3034
0
            pszUnitAuthority, pszUnitCode));
3035
0
        d->undoDemoteFromBoundCRS();
3036
3037
0
        d->m_osLinearUnits = pszUnitsName;
3038
0
        d->dfToMeter = dfInMeters;
3039
3040
0
        return OGRERR_NONE;
3041
0
    }
3042
3043
0
    OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3044
3045
0
    if (poCS == nullptr)
3046
0
        return OGRERR_FAILURE;
3047
3048
0
    char szValue[128] = {'\0'};
3049
0
    if (dfInMeters < std::numeric_limits<int>::max() &&
3050
0
        dfInMeters > std::numeric_limits<int>::min() &&
3051
0
        dfInMeters == static_cast<int>(dfInMeters))
3052
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3053
0
    else
3054
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3055
3056
0
    OGR_SRSNode *poUnits = nullptr;
3057
0
    if (poCS->FindChild("UNIT") >= 0)
3058
0
    {
3059
0
        poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3060
0
        if (poUnits->GetChildCount() < 2)
3061
0
            return OGRERR_FAILURE;
3062
0
        poUnits->GetChild(0)->SetValue(pszUnitsName);
3063
0
        poUnits->GetChild(1)->SetValue(szValue);
3064
0
        if (poUnits->FindChild("AUTHORITY") != -1)
3065
0
            poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3066
0
    }
3067
0
    else
3068
0
    {
3069
0
        poUnits = new OGR_SRSNode("UNIT");
3070
0
        poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3071
0
        poUnits->AddChild(new OGR_SRSNode(szValue));
3072
3073
0
        poCS->AddChild(poUnits);
3074
0
    }
3075
3076
0
    return OGRERR_NONE;
3077
0
}
3078
3079
/************************************************************************/
3080
/*                         OSRSetLinearUnits()                          */
3081
/************************************************************************/
3082
3083
/**
3084
 * \brief Set the linear units for the target node.
3085
 *
3086
 * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3087
 *
3088
 * @since OGR 1.9.0
3089
 */
3090
OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3091
                               const char *pszTargetKey, const char *pszUnits,
3092
                               double dfInMeters)
3093
3094
0
{
3095
0
    VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3096
3097
0
    return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3098
0
                                                 dfInMeters);
3099
0
}
3100
3101
/************************************************************************/
3102
/*                           GetLinearUnits()                           */
3103
/************************************************************************/
3104
3105
/**
3106
 * \brief Fetch linear projection units.
3107
 *
3108
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3109
 * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3110
 * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3111
 * axis units will be returned.
3112
 *
3113
 * This method does the same thing as the C function OSRGetLinearUnits()
3114
 *
3115
 * @param ppszName a pointer to be updated with the pointer to the units name.
3116
 * The returned value remains internal to the OGRSpatialReference and should
3117
 * not be freed, or modified.  It may be invalidated on the next
3118
 * OGRSpatialReference call.
3119
 *
3120
 * @return the value to multiply by linear distances to transform them to
3121
 * meters.
3122
 * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
3123
 */
3124
3125
double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3126
3127
0
{
3128
0
    return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3129
0
}
3130
3131
/**
3132
 * \brief Fetch linear projection units.
3133
 *
3134
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3135
 * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3136
 * for units.
3137
 *
3138
 * This method does the same thing as the C function OSRGetLinearUnits()
3139
 *
3140
 * @param ppszName a pointer to be updated with the pointer to the units name.
3141
 * The returned value remains internal to the OGRSpatialReference and should
3142
 * not be freed, or modified.  It may be invalidated on the next
3143
 * OGRSpatialReference call.
3144
 *
3145
 * @return the value to multiply by linear distances to transform them to
3146
 * meters.
3147
 * @since GDAL 2.3.0
3148
 */
3149
3150
double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3151
3152
0
{
3153
0
    return GetTargetLinearUnits(nullptr, ppszName);
3154
0
}
3155
3156
/************************************************************************/
3157
/*                         OSRGetLinearUnits()                          */
3158
/************************************************************************/
3159
3160
/**
3161
 * \brief Fetch linear projection units.
3162
 *
3163
 * This function is the same as OGRSpatialReference::GetLinearUnits()
3164
 */
3165
double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3166
3167
0
{
3168
0
    VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3169
3170
0
    return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3171
0
}
3172
3173
/************************************************************************/
3174
/*                        GetTargetLinearUnits()                        */
3175
/************************************************************************/
3176
3177
/**
3178
 * \brief Fetch linear units for target.
3179
 *
3180
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3181
 *
3182
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3183
 *
3184
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3185
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3186
 * GEOCCS, GEOGCS and VERT_CS are looked up)
3187
 * @param ppszName a pointer to be updated with the pointer to the units name.
3188
 * The returned value remains internal to the OGRSpatialReference and should not
3189
 * be freed, or modified.  It may be invalidated on the next
3190
 * OGRSpatialReference call. ppszName can be set to NULL.
3191
 *
3192
 * @return the value to multiply by linear distances to transform them to
3193
 * meters.
3194
 *
3195
 * @since OGR 1.9.0
3196
 * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
3197
 * const.
3198
 */
3199
3200
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3201
                                                 const char **ppszName) const
3202
3203
0
{
3204
0
    TAKE_OPTIONAL_LOCK();
3205
3206
0
    d->refreshProjObj();
3207
3208
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3209
0
    if (pszTargetKey == nullptr)
3210
0
    {
3211
        // Use cached result if available
3212
0
        if (!d->m_osLinearUnits.empty())
3213
0
        {
3214
0
            if (ppszName)
3215
0
                *ppszName = d->m_osLinearUnits.c_str();
3216
0
            return d->dfToMeter;
3217
0
        }
3218
3219
0
        while (true)
3220
0
        {
3221
0
            if (d->m_pj_crs == nullptr)
3222
0
            {
3223
0
                break;
3224
0
            }
3225
3226
0
            d->demoteFromBoundCRS();
3227
0
            PJ *coordSys = nullptr;
3228
0
            if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3229
0
            {
3230
0
                for (int iComponent = 0; iComponent < 2; iComponent++)
3231
0
                {
3232
0
                    auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3233
0
                                                       d->m_pj_crs, iComponent);
3234
0
                    if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3235
0
                    {
3236
0
                        auto temp =
3237
0
                            proj_get_source_crs(d->getPROJContext(), subCRS);
3238
0
                        proj_destroy(subCRS);
3239
0
                        subCRS = temp;
3240
0
                    }
3241
0
                    if (subCRS &&
3242
0
                        (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3243
0
                         proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3244
0
                         proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3245
0
                    {
3246
0
                        coordSys = proj_crs_get_coordinate_system(
3247
0
                            d->getPROJContext(), subCRS);
3248
0
                        proj_destroy(subCRS);
3249
0
                        break;
3250
0
                    }
3251
0
                    else if (subCRS)
3252
0
                    {
3253
0
                        proj_destroy(subCRS);
3254
0
                    }
3255
0
                }
3256
0
                if (coordSys == nullptr)
3257
0
                {
3258
0
                    d->undoDemoteFromBoundCRS();
3259
0
                    break;
3260
0
                }
3261
0
            }
3262
0
            else
3263
0
            {
3264
0
                coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3265
0
                                                          d->m_pj_crs);
3266
0
            }
3267
3268
0
            d->undoDemoteFromBoundCRS();
3269
0
            if (!coordSys)
3270
0
            {
3271
0
                break;
3272
0
            }
3273
0
            auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3274
3275
0
            if (csType != PJ_CS_TYPE_CARTESIAN &&
3276
0
                csType != PJ_CS_TYPE_VERTICAL &&
3277
0
                csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3278
0
                csType != PJ_CS_TYPE_SPHERICAL)
3279
0
            {
3280
0
                proj_destroy(coordSys);
3281
0
                break;
3282
0
            }
3283
3284
0
            int axis = 0;
3285
3286
0
            if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3287
0
                csType == PJ_CS_TYPE_SPHERICAL)
3288
0
            {
3289
0
                const int axisCount =
3290
0
                    proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3291
3292
0
                if (axisCount == 3)
3293
0
                {
3294
0
                    axis = 2;
3295
0
                }
3296
0
                else
3297
0
                {
3298
0
                    proj_destroy(coordSys);
3299
0
                    break;
3300
0
                }
3301
0
            }
3302
3303
0
            double dfConvFactor = 0.0;
3304
0
            const char *pszUnitName = nullptr;
3305
0
            if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3306
0
                                       nullptr, nullptr, nullptr, &dfConvFactor,
3307
0
                                       &pszUnitName, nullptr, nullptr))
3308
0
            {
3309
0
                proj_destroy(coordSys);
3310
0
                break;
3311
0
            }
3312
3313
0
            d->m_osLinearUnits = pszUnitName;
3314
0
            d->dfToMeter = dfConvFactor;
3315
0
            if (ppszName)
3316
0
                *ppszName = d->m_osLinearUnits.c_str();
3317
3318
0
            proj_destroy(coordSys);
3319
0
            return dfConvFactor;
3320
0
        }
3321
3322
0
        d->m_osLinearUnits = "unknown";
3323
0
        d->dfToMeter = 1.0;
3324
3325
0
        if (ppszName != nullptr)
3326
0
            *ppszName = d->m_osLinearUnits.c_str();
3327
0
        return 1.0;
3328
0
    }
3329
3330
0
    const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3331
3332
0
    if (ppszName != nullptr)
3333
0
        *ppszName = "unknown";
3334
3335
0
    if (poCS == nullptr)
3336
0
        return 1.0;
3337
3338
0
    for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3339
0
    {
3340
0
        const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3341
3342
0
        if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3343
0
        {
3344
0
            if (ppszName != nullptr)
3345
0
                *ppszName = poChild->GetChild(0)->GetValue();
3346
3347
0
            return CPLAtof(poChild->GetChild(1)->GetValue());
3348
0
        }
3349
0
    }
3350
3351
0
    return 1.0;
3352
0
}
3353
3354
/**
3355
 * \brief Fetch linear units for target.
3356
 *
3357
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3358
 *
3359
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3360
 *
3361
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3362
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3363
 * GEOCCS and VERT_CS are looked up)
3364
 * @param ppszName a pointer to be updated with the pointer to the units name.
3365
 * The returned value remains internal to the OGRSpatialReference and should not
3366
 * be freed, or modified.  It may be invalidated on the next
3367
 * OGRSpatialReference call. ppszName can be set to NULL.
3368
 *
3369
 * @return the value to multiply by linear distances to transform them to
3370
 * meters.
3371
 *
3372
 * @since GDAL 2.3.0
3373
 */
3374
3375
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3376
                                                 char **ppszName) const
3377
3378
0
{
3379
0
    return GetTargetLinearUnits(pszTargetKey,
3380
0
                                const_cast<const char **>(ppszName));
3381
0
}
3382
3383
/************************************************************************/
3384
/*                      OSRGetTargetLinearUnits()                       */
3385
/************************************************************************/
3386
3387
/**
3388
 * \brief Fetch linear projection units.
3389
 *
3390
 * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3391
 *
3392
 * @since OGR 1.9.0
3393
 */
3394
double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3395
                               const char *pszTargetKey, char **ppszName)
3396
3397
0
{
3398
0
    VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3399
3400
0
    return ToPointer(hSRS)->GetTargetLinearUnits(
3401
0
        pszTargetKey, const_cast<const char **>(ppszName));
3402
0
}
3403
3404
/************************************************************************/
3405
/*                          GetPrimeMeridian()                          */
3406
/************************************************************************/
3407
3408
/**
3409
 * \brief Fetch prime meridian info.
3410
 *
3411
 * Returns the offset of the prime meridian from greenwich in degrees,
3412
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3413
 * in the coordinate system definition a value of "Greenwich" and an
3414
 * offset of 0.0 is assumed.
3415
 *
3416
 * If the prime meridian name is returned, the pointer is to an internal
3417
 * copy of the name. It should not be freed, altered or depended on after
3418
 * the next OGR call.
3419
 *
3420
 * This method is the same as the C function OSRGetPrimeMeridian().
3421
 *
3422
 * @param ppszName return location for prime meridian name.  If NULL, name
3423
 * is not returned.
3424
 *
3425
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3426
 * degrees.
3427
 * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3428
 */
3429
3430
double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3431
3432
0
{
3433
0
    TAKE_OPTIONAL_LOCK();
3434
3435
0
    d->refreshProjObj();
3436
3437
0
    if (!d->m_osPrimeMeridianName.empty())
3438
0
    {
3439
0
        if (ppszName != nullptr)
3440
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3441
0
        return d->dfFromGreenwich;
3442
0
    }
3443
3444
0
    while (true)
3445
0
    {
3446
0
        if (!d->m_pj_crs)
3447
0
            break;
3448
3449
0
        auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3450
0
        if (!pm)
3451
0
            break;
3452
3453
0
        d->m_osPrimeMeridianName = proj_get_name(pm);
3454
0
        if (ppszName)
3455
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3456
0
        double dfLongitude = 0.0;
3457
0
        double dfConvFactor = 0.0;
3458
0
        proj_prime_meridian_get_parameters(
3459
0
            d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3460
0
        proj_destroy(pm);
3461
0
        d->dfFromGreenwich =
3462
0
            dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3463
0
        return d->dfFromGreenwich;
3464
0
    }
3465
3466
0
    d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3467
0
    d->dfFromGreenwich = 0.0;
3468
0
    if (ppszName != nullptr)
3469
0
        *ppszName = d->m_osPrimeMeridianName.c_str();
3470
0
    return d->dfFromGreenwich;
3471
0
}
3472
3473
/**
3474
 * \brief Fetch prime meridian info.
3475
 *
3476
 * Returns the offset of the prime meridian from greenwich in degrees,
3477
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3478
 * in the coordinate system definition a value of "Greenwich" and an
3479
 * offset of 0.0 is assumed.
3480
 *
3481
 * If the prime meridian name is returned, the pointer is to an internal
3482
 * copy of the name. It should not be freed, altered or depended on after
3483
 * the next OGR call.
3484
 *
3485
 * This method is the same as the C function OSRGetPrimeMeridian().
3486
 *
3487
 * @param ppszName return location for prime meridian name.  If NULL, name
3488
 * is not returned.
3489
 *
3490
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3491
 * degrees.
3492
 * @since GDAL 2.3.0
3493
 */
3494
3495
double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3496
3497
0
{
3498
0
    return GetPrimeMeridian(const_cast<const char **>(ppszName));
3499
0
}
3500
3501
/************************************************************************/
3502
/*                        OSRGetPrimeMeridian()                         */
3503
/************************************************************************/
3504
3505
/**
3506
 * \brief Fetch prime meridian info.
3507
 *
3508
 * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3509
 */
3510
double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3511
3512
0
{
3513
0
    VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3514
3515
0
    return ToPointer(hSRS)->GetPrimeMeridian(
3516
0
        const_cast<const char **>(ppszName));
3517
0
}
3518
3519
/************************************************************************/
3520
/*                             SetGeogCS()                              */
3521
/************************************************************************/
3522
3523
/**
3524
 * \brief Set geographic coordinate system.
3525
 *
3526
 * This method is used to set the datum, ellipsoid, prime meridian and
3527
 * angular units for a geographic coordinate system.  It can be used on its
3528
 * own to establish a geographic spatial reference, or applied to a
3529
 * projected coordinate system to establish the underlying geographic
3530
 * coordinate system.
3531
 *
3532
 * This method does the same as the C function OSRSetGeogCS().
3533
 *
3534
 * @param pszGeogName user visible name for the geographic coordinate system
3535
 * (not to serve as a key).
3536
 *
3537
 * @param pszDatumName key name for this datum.  The OpenGIS specification
3538
 * lists some known values, and otherwise EPSG datum names with a standard
3539
 * transformation are considered legal keys.
3540
 *
3541
 * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3542
 *
3543
 * @param dfSemiMajor the semi major axis of the spheroid.
3544
 *
3545
 * @param dfInvFlattening the inverse flattening for the spheroid.
3546
 * This can be computed from the semi minor axis as
3547
 * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3548
 *
3549
 * @param pszPMName the name of the prime meridian (not to serve as a key)
3550
 * If this is NULL a default value of "Greenwich" will be used.
3551
 *
3552
 * @param dfPMOffset the longitude of Greenwich relative to this prime
3553
 * meridian. Always in Degrees
3554
 *
3555
 * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3556
 * standard names).  If NULL a value of "degrees" will be assumed.
3557
 *
3558
 * @param dfConvertToRadians value to multiply angular units by to transform
3559
 * them to radians.  A value of SRS_UA_DEGREE_CONV will be used if
3560
 * pszAngularUnits is NULL.
3561
 *
3562
 * @return OGRERR_NONE on success.
3563
 */
3564
3565
OGRErr OGRSpatialReference::SetGeogCS(
3566
    const char *pszGeogName, const char *pszDatumName,
3567
    const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3568
    const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3569
    double dfConvertToRadians)
3570
3571
0
{
3572
0
    TAKE_OPTIONAL_LOCK();
3573
3574
0
    d->bNormInfoSet = FALSE;
3575
0
    d->m_osAngularUnits.clear();
3576
0
    d->m_dfAngularUnitToRadian = 0.0;
3577
0
    d->m_osPrimeMeridianName.clear();
3578
0
    d->dfFromGreenwich = 0.0;
3579
3580
    /* -------------------------------------------------------------------- */
3581
    /*      For a geocentric coordinate system we want to set the datum     */
3582
    /*      and ellipsoid based on the GEOGCS.  Create the GEOGCS in a      */
3583
    /*      temporary srs and use the copy method which has special         */
3584
    /*      handling for GEOCCS.                                            */
3585
    /* -------------------------------------------------------------------- */
3586
0
    if (IsGeocentric())
3587
0
    {
3588
0
        OGRSpatialReference oGCS;
3589
3590
0
        oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3591
0
                       dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3592
0
                       dfConvertToRadians);
3593
0
        return CopyGeogCSFrom(&oGCS);
3594
0
    }
3595
3596
0
    auto cs = proj_create_ellipsoidal_2D_cs(
3597
0
        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3598
0
        dfConvertToRadians);
3599
    // Prime meridian expressed in Degree
3600
0
    auto obj = proj_create_geographic_crs(
3601
0
        d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3602
0
        dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3603
0
    proj_destroy(cs);
3604
3605
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3606
0
        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3607
0
    {
3608
0
        d->setPjCRS(obj);
3609
0
    }
3610
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3611
0
    {
3612
0
        d->setPjCRS(
3613
0
            proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3614
0
        proj_destroy(obj);
3615
0
    }
3616
0
    else
3617
0
    {
3618
0
        proj_destroy(obj);
3619
0
    }
3620
3621
0
    return OGRERR_NONE;
3622
0
}
3623
3624
/************************************************************************/
3625
/*                            OSRSetGeogCS()                            */
3626
/************************************************************************/
3627
3628
/**
3629
 * \brief Set geographic coordinate system.
3630
 *
3631
 * This function is the same as OGRSpatialReference::SetGeogCS()
3632
 */
3633
OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3634
                    const char *pszDatumName, const char *pszSpheroidName,
3635
                    double dfSemiMajor, double dfInvFlattening,
3636
                    const char *pszPMName, double dfPMOffset,
3637
                    const char *pszAngularUnits, double dfConvertToRadians)
3638
3639
0
{
3640
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3641
3642
0
    return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3643
0
                                      pszSpheroidName, dfSemiMajor,
3644
0
                                      dfInvFlattening, pszPMName, dfPMOffset,
3645
0
                                      pszAngularUnits, dfConvertToRadians);
3646
0
}
3647
3648
/************************************************************************/
3649
/*                         SetWellKnownGeogCS()                         */
3650
/************************************************************************/
3651
3652
/**
3653
 * \brief Set a GeogCS based on well known name.
3654
 *
3655
 * This may be called on an empty OGRSpatialReference to make a geographic
3656
 * coordinate system, or on something with an existing PROJCS node to
3657
 * set the underlying geographic coordinate system of a projected coordinate
3658
 * system.
3659
 *
3660
 * The following well known text values are currently supported,
3661
 * Except for "EPSG:n", the others are without dependency on EPSG data files:
3662
 * <ul>
3663
 * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3664
 * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3665
 * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3666
 * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3667
 * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3668
 * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3669
 * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3670
 * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3671
 * </ul>
3672
 *
3673
 * @param pszName name of well known geographic coordinate system.
3674
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3675
 * recognised, the target object is already initialized, or an EPSG value
3676
 * can't be successfully looked up.
3677
 */
3678
3679
OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3680
3681
0
{
3682
0
    TAKE_OPTIONAL_LOCK();
3683
3684
    /* -------------------------------------------------------------------- */
3685
    /*      Check for EPSG authority numbers.                               */
3686
    /* -------------------------------------------------------------------- */
3687
0
    if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3688
0
    {
3689
0
        OGRSpatialReference oSRS2;
3690
0
        const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3691
0
        if (eErr != OGRERR_NONE)
3692
0
            return eErr;
3693
3694
0
        if (!oSRS2.IsGeographic())
3695
0
            return OGRERR_FAILURE;
3696
3697
0
        return CopyGeogCSFrom(&oSRS2);
3698
0
    }
3699
3700
    /* -------------------------------------------------------------------- */
3701
    /*      Check for simple names.                                         */
3702
    /* -------------------------------------------------------------------- */
3703
0
    const char *pszWKT = nullptr;
3704
3705
0
    if (EQUAL(pszName, "WGS84"))
3706
0
    {
3707
0
        pszWKT = SRS_WKT_WGS84_LAT_LONG;
3708
0
    }
3709
0
    else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3710
0
    {
3711
0
        pszWKT =
3712
0
            "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3713
0
            "ELLIPSOID[\"WGS "
3714
0
            "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3715
0
            "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3716
0
            "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3717
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3718
0
            "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3719
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3720
0
            "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3721
0
            "ID[\"OGC\",\"CRS84\"]]";
3722
0
    }
3723
0
    else if (EQUAL(pszName, "WGS72"))
3724
0
        pszWKT =
3725
0
            "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3726
0
            "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3727
0
            "AUTHORITY[\"EPSG\",\"6322\"]],"
3728
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3729
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3730
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3731
0
            "AUTHORITY[\"EPSG\",\"4322\"]]";
3732
3733
0
    else if (EQUAL(pszName, "NAD27"))
3734
0
        pszWKT =
3735
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3736
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3737
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3738
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3739
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3740
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3741
0
            "AUTHORITY[\"EPSG\",\"4267\"]]";
3742
3743
0
    else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3744
0
        pszWKT =
3745
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3746
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3747
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3748
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3749
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3750
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3751
3752
0
    else if (EQUAL(pszName, "NAD83"))
3753
0
        pszWKT =
3754
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3755
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3756
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3757
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3758
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3759
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3760
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3761
0
            "\"EPSG\",\"4269\"]]";
3762
3763
0
    else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3764
0
        pszWKT =
3765
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3766
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3767
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3768
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3769
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3770
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3771
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3772
3773
0
    else
3774
0
        return OGRERR_FAILURE;
3775
3776
    /* -------------------------------------------------------------------- */
3777
    /*      Import the WKT                                                  */
3778
    /* -------------------------------------------------------------------- */
3779
0
    OGRSpatialReference oSRS2;
3780
0
    const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3781
0
    if (eErr != OGRERR_NONE)
3782
0
        return eErr;
3783
3784
    /* -------------------------------------------------------------------- */
3785
    /*      Copy over.                                                      */
3786
    /* -------------------------------------------------------------------- */
3787
0
    return CopyGeogCSFrom(&oSRS2);
3788
0
}
3789
3790
/************************************************************************/
3791
/*                       OSRSetWellKnownGeogCS()                        */
3792
/************************************************************************/
3793
3794
/**
3795
 * \brief Set a GeogCS based on well known name.
3796
 *
3797
 * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3798
 */
3799
OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3800
3801
0
{
3802
0
    VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3803
3804
0
    return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3805
0
}
3806
3807
/************************************************************************/
3808
/*                           CopyGeogCSFrom()                           */
3809
/************************************************************************/
3810
3811
/**
3812
 * \brief Copy GEOGCS from another OGRSpatialReference.
3813
 *
3814
 * The GEOGCS information is copied into this OGRSpatialReference from another.
3815
 * If this object has a PROJCS root already, the GEOGCS is installed within
3816
 * it, otherwise it is installed as the root.
3817
 *
3818
 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3819
 *
3820
 * @return OGRERR_NONE on success or an error code.
3821
 */
3822
3823
OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3824
3825
0
{
3826
0
    TAKE_OPTIONAL_LOCK();
3827
3828
0
    d->bNormInfoSet = FALSE;
3829
0
    d->m_osAngularUnits.clear();
3830
0
    d->m_dfAngularUnitToRadian = 0.0;
3831
0
    d->m_osPrimeMeridianName.clear();
3832
0
    d->dfFromGreenwich = 0.0;
3833
3834
0
    d->refreshProjObj();
3835
0
    poSrcSRS->d->refreshProjObj();
3836
0
    if (!poSrcSRS->d->m_pj_crs)
3837
0
    {
3838
0
        return OGRERR_FAILURE;
3839
0
    }
3840
0
    auto geodCRS =
3841
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3842
0
    if (!geodCRS)
3843
0
    {
3844
0
        return OGRERR_FAILURE;
3845
0
    }
3846
3847
    /* -------------------------------------------------------------------- */
3848
    /*      Handle geocentric coordinate systems specially.  We just        */
3849
    /*      want to copy the DATUM.                                         */
3850
    /* -------------------------------------------------------------------- */
3851
0
    if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3852
0
    {
3853
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3854
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
3855
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3856
0
        if (datum == nullptr)
3857
0
        {
3858
0
            datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3859
0
        }
3860
0
#endif
3861
0
        if (datum == nullptr)
3862
0
        {
3863
0
            proj_destroy(geodCRS);
3864
0
            return OGRERR_FAILURE;
3865
0
        }
3866
3867
0
        const char *pszUnitName = nullptr;
3868
0
        double unitConvFactor = GetLinearUnits(&pszUnitName);
3869
3870
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
3871
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3872
0
            unitConvFactor);
3873
0
        proj_destroy(datum);
3874
3875
0
        d->setPjCRS(pj_crs);
3876
0
    }
3877
3878
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3879
0
    {
3880
0
        auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3881
0
                                                  d->m_pj_crs, geodCRS);
3882
0
        d->setPjCRS(pj_crs);
3883
0
    }
3884
3885
0
    else
3886
0
    {
3887
0
        d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3888
0
    }
3889
3890
    // Apply TOWGS84 of source CRS
3891
0
    if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3892
0
    {
3893
0
        auto target =
3894
0
            proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3895
0
        auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3896
0
                                              poSrcSRS->d->m_pj_crs);
3897
0
        d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3898
0
                                              target, co));
3899
0
        proj_destroy(target);
3900
0
        proj_destroy(co);
3901
0
    }
3902
3903
0
    proj_destroy(geodCRS);
3904
3905
0
    return OGRERR_NONE;
3906
0
}
3907
3908
/************************************************************************/
3909
/*                         OSRCopyGeogCSFrom()                          */
3910
/************************************************************************/
3911
3912
/**
3913
 * \brief Copy GEOGCS from another OGRSpatialReference.
3914
 *
3915
 * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3916
 */
3917
OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3918
                         const OGRSpatialReferenceH hSrcSRS)
3919
3920
0
{
3921
0
    VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3922
0
    VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3923
3924
0
    return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3925
0
}
3926
3927
/************************************************************************/
3928
/*                   SET_FROM_USER_INPUT_LIMITATIONS_get()              */
3929
/************************************************************************/
3930
3931
/** Limitations for OGRSpatialReference::SetFromUserInput().
3932
 *
3933
 * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3934
 */
3935
const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3936
    "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3937
3938
/**
3939
 * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3940
 */
3941
CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3942
0
{
3943
0
    return SET_FROM_USER_INPUT_LIMITATIONS;
3944
0
}
3945
3946
/************************************************************************/
3947
/*                      RemoveIDFromMemberOfEnsembles()                 */
3948
/************************************************************************/
3949
3950
// cppcheck-suppress constParameterReference
3951
static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3952
0
{
3953
    // Remove "id" from members of datum ensembles for compatibility with
3954
    // older PROJ versions
3955
    // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3956
    // and https://github.com/OSGeo/PROJ/pull/3221
3957
0
    if (obj.GetType() == CPLJSONObject::Type::Object)
3958
0
    {
3959
0
        for (auto &subObj : obj.GetChildren())
3960
0
        {
3961
0
            RemoveIDFromMemberOfEnsembles(subObj);
3962
0
        }
3963
0
    }
3964
0
    else if (obj.GetType() == CPLJSONObject::Type::Array &&
3965
0
             obj.GetName() == "members")
3966
0
    {
3967
0
        for (auto &subObj : obj.ToArray())
3968
0
        {
3969
0
            if (subObj.GetType() == CPLJSONObject::Type::Object)
3970
0
            {
3971
0
                subObj.Delete("id");
3972
0
            }
3973
0
        }
3974
0
    }
3975
0
}
3976
3977
/************************************************************************/
3978
/*                          SetFromUserInput()                          */
3979
/************************************************************************/
3980
3981
/**
3982
 * \brief Set spatial reference from various text formats.
3983
 *
3984
 * This method will examine the provided input, and try to deduce the
3985
 * format, and then use it to initialize the spatial reference system.  It
3986
 * may take the following forms:
3987
 *
3988
 * <ol>
3989
 * <li> Well Known Text definition - passed on to importFromWkt().
3990
 * <li> "EPSG:n" - number passed on to importFromEPSG().
3991
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3992
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3993
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3994
 * <li> PROJ.4 definitions - passed on to importFromProj4().
3995
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3996
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3997
 * WGS84 or WGS72.
3998
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3999
 * <li> PROJJSON (PROJ &gt;= 6.2)
4000
 * </ol>
4001
 *
4002
 * It is expected that this method will be extended in the future to support
4003
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4004
 * State Plane definitions.
4005
 *
4006
 * This method is intended to be flexible, but by its nature it is
4007
 * imprecise as it must guess information about the format intended.  When
4008
 * possible applications should call the specific method appropriate if the
4009
 * input is known to be in a particular format.
4010
 *
4011
 * This method does the same thing as the OSRSetFromUserInput() function.
4012
 *
4013
 * @param pszDefinition text definition to try to deduce SRS from.
4014
 *
4015
 * @return OGRERR_NONE on success, or an error code if the name isn't
4016
 * recognised, the definition is corrupt, or an EPSG value can't be
4017
 * successfully looked up.
4018
 */
4019
4020
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4021
0
{
4022
0
    return SetFromUserInput(pszDefinition, nullptr);
4023
0
}
4024
4025
/**
4026
 * \brief Set spatial reference from various text formats.
4027
 *
4028
 * This method will examine the provided input, and try to deduce the
4029
 * format, and then use it to initialize the spatial reference system.  It
4030
 * may take the following forms:
4031
 *
4032
 * <ol>
4033
 * <li> Well Known Text definition - passed on to importFromWkt().
4034
 * <li> "EPSG:n" - number passed on to importFromEPSG().
4035
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4036
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4037
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4038
 * <li> PROJ.4 definitions - passed on to importFromProj4().
4039
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
4040
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4041
 * WGS84 or WGS72.
4042
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4043
 * <li> PROJJSON (PROJ &gt;= 6.2)
4044
 * </ol>
4045
 *
4046
 * It is expected that this method will be extended in the future to support
4047
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4048
 * State Plane definitions.
4049
 *
4050
 * This method is intended to be flexible, but by its nature it is
4051
 * imprecise as it must guess information about the format intended.  When
4052
 * possible applications should call the specific method appropriate if the
4053
 * input is known to be in a particular format.
4054
 *
4055
 * This method does the same thing as the OSRSetFromUserInput() and
4056
 * OSRSetFromUserInputEx() functions.
4057
 *
4058
 * @param pszDefinition text definition to try to deduce SRS from.
4059
 *
4060
 * @param papszOptions NULL terminated list of options, or NULL.
4061
 * <ol>
4062
 * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4063
 *      Whether http:// or https:// access is allowed. Defaults to YES.
4064
 * <li> ALLOW_FILE_ACCESS=YES/NO.
4065
 *      Whether reading a file using the Virtual File System layer is allowed
4066
 *      (can also involve network access). Defaults to YES.
4067
 * </ol>
4068
 *
4069
 * @return OGRERR_NONE on success, or an error code if the name isn't
4070
 * recognised, the definition is corrupt, or an EPSG value can't be
4071
 * successfully looked up.
4072
 */
4073
4074
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4075
                                             CSLConstList papszOptions)
4076
0
{
4077
0
    TAKE_OPTIONAL_LOCK();
4078
4079
    // Skip leading white space
4080
0
    while (isspace(static_cast<unsigned char>(*pszDefinition)))
4081
0
        pszDefinition++;
4082
4083
0
    if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4084
0
    {
4085
0
        pszDefinition += 6;
4086
0
    }
4087
4088
    /* -------------------------------------------------------------------- */
4089
    /*      Is it a recognised syntax?                                      */
4090
    /* -------------------------------------------------------------------- */
4091
0
    const char *const wktKeywords[] = {
4092
        // WKT1
4093
0
        "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4094
        // WKT2"
4095
0
        "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4096
0
        "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4097
0
        "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4098
0
    for (const char *keyword : wktKeywords)
4099
0
    {
4100
0
        if (STARTS_WITH_CI(pszDefinition, keyword))
4101
0
        {
4102
0
            return importFromWkt(pszDefinition);
4103
0
        }
4104
0
    }
4105
4106
0
    const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4107
0
    if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4108
0
    {
4109
0
        OGRErr eStatus = OGRERR_NONE;
4110
4111
0
        if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4112
0
        {
4113
            // Use proj_create() as it allows things like EPSG:3157+4617
4114
            // that are not normally supported by the below code that
4115
            // builds manually a compound CRS
4116
0
            PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4117
0
            if (!pj)
4118
0
            {
4119
0
                return OGRERR_FAILURE;
4120
0
            }
4121
0
            Clear();
4122
0
            d->setPjCRS(pj);
4123
0
            return OGRERR_NONE;
4124
0
        }
4125
0
        else
4126
0
        {
4127
0
            eStatus =
4128
0
                importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4129
0
        }
4130
4131
0
        return eStatus;
4132
0
    }
4133
4134
0
    if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4135
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4136
0
        STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4137
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4138
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4139
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4140
0
        return importFromURN(pszDefinition);
4141
4142
0
    if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4143
0
        STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4144
0
        STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4145
0
        STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4146
0
        STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4147
0
        return importFromCRSURL(pszDefinition);
4148
4149
0
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4150
0
        return importFromWMSAUTO(pszDefinition);
4151
4152
    // WMS/WCS OGC codes like OGC:CRS84.
4153
0
    if (EQUAL(pszDefinition, "OGC:CRS84"))
4154
0
        return SetWellKnownGeogCS(pszDefinition + 4);
4155
4156
0
    if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4157
0
        return SetWellKnownGeogCS(pszDefinition);
4158
4159
0
    if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4160
0
    {
4161
0
        char *pszFile = CPLStrdup(pszDefinition + 5);
4162
0
        char *pszCode = strstr(pszFile, ",") + 1;
4163
4164
0
        pszCode[-1] = '\0';
4165
4166
0
        OGRErr err = importFromDict(pszFile, pszCode);
4167
0
        CPLFree(pszFile);
4168
4169
0
        return err;
4170
0
    }
4171
4172
0
    if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4173
0
        EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4174
0
    {
4175
0
        Clear();
4176
0
        return SetWellKnownGeogCS(pszDefinition);
4177
0
    }
4178
4179
    // PROJJSON
4180
0
    if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4181
0
        (strstr(pszDefinition, "GeodeticCRS") ||
4182
0
         strstr(pszDefinition, "GeographicCRS") ||
4183
0
         strstr(pszDefinition, "ProjectedCRS") ||
4184
0
         strstr(pszDefinition, "VerticalCRS") ||
4185
0
         strstr(pszDefinition, "BoundCRS") ||
4186
0
         strstr(pszDefinition, "CompoundCRS") ||
4187
0
         strstr(pszDefinition, "DerivedGeodeticCRS") ||
4188
0
         strstr(pszDefinition, "DerivedGeographicCRS") ||
4189
0
         strstr(pszDefinition, "DerivedProjectedCRS") ||
4190
0
         strstr(pszDefinition, "DerivedVerticalCRS") ||
4191
0
         strstr(pszDefinition, "EngineeringCRS") ||
4192
0
         strstr(pszDefinition, "DerivedEngineeringCRS") ||
4193
0
         strstr(pszDefinition, "ParametricCRS") ||
4194
0
         strstr(pszDefinition, "DerivedParametricCRS") ||
4195
0
         strstr(pszDefinition, "TemporalCRS") ||
4196
0
         strstr(pszDefinition, "DerivedTemporalCRS")))
4197
0
    {
4198
0
        PJ *pj;
4199
0
        if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4200
0
        {
4201
            // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4202
            // a unknown id.
4203
0
            CPLJSONDocument oCRSDoc;
4204
0
            if (!oCRSDoc.LoadMemory(pszDefinition))
4205
0
                return OGRERR_CORRUPT_DATA;
4206
0
            CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4207
0
            RemoveIDFromMemberOfEnsembles(oCRSRoot);
4208
0
            pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4209
0
        }
4210
0
        else
4211
0
        {
4212
0
            pj = proj_create(d->getPROJContext(), pszDefinition);
4213
0
        }
4214
0
        if (!pj)
4215
0
        {
4216
0
            return OGRERR_FAILURE;
4217
0
        }
4218
0
        Clear();
4219
0
        d->setPjCRS(pj);
4220
0
        return OGRERR_NONE;
4221
0
    }
4222
4223
0
    if (strstr(pszDefinition, "+proj") != nullptr ||
4224
0
        strstr(pszDefinition, "+init") != nullptr)
4225
0
        return importFromProj4(pszDefinition);
4226
4227
0
    if (STARTS_WITH_CI(pszDefinition, "http://") ||
4228
0
        STARTS_WITH_CI(pszDefinition, "https://"))
4229
0
    {
4230
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4231
0
                                             "ALLOW_NETWORK_ACCESS", "YES")))
4232
0
            return importFromUrl(pszDefinition);
4233
4234
0
        CPLError(CE_Failure, CPLE_AppDefined,
4235
0
                 "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4236
0
                 pszDefinition);
4237
0
        return OGRERR_FAILURE;
4238
0
    }
4239
4240
0
    if (EQUAL(pszDefinition, "osgb:BNG"))
4241
0
    {
4242
0
        return importFromEPSG(27700);
4243
0
    }
4244
4245
    // Used by German CityGML files
4246
0
    if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4247
0
    {
4248
        // "ETRS89 / UTM Zone 32N + DHHN92 height"
4249
0
        return SetFromUserInput("EPSG:25832+5783");
4250
0
    }
4251
0
    else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4252
0
    {
4253
        // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4254
0
        return SetFromUserInput("EPSG:25832+7837");
4255
0
    }
4256
4257
    // Used by  Japan's Fundamental Geospatial Data (FGD) GML
4258
0
    if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4259
0
        return importFromEPSG(4612);  // JGD2000 (slight difference in years)
4260
0
    else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4261
0
        return importFromEPSG(6668);  // JGD2011
4262
0
    else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4263
0
    {
4264
        // FIXME when EPSG attributes a CRS code
4265
0
        return importFromWkt(
4266
0
            "GEOGCRS[\"JGD2024\",\n"
4267
0
            "    DATUM[\"Japanese Geodetic Datum 2024\",\n"
4268
0
            "        ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4269
0
            "            LENGTHUNIT[\"metre\",1]]],\n"
4270
0
            "    PRIMEM[\"Greenwich\",0,\n"
4271
0
            "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4272
0
            "    CS[ellipsoidal,2],\n"
4273
0
            "        AXIS[\"geodetic latitude (Lat)\",north,\n"
4274
0
            "            ORDER[1],\n"
4275
0
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4276
0
            "        AXIS[\"geodetic longitude (Lon)\",east,\n"
4277
0
            "            ORDER[2],\n"
4278
0
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4279
0
            "    USAGE[\n"
4280
0
            "        SCOPE[\"Horizontal component of 3D system.\"],\n"
4281
0
            "        AREA[\"Japan - onshore and offshore.\"],\n"
4282
0
            "        BBOX[17.09,122.38,46.05,157.65]]]");
4283
0
    }
4284
4285
    // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4286
0
    const char *pszDot = strrchr(pszDefinition, ':');
4287
0
    if (pszDot)
4288
0
    {
4289
0
        CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4290
0
        auto authorities =
4291
0
            proj_get_authorities_from_database(d->getPROJContext());
4292
0
        if (authorities)
4293
0
        {
4294
0
            std::set<std::string> aosCandidateAuthorities;
4295
0
            for (auto iter = authorities; *iter; ++iter)
4296
0
            {
4297
0
                if (*iter == osPrefix)
4298
0
                {
4299
0
                    aosCandidateAuthorities.clear();
4300
0
                    aosCandidateAuthorities.insert(*iter);
4301
0
                    break;
4302
0
                }
4303
                // Deal with "IAU_2015" as authority in the list and input
4304
                // "IAU:code"
4305
0
                else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4306
0
                             0 &&
4307
0
                         (*iter)[osPrefix.size()] == '_')
4308
0
                {
4309
0
                    aosCandidateAuthorities.insert(*iter);
4310
0
                }
4311
                // Deal with "IAU_2015" as authority in the list and input
4312
                // "IAU:2015:code"
4313
0
                else if (osPrefix.find(':') != std::string::npos &&
4314
0
                         osPrefix.size() == strlen(*iter) &&
4315
0
                         CPLString(osPrefix).replaceAll(':', '_') == *iter)
4316
0
                {
4317
0
                    aosCandidateAuthorities.clear();
4318
0
                    aosCandidateAuthorities.insert(*iter);
4319
0
                    break;
4320
0
                }
4321
0
            }
4322
4323
0
            proj_string_list_destroy(authorities);
4324
4325
0
            if (!aosCandidateAuthorities.empty())
4326
0
            {
4327
0
                auto obj = proj_create_from_database(
4328
0
                    d->getPROJContext(),
4329
0
                    aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4330
0
                    PJ_CATEGORY_CRS, false, nullptr);
4331
0
                if (!obj)
4332
0
                {
4333
0
                    return OGRERR_FAILURE;
4334
0
                }
4335
0
                Clear();
4336
0
                d->setPjCRS(obj);
4337
0
                return OGRERR_NONE;
4338
0
            }
4339
0
        }
4340
0
    }
4341
4342
    /* -------------------------------------------------------------------- */
4343
    /*      Try to open it as a file.                                       */
4344
    /* -------------------------------------------------------------------- */
4345
0
    if (!CPLTestBool(
4346
0
            CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4347
0
    {
4348
0
        VSIStatBufL sStat;
4349
0
        if (STARTS_WITH(pszDefinition, "/vsi") ||
4350
0
            VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4351
0
        {
4352
0
            CPLError(CE_Failure, CPLE_AppDefined,
4353
0
                     "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4354
0
                     pszDefinition);
4355
0
            return OGRERR_FAILURE;
4356
0
        }
4357
        // We used to silently return an error without a CE_Failure message
4358
        // Cf https://github.com/Toblerity/Fiona/issues/1063
4359
0
        return OGRERR_CORRUPT_DATA;
4360
0
    }
4361
4362
0
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4363
0
    VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4364
0
    if (fp == nullptr)
4365
0
        return OGRERR_CORRUPT_DATA;
4366
4367
0
    const size_t nBufMax = 100000;
4368
0
    char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4369
0
    const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4370
0
    VSIFCloseL(fp);
4371
4372
0
    if (nBytes == nBufMax - 1)
4373
0
    {
4374
0
        CPLDebug("OGR",
4375
0
                 "OGRSpatialReference::SetFromUserInput(%s), opened file "
4376
0
                 "but it is to large for our generous buffer.  Is it really "
4377
0
                 "just a WKT definition?",
4378
0
                 pszDefinition);
4379
0
        CPLFree(pszBuffer);
4380
0
        return OGRERR_FAILURE;
4381
0
    }
4382
4383
0
    pszBuffer[nBytes] = '\0';
4384
4385
0
    char *pszBufPtr = pszBuffer;
4386
0
    while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4387
0
        pszBufPtr++;
4388
4389
0
    OGRErr err = OGRERR_NONE;
4390
0
    if (pszBufPtr[0] == '<')
4391
0
        err = importFromXML(pszBufPtr);
4392
0
    else if ((strstr(pszBuffer, "+proj") != nullptr ||
4393
0
              strstr(pszBuffer, "+init") != nullptr) &&
4394
0
             (strstr(pszBuffer, "EXTENSION") == nullptr &&
4395
0
              strstr(pszBuffer, "extension") == nullptr))
4396
0
        err = importFromProj4(pszBufPtr);
4397
0
    else
4398
0
    {
4399
0
        if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4400
0
        {
4401
0
            pszBufPtr += 6;
4402
0
        }
4403
4404
        // coverity[tainted_data]
4405
0
        err = importFromWkt(pszBufPtr);
4406
0
    }
4407
4408
0
    CPLFree(pszBuffer);
4409
4410
0
    return err;
4411
0
}
4412
4413
/************************************************************************/
4414
/*                        OSRSetFromUserInput()                         */
4415
/************************************************************************/
4416
4417
/**
4418
 * \brief Set spatial reference from various text formats.
4419
 *
4420
 * This function is the same as OGRSpatialReference::SetFromUserInput()
4421
 *
4422
 * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4423
 */
4424
OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4425
                                       const char *pszDef)
4426
4427
0
{
4428
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4429
4430
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef);
4431
0
}
4432
4433
/************************************************************************/
4434
/*                       OSRSetFromUserInputEx()                        */
4435
/************************************************************************/
4436
4437
/**
4438
 * \brief Set spatial reference from various text formats.
4439
 *
4440
 * This function is the same as OGRSpatialReference::SetFromUserInput().
4441
 *
4442
 * @since GDAL 3.9
4443
 */
4444
OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4445
                             CSLConstList papszOptions)
4446
4447
0
{
4448
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4449
4450
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4451
0
}
4452
4453
/************************************************************************/
4454
/*                          ImportFromUrl()                             */
4455
/************************************************************************/
4456
4457
/**
4458
 * \brief Set spatial reference from a URL.
4459
 *
4460
 * This method will download the spatial reference at a given URL and
4461
 * feed it into SetFromUserInput for you.
4462
 *
4463
 * This method does the same thing as the OSRImportFromUrl() function.
4464
 *
4465
 * @param pszUrl text definition to try to deduce SRS from.
4466
 *
4467
 * @return OGRERR_NONE on success, or an error code with the curl
4468
 * error message if it is unable to download data.
4469
 */
4470
4471
OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4472
4473
0
{
4474
0
    TAKE_OPTIONAL_LOCK();
4475
4476
0
    if (!STARTS_WITH_CI(pszUrl, "http://") &&
4477
0
        !STARTS_WITH_CI(pszUrl, "https://"))
4478
0
    {
4479
0
        CPLError(CE_Failure, CPLE_AppDefined,
4480
0
                 "The given string is not recognized as a URL"
4481
0
                 "starting with 'http://' -- %s",
4482
0
                 pszUrl);
4483
0
        return OGRERR_FAILURE;
4484
0
    }
4485
4486
    /* -------------------------------------------------------------------- */
4487
    /*      Fetch the result.                                               */
4488
    /* -------------------------------------------------------------------- */
4489
0
    CPLErrorReset();
4490
4491
0
    std::string osUrl(pszUrl);
4492
    // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4493
    // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4494
    // to query WKT. To allow a static server to be used, rather append a
4495
    // "ogcwkt/" suffix.
4496
0
    for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4497
0
                                  "http://spatialreference.org/ref/"})
4498
0
    {
4499
0
        if (STARTS_WITH(pszUrl, pszPrefix))
4500
0
        {
4501
0
            const CPLStringList aosTokens(
4502
0
                CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4503
0
            if (aosTokens.size() == 2)
4504
0
            {
4505
0
                osUrl = "https://spatialreference.org/ref/";
4506
0
                osUrl += aosTokens[0];  // authority
4507
0
                osUrl += '/';
4508
0
                osUrl += aosTokens[1];  // code
4509
0
                osUrl += "/ogcwkt/";
4510
0
            }
4511
0
            break;
4512
0
        }
4513
0
    }
4514
4515
0
    const char *pszTimeout = "TIMEOUT=10";
4516
0
    char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4517
4518
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4519
4520
    /* -------------------------------------------------------------------- */
4521
    /*      Try to handle errors.                                           */
4522
    /* -------------------------------------------------------------------- */
4523
4524
0
    if (psResult == nullptr)
4525
0
        return OGRERR_FAILURE;
4526
0
    if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4527
0
        psResult->pabyData == nullptr)
4528
0
    {
4529
0
        if (CPLGetLastErrorNo() == 0)
4530
0
        {
4531
0
            CPLError(CE_Failure, CPLE_AppDefined,
4532
0
                     "No data was returned from the given URL");
4533
0
        }
4534
0
        CPLHTTPDestroyResult(psResult);
4535
0
        return OGRERR_FAILURE;
4536
0
    }
4537
4538
0
    if (psResult->nStatus != 0)
4539
0
    {
4540
0
        CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4541
0
                 psResult->nStatus, psResult->pszErrBuf);
4542
0
        CPLHTTPDestroyResult(psResult);
4543
0
        return OGRERR_FAILURE;
4544
0
    }
4545
4546
0
    const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4547
0
    if (STARTS_WITH_CI(pszData, "http://") ||
4548
0
        STARTS_WITH_CI(pszData, "https://"))
4549
0
    {
4550
0
        CPLError(CE_Failure, CPLE_AppDefined,
4551
0
                 "The data that was downloaded also starts with 'http://' "
4552
0
                 "and cannot be passed into SetFromUserInput.  Is this "
4553
0
                 "really a spatial reference definition? ");
4554
0
        CPLHTTPDestroyResult(psResult);
4555
0
        return OGRERR_FAILURE;
4556
0
    }
4557
0
    if (OGRERR_NONE != SetFromUserInput(pszData))
4558
0
    {
4559
0
        CPLHTTPDestroyResult(psResult);
4560
0
        return OGRERR_FAILURE;
4561
0
    }
4562
4563
0
    CPLHTTPDestroyResult(psResult);
4564
0
    return OGRERR_NONE;
4565
0
}
4566
4567
/************************************************************************/
4568
/*                        OSRimportFromUrl()                            */
4569
/************************************************************************/
4570
4571
/**
4572
 * \brief Set spatial reference from a URL.
4573
 *
4574
 * This function is the same as OGRSpatialReference::importFromUrl()
4575
 */
4576
OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4577
4578
0
{
4579
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4580
4581
0
    return ToPointer(hSRS)->importFromUrl(pszUrl);
4582
0
}
4583
4584
/************************************************************************/
4585
/*                         importFromURNPart()                          */
4586
/************************************************************************/
4587
OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4588
                                              const char *pszCode,
4589
                                              const char *pszURN)
4590
0
{
4591
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4592
0
    (void)this;
4593
0
    (void)pszAuthority;
4594
0
    (void)pszCode;
4595
0
    (void)pszURN;
4596
0
    return OGRERR_FAILURE;
4597
#else
4598
    /* -------------------------------------------------------------------- */
4599
    /*      Is this an EPSG code? Note that we import it with EPSG          */
4600
    /*      preferred axis ordering for geographic coordinate systems.      */
4601
    /* -------------------------------------------------------------------- */
4602
    if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4603
        return importFromEPSGA(atoi(pszCode));
4604
4605
    /* -------------------------------------------------------------------- */
4606
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4607
    /* -------------------------------------------------------------------- */
4608
    if (STARTS_WITH_CI(pszAuthority, "IAU"))
4609
        return importFromDict("IAU2000.wkt", pszCode);
4610
4611
    /* -------------------------------------------------------------------- */
4612
    /*      Is this an OGC code?                                            */
4613
    /* -------------------------------------------------------------------- */
4614
    if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4615
    {
4616
        CPLError(CE_Failure, CPLE_AppDefined,
4617
                 "URN %s has unrecognized authority.", pszURN);
4618
        return OGRERR_FAILURE;
4619
    }
4620
4621
    if (STARTS_WITH_CI(pszCode, "CRS84"))
4622
        return SetWellKnownGeogCS(pszCode);
4623
    else if (STARTS_WITH_CI(pszCode, "CRS83"))
4624
        return SetWellKnownGeogCS(pszCode);
4625
    else if (STARTS_WITH_CI(pszCode, "CRS27"))
4626
        return SetWellKnownGeogCS(pszCode);
4627
    else if (STARTS_WITH_CI(pszCode, "84"))  // urn:ogc:def:crs:OGC:2:84
4628
        return SetWellKnownGeogCS("CRS84");
4629
4630
    /* -------------------------------------------------------------------- */
4631
    /*      Handle auto codes.  We need to convert from format              */
4632
    /*      AUTO42001:99:8888 to format AUTO:42001,99,8888.                 */
4633
    /* -------------------------------------------------------------------- */
4634
    else if (STARTS_WITH_CI(pszCode, "AUTO"))
4635
    {
4636
        char szWMSAuto[100] = {'\0'};
4637
4638
        if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4639
            return OGRERR_FAILURE;
4640
4641
        snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4642
        for (int i = 5; szWMSAuto[i] != '\0'; i++)
4643
        {
4644
            if (szWMSAuto[i] == ':')
4645
                szWMSAuto[i] = ',';
4646
        }
4647
4648
        return importFromWMSAUTO(szWMSAuto);
4649
    }
4650
4651
    /* -------------------------------------------------------------------- */
4652
    /*      Not a recognise OGC item.                                       */
4653
    /* -------------------------------------------------------------------- */
4654
    CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4655
             pszURN);
4656
4657
    return OGRERR_FAILURE;
4658
#endif
4659
0
}
4660
4661
/************************************************************************/
4662
/*                           importFromURN()                            */
4663
/*                                                                      */
4664
/*      See OGC recommendation paper 06-023r1 or later for details.     */
4665
/************************************************************************/
4666
4667
/**
4668
 * \brief Initialize from OGC URN.
4669
 *
4670
 * Initializes this spatial reference from a coordinate system defined
4671
 * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4672
 * paper 06-023r1.  Currently EPSG and OGC authority values are supported,
4673
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4674
 *
4675
 * This method is also support through SetFromUserInput() which can
4676
 * normally be used for URNs.
4677
 *
4678
 * @param pszURN the urn string.
4679
 *
4680
 * @return OGRERR_NONE on success or an error code.
4681
 */
4682
4683
OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4684
4685
0
{
4686
0
    constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4687
0
    if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4688
0
        CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4689
0
            CPL_VALUE_INTEGER)
4690
0
    {
4691
0
        return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4692
0
    }
4693
4694
0
    TAKE_OPTIONAL_LOCK();
4695
4696
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4697
4698
    // PROJ 8.2.0 has support for IAU codes now.
4699
#if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4700
    /* -------------------------------------------------------------------- */
4701
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4702
    /* -------------------------------------------------------------------- */
4703
    const char *pszIAU = strstr(pszURN, "IAU");
4704
    if (pszIAU)
4705
    {
4706
        const char *pszCode = strchr(pszIAU, ':');
4707
        if (pszCode)
4708
        {
4709
            ++pszCode;
4710
            if (*pszCode == ':')
4711
                ++pszCode;
4712
            return importFromDict("IAU2000.wkt", pszCode);
4713
        }
4714
    }
4715
#endif
4716
4717
0
    if (strlen(pszURN) >= 1000)
4718
0
    {
4719
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4720
0
        return OGRERR_CORRUPT_DATA;
4721
0
    }
4722
0
    auto obj = proj_create(d->getPROJContext(), pszURN);
4723
0
    if (!obj)
4724
0
    {
4725
0
        return OGRERR_FAILURE;
4726
0
    }
4727
0
    Clear();
4728
0
    d->setPjCRS(obj);
4729
0
    return OGRERR_NONE;
4730
#else
4731
    const char *pszCur = nullptr;
4732
4733
    if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4734
        pszCur = pszURN + 16;
4735
    else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4736
        pszCur = pszURN + 20;
4737
    else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4738
        pszCur = pszURN + 18;
4739
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4740
        pszCur = pszURN + 16;
4741
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4742
        pszCur = pszURN + 20;
4743
    else
4744
    {
4745
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4746
                 pszURN);
4747
        return OGRERR_FAILURE;
4748
    }
4749
4750
    /* -------------------------------------------------------------------- */
4751
    /*      Clear any existing definition.                                  */
4752
    /* -------------------------------------------------------------------- */
4753
    Clear();
4754
4755
    /* -------------------------------------------------------------------- */
4756
    /*      Find code (ignoring version) out of string like:                */
4757
    /*                                                                      */
4758
    /*      authority:[version]:code                                        */
4759
    /* -------------------------------------------------------------------- */
4760
    const char *pszAuthority = pszCur;
4761
4762
    // skip authority
4763
    while (*pszCur != ':' && *pszCur)
4764
        pszCur++;
4765
    if (*pszCur == ':')
4766
        pszCur++;
4767
4768
    // skip version
4769
    const char *pszBeforeVersion = pszCur;
4770
    while (*pszCur != ':' && *pszCur)
4771
        pszCur++;
4772
    if (*pszCur == ':')
4773
        pszCur++;
4774
    else
4775
        // We come here in the case, the content to parse is authority:code
4776
        // (instead of authority::code) which is probably illegal according to
4777
        // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4778
        // for example in what is returned by GeoServer.
4779
        pszCur = pszBeforeVersion;
4780
4781
    const char *pszCode = pszCur;
4782
4783
    const char *pszComma = strchr(pszCur, ',');
4784
    if (pszComma == nullptr)
4785
        return importFromURNPart(pszAuthority, pszCode, pszURN);
4786
4787
    // There's a second part with the vertical SRS.
4788
    pszCur = pszComma + 1;
4789
    if (!STARTS_WITH(pszCur, "crs:"))
4790
    {
4791
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4792
                 pszURN);
4793
        return OGRERR_FAILURE;
4794
    }
4795
4796
    pszCur += 4;
4797
4798
    char *pszFirstCode = CPLStrdup(pszCode);
4799
    pszFirstCode[pszComma - pszCode] = '\0';
4800
    OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4801
    CPLFree(pszFirstCode);
4802
4803
    // Do we want to turn this into a compound definition
4804
    // with a vertical datum?
4805
    if (eStatus != OGRERR_NONE)
4806
        return eStatus;
4807
4808
    /* -------------------------------------------------------------------- */
4809
    /*      Find code (ignoring version) out of string like:                */
4810
    /*                                                                      */
4811
    /*      authority:[version]:code                                        */
4812
    /* -------------------------------------------------------------------- */
4813
    pszAuthority = pszCur;
4814
4815
    // skip authority
4816
    while (*pszCur != ':' && *pszCur)
4817
        pszCur++;
4818
    if (*pszCur == ':')
4819
        pszCur++;
4820
4821
    // skip version
4822
    pszBeforeVersion = pszCur;
4823
    while (*pszCur != ':' && *pszCur)
4824
        pszCur++;
4825
    if (*pszCur == ':')
4826
        pszCur++;
4827
    else
4828
        pszCur = pszBeforeVersion;
4829
4830
    pszCode = pszCur;
4831
4832
    OGRSpatialReference oVertSRS;
4833
    eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4834
    if (eStatus == OGRERR_NONE)
4835
    {
4836
        OGRSpatialReference oHorizSRS(*this);
4837
4838
        Clear();
4839
4840
        oHorizSRS.d->refreshProjObj();
4841
        oVertSRS.d->refreshProjObj();
4842
        if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4843
            return OGRERR_FAILURE;
4844
4845
        const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4846
        const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4847
4848
        CPLString osName = pszHorizName ? pszHorizName : "";
4849
        osName += " + ";
4850
        osName += pszVertName ? pszVertName : "";
4851
4852
        SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4853
    }
4854
4855
    return eStatus;
4856
#endif
4857
0
}
4858
4859
/************************************************************************/
4860
/*                           importFromCRSURL()                         */
4861
/*                                                                      */
4862
/*      See OGC Best Practice document 11-135 for details.              */
4863
/************************************************************************/
4864
4865
/**
4866
 * \brief Initialize from OGC URL.
4867
 *
4868
 * Initializes this spatial reference from a coordinate system defined
4869
 * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4870
 * paper 11-135.  Currently EPSG and OGC authority values are supported,
4871
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4872
 *
4873
 * This method is also supported through SetFromUserInput() which can
4874
 * normally be used for URLs.
4875
 *
4876
 * @param pszURL the URL string.
4877
 *
4878
 * @return OGRERR_NONE on success or an error code.
4879
 */
4880
4881
OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4882
4883
0
{
4884
0
    TAKE_OPTIONAL_LOCK();
4885
4886
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4887
0
    if (strlen(pszURL) >= 10000)
4888
0
    {
4889
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4890
0
        return OGRERR_CORRUPT_DATA;
4891
0
    }
4892
4893
0
    PJ *obj;
4894
#if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4895
    if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4896
    {
4897
        obj = proj_create(
4898
            d->getPROJContext(),
4899
            CPLSPrintf("IAU:%s",
4900
                       pszURL +
4901
                           strlen("http://www.opengis.net/def/crs/IAU/0/")));
4902
    }
4903
    else
4904
#endif
4905
0
    {
4906
0
        obj = proj_create(d->getPROJContext(), pszURL);
4907
0
    }
4908
0
    if (!obj)
4909
0
    {
4910
0
        return OGRERR_FAILURE;
4911
0
    }
4912
0
    Clear();
4913
0
    d->setPjCRS(obj);
4914
0
    return OGRERR_NONE;
4915
#else
4916
    const char *pszCur = nullptr;
4917
4918
    if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4919
        pszCur = pszURL + 26;
4920
    else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4921
        pszCur = pszURL + 27;
4922
    else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4923
        pszCur = pszURL + 30;
4924
    else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4925
        pszCur = pszURL + 31;
4926
    else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4927
        pszCur = pszURL + 23;
4928
    else
4929
    {
4930
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4931
                 pszURL);
4932
        return OGRERR_FAILURE;
4933
    }
4934
4935
    if (*pszCur == '\0')
4936
    {
4937
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4938
        return OGRERR_FAILURE;
4939
    }
4940
4941
    /* -------------------------------------------------------------------- */
4942
    /*      Clear any existing definition.                                  */
4943
    /* -------------------------------------------------------------------- */
4944
    Clear();
4945
4946
    if (STARTS_WITH_CI(pszCur, "-compound?1="))
4947
    {
4948
        /* --------------------------------------------------------------------
4949
         */
4950
        /*      It's a compound CRS, of the form: */
4951
        /*                                                                      */
4952
        /*      http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4953
        /* --------------------------------------------------------------------
4954
         */
4955
        pszCur += 12;
4956
4957
        // Extract each component CRS URL.
4958
        int iComponentUrl = 2;
4959
4960
        CPLString osName = "";
4961
        Clear();
4962
4963
        while (iComponentUrl != -1)
4964
        {
4965
            char searchStr[15] = {};
4966
            snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4967
4968
            const char *pszUrlEnd = strstr(pszCur, searchStr);
4969
4970
            // Figure out the next component URL.
4971
            char *pszComponentUrl = nullptr;
4972
4973
            if (pszUrlEnd)
4974
            {
4975
                size_t nLen = pszUrlEnd - pszCur;
4976
                pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4977
                strncpy(pszComponentUrl, pszCur, nLen);
4978
                pszComponentUrl[nLen] = '\0';
4979
4980
                ++iComponentUrl;
4981
                pszCur += nLen + strlen(searchStr);
4982
            }
4983
            else
4984
            {
4985
                if (iComponentUrl == 2)
4986
                {
4987
                    CPLError(CE_Failure, CPLE_AppDefined,
4988
                             "Compound CRS URLs must have at least two "
4989
                             "component CRSs.");
4990
                    return OGRERR_FAILURE;
4991
                }
4992
                else
4993
                {
4994
                    pszComponentUrl = CPLStrdup(pszCur);
4995
                    // no more components
4996
                    iComponentUrl = -1;
4997
                }
4998
            }
4999
5000
            OGRSpatialReference oComponentSRS;
5001
            OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5002
5003
            CPLFree(pszComponentUrl);
5004
            pszComponentUrl = nullptr;
5005
5006
            if (eStatus == OGRERR_NONE)
5007
            {
5008
                if (osName.length() != 0)
5009
                {
5010
                    osName += " + ";
5011
                }
5012
                osName += oComponentSRS.GetRoot()->GetValue();
5013
                SetNode("COMPD_CS", osName);
5014
                GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5015
            }
5016
            else
5017
                return eStatus;
5018
        }
5019
5020
        return OGRERR_NONE;
5021
    }
5022
5023
    /* -------------------------------------------------------------------- */
5024
    /*      It's a normal CRS URL, of the form:                             */
5025
    /*                                                                      */
5026
    /*      http://opengis.net/def/crs/AUTHORITY/VERSION/CODE               */
5027
    /* -------------------------------------------------------------------- */
5028
    ++pszCur;
5029
    const char *pszAuthority = pszCur;
5030
5031
    // skip authority
5032
    while (*pszCur != '/' && *pszCur)
5033
        pszCur++;
5034
    if (*pszCur == '/')
5035
        pszCur++;
5036
5037
    // skip version
5038
    while (*pszCur != '/' && *pszCur)
5039
        pszCur++;
5040
    if (*pszCur == '/')
5041
        pszCur++;
5042
5043
    const char *pszCode = pszCur;
5044
5045
    return importFromURNPart(pszAuthority, pszCode, pszURL);
5046
#endif
5047
0
}
5048
5049
/************************************************************************/
5050
/*                         importFromWMSAUTO()                          */
5051
/************************************************************************/
5052
5053
/**
5054
 * \brief Initialize from WMSAUTO string.
5055
 *
5056
 * Note that the WMS 1.3 specification does not include the
5057
 * units code, while apparently earlier specs do.  We try to
5058
 * guess around this.
5059
 *
5060
 * @param pszDefinition the WMSAUTO string
5061
 *
5062
 * @return OGRERR_NONE on success or an error code.
5063
 */
5064
OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5065
5066
0
{
5067
0
    TAKE_OPTIONAL_LOCK();
5068
5069
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
5070
0
    if (strlen(pszDefinition) >= 10000)
5071
0
    {
5072
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5073
0
        return OGRERR_CORRUPT_DATA;
5074
0
    }
5075
5076
0
    auto obj = proj_create(d->getPROJContext(), pszDefinition);
5077
0
    if (!obj)
5078
0
    {
5079
0
        return OGRERR_FAILURE;
5080
0
    }
5081
0
    Clear();
5082
0
    d->setPjCRS(obj);
5083
0
    return OGRERR_NONE;
5084
#else
5085
    int nProjId, nUnitsId;
5086
    double dfRefLong, dfRefLat = 0.0;
5087
5088
    /* -------------------------------------------------------------------- */
5089
    /*      Tokenize                                                        */
5090
    /* -------------------------------------------------------------------- */
5091
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5092
        pszDefinition += 5;
5093
5094
    char **papszTokens =
5095
        CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5096
5097
    if (CSLCount(papszTokens) == 4)
5098
    {
5099
        nProjId = atoi(papszTokens[0]);
5100
        nUnitsId = atoi(papszTokens[1]);
5101
        dfRefLong = CPLAtof(papszTokens[2]);
5102
        dfRefLat = CPLAtof(papszTokens[3]);
5103
    }
5104
    else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5105
    {
5106
        nProjId = atoi(papszTokens[0]);
5107
        nUnitsId = atoi(papszTokens[1]);
5108
        dfRefLong = CPLAtof(papszTokens[2]);
5109
        dfRefLat = 0.0;
5110
    }
5111
    else if (CSLCount(papszTokens) == 3)
5112
    {
5113
        nProjId = atoi(papszTokens[0]);
5114
        nUnitsId = 9001;
5115
        dfRefLong = CPLAtof(papszTokens[1]);
5116
        dfRefLat = CPLAtof(papszTokens[2]);
5117
    }
5118
    else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5119
    {
5120
        nProjId = atoi(papszTokens[0]);
5121
        nUnitsId = 9001;
5122
        dfRefLong = CPLAtof(papszTokens[1]);
5123
    }
5124
    else
5125
    {
5126
        CSLDestroy(papszTokens);
5127
        CPLError(CE_Failure, CPLE_AppDefined,
5128
                 "AUTO projection has wrong number of arguments, expected\n"
5129
                 "AUTO:proj_id,units_id,ref_long,ref_lat or"
5130
                 "AUTO:proj_id,ref_long,ref_lat");
5131
        return OGRERR_FAILURE;
5132
    }
5133
5134
    CSLDestroy(papszTokens);
5135
    papszTokens = nullptr;
5136
5137
    /* -------------------------------------------------------------------- */
5138
    /*      Build coordsys.                                                 */
5139
    /* -------------------------------------------------------------------- */
5140
    Clear();
5141
5142
    /* -------------------------------------------------------------------- */
5143
    /*      Set WGS84.                                                      */
5144
    /* -------------------------------------------------------------------- */
5145
    SetWellKnownGeogCS("WGS84");
5146
5147
    switch (nProjId)
5148
    {
5149
        case 42001:  // Auto UTM
5150
            SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5151
                   dfRefLat >= 0.0);
5152
            break;
5153
5154
        case 42002:  // Auto TM (strangely very UTM-like).
5155
            SetTM(0, dfRefLong, 0.9996, 500000.0,
5156
                  (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5157
            break;
5158
5159
        case 42003:  // Auto Orthographic.
5160
            SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5161
            break;
5162
5163
        case 42004:  // Auto Equirectangular
5164
            SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5165
            break;
5166
5167
        case 42005:
5168
            SetMollweide(dfRefLong, 0.0, 0.0);
5169
            break;
5170
5171
        default:
5172
            CPLError(CE_Failure, CPLE_AppDefined,
5173
                     "Unsupported projection id in importFromWMSAUTO(): %d",
5174
                     nProjId);
5175
            return OGRERR_FAILURE;
5176
    }
5177
5178
    /* -------------------------------------------------------------------- */
5179
    /*      Set units.                                                      */
5180
    /* -------------------------------------------------------------------- */
5181
5182
    switch (nUnitsId)
5183
    {
5184
        case 9001:
5185
            SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5186
            break;
5187
5188
        case 9002:
5189
            SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5190
            break;
5191
5192
        case 9003:
5193
            SetTargetLinearUnits(nullptr, "US survey foot",
5194
                                 CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5195
            break;
5196
5197
        default:
5198
            CPLError(CE_Failure, CPLE_AppDefined,
5199
                     "Unsupported units code (%d).", nUnitsId);
5200
            return OGRERR_FAILURE;
5201
            break;
5202
    }
5203
5204
    return OGRERR_NONE;
5205
#endif
5206
0
}
5207
5208
/************************************************************************/
5209
/*                            GetSemiMajor()                            */
5210
/************************************************************************/
5211
5212
/**
5213
 * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5214
 *
5215
 * This method does the same thing as the C function OSRGetSemiMajor().
5216
 *
5217
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5218
 * can be found.
5219
 *
5220
 * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5221
 */
5222
5223
double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5224
5225
0
{
5226
0
    TAKE_OPTIONAL_LOCK();
5227
5228
0
    if (pnErr != nullptr)
5229
0
        *pnErr = OGRERR_FAILURE;
5230
5231
0
    d->refreshProjObj();
5232
0
    if (!d->m_pj_crs)
5233
0
        return SRS_WGS84_SEMIMAJOR;
5234
5235
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5236
0
    if (!ellps)
5237
0
        return SRS_WGS84_SEMIMAJOR;
5238
5239
0
    double dfSemiMajor = 0.0;
5240
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5241
0
                                  nullptr, nullptr, nullptr);
5242
0
    proj_destroy(ellps);
5243
5244
0
    if (dfSemiMajor > 0)
5245
0
    {
5246
0
        if (pnErr != nullptr)
5247
0
            *pnErr = OGRERR_NONE;
5248
0
        return dfSemiMajor;
5249
0
    }
5250
5251
0
    return SRS_WGS84_SEMIMAJOR;
5252
0
}
5253
5254
/************************************************************************/
5255
/*                          OSRGetSemiMajor()                           */
5256
/************************************************************************/
5257
5258
/**
5259
 * \brief Get spheroid semi major axis.
5260
 *
5261
 * This function is the same as OGRSpatialReference::GetSemiMajor()
5262
 */
5263
double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5264
5265
0
{
5266
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5267
5268
0
    return ToPointer(hSRS)->GetSemiMajor(pnErr);
5269
0
}
5270
5271
/************************************************************************/
5272
/*                          GetInvFlattening()                          */
5273
/************************************************************************/
5274
5275
/**
5276
 * \brief Get spheroid inverse flattening.
5277
 *
5278
 * This method does the same thing as the C function OSRGetInvFlattening().
5279
 *
5280
 * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5281
 * can be found.
5282
 *
5283
 * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5284
 */
5285
5286
double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5287
5288
0
{
5289
0
    TAKE_OPTIONAL_LOCK();
5290
5291
0
    if (pnErr != nullptr)
5292
0
        *pnErr = OGRERR_FAILURE;
5293
5294
0
    d->refreshProjObj();
5295
0
    if (!d->m_pj_crs)
5296
0
        return SRS_WGS84_INVFLATTENING;
5297
5298
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5299
0
    if (!ellps)
5300
0
        return SRS_WGS84_INVFLATTENING;
5301
5302
0
    double dfInvFlattening = -1.0;
5303
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5304
0
                                  nullptr, &dfInvFlattening);
5305
0
    proj_destroy(ellps);
5306
5307
0
    if (dfInvFlattening >= 0.0)
5308
0
    {
5309
0
        if (pnErr != nullptr)
5310
0
            *pnErr = OGRERR_NONE;
5311
0
        return dfInvFlattening;
5312
0
    }
5313
5314
0
    return SRS_WGS84_INVFLATTENING;
5315
0
}
5316
5317
/************************************************************************/
5318
/*                        OSRGetInvFlattening()                         */
5319
/************************************************************************/
5320
5321
/**
5322
 * \brief Get spheroid inverse flattening.
5323
 *
5324
 * This function is the same as OGRSpatialReference::GetInvFlattening()
5325
 */
5326
double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5327
5328
0
{
5329
0
    VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5330
5331
0
    return ToPointer(hSRS)->GetInvFlattening(pnErr);
5332
0
}
5333
5334
/************************************************************************/
5335
/*                           GetEccentricity()                          */
5336
/************************************************************************/
5337
5338
/**
5339
 * \brief Get spheroid eccentricity
5340
 *
5341
 * @return eccentricity (or -1 in case of error)
5342
 * @since GDAL 2.3
5343
 */
5344
5345
double OGRSpatialReference::GetEccentricity() const
5346
5347
0
{
5348
0
    OGRErr eErr = OGRERR_NONE;
5349
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5350
0
    if (eErr != OGRERR_NONE)
5351
0
    {
5352
0
        return -1.0;
5353
0
    }
5354
0
    if (dfInvFlattening == 0.0)
5355
0
        return 0.0;
5356
0
    if (dfInvFlattening < 0.5)
5357
0
        return -1.0;
5358
0
    return sqrt(2.0 / dfInvFlattening -
5359
0
                1.0 / (dfInvFlattening * dfInvFlattening));
5360
0
}
5361
5362
/************************************************************************/
5363
/*                      GetSquaredEccentricity()                        */
5364
/************************************************************************/
5365
5366
/**
5367
 * \brief Get spheroid squared eccentricity
5368
 *
5369
 * @return squared eccentricity (or -1 in case of error)
5370
 * @since GDAL 2.3
5371
 */
5372
5373
double OGRSpatialReference::GetSquaredEccentricity() const
5374
5375
0
{
5376
0
    OGRErr eErr = OGRERR_NONE;
5377
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5378
0
    if (eErr != OGRERR_NONE)
5379
0
    {
5380
0
        return -1.0;
5381
0
    }
5382
0
    if (dfInvFlattening == 0.0)
5383
0
        return 0.0;
5384
0
    if (dfInvFlattening < 0.5)
5385
0
        return -1.0;
5386
0
    return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5387
0
}
5388
5389
/************************************************************************/
5390
/*                            GetSemiMinor()                            */
5391
/************************************************************************/
5392
5393
/**
5394
 * \brief Get spheroid semi minor axis.
5395
 *
5396
 * This method does the same thing as the C function OSRGetSemiMinor().
5397
 *
5398
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5399
 * can be found.
5400
 *
5401
 * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5402
 */
5403
5404
double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5405
5406
0
{
5407
0
    const double dfSemiMajor = GetSemiMajor(pnErr);
5408
0
    const double dfInvFlattening = GetInvFlattening(pnErr);
5409
5410
0
    return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5411
0
}
5412
5413
/************************************************************************/
5414
/*                          OSRGetSemiMinor()                           */
5415
/************************************************************************/
5416
5417
/**
5418
 * \brief Get spheroid semi minor axis.
5419
 *
5420
 * This function is the same as OGRSpatialReference::GetSemiMinor()
5421
 */
5422
double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5423
5424
0
{
5425
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5426
5427
0
    return ToPointer(hSRS)->GetSemiMinor(pnErr);
5428
0
}
5429
5430
/************************************************************************/
5431
/*                             SetLocalCS()                             */
5432
/************************************************************************/
5433
5434
/**
5435
 * \brief Set the user visible LOCAL_CS name.
5436
 *
5437
 * This method is the same as the C function OSRSetLocalCS().
5438
 *
5439
 * This method will ensure a LOCAL_CS node is created as the root,
5440
 * and set the provided name on it.  It must be used before SetLinearUnits().
5441
 *
5442
 * @param pszName the user visible name to assign.  Not used as a key.
5443
 *
5444
 * @return OGRERR_NONE on success.
5445
 */
5446
5447
OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5448
5449
0
{
5450
0
    TAKE_OPTIONAL_LOCK();
5451
5452
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5453
0
        d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5454
0
    {
5455
0
        d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5456
0
    }
5457
0
    else
5458
0
    {
5459
0
        CPLDebug("OGR",
5460
0
                 "OGRSpatialReference::SetLocalCS(%s) failed.  "
5461
0
                 "It appears an incompatible object already exists.",
5462
0
                 pszName);
5463
0
        return OGRERR_FAILURE;
5464
0
    }
5465
5466
0
    return OGRERR_NONE;
5467
0
}
5468
5469
/************************************************************************/
5470
/*                           OSRSetLocalCS()                            */
5471
/************************************************************************/
5472
5473
/**
5474
 * \brief Set the user visible LOCAL_CS name.
5475
 *
5476
 * This function is the same as OGRSpatialReference::SetLocalCS()
5477
 */
5478
OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5479
5480
0
{
5481
0
    VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5482
5483
0
    return ToPointer(hSRS)->SetLocalCS(pszName);
5484
0
}
5485
5486
/************************************************************************/
5487
/*                             SetGeocCS()                              */
5488
/************************************************************************/
5489
5490
/**
5491
 * \brief Set the user visible GEOCCS name.
5492
 *
5493
 * This method is the same as the C function OSRSetGeocCS().
5494
5495
 * This method will ensure a GEOCCS node is created as the root,
5496
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5497
 * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5498
 * the GEOGCS.
5499
 *
5500
 * @param pszName the user visible name to assign.  Not used as a key.
5501
 *
5502
 * @return OGRERR_NONE on success.
5503
 *
5504
 * @since OGR 1.9.0
5505
 */
5506
5507
OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5508
5509
0
{
5510
0
    TAKE_OPTIONAL_LOCK();
5511
5512
0
    OGRErr eErr = OGRERR_NONE;
5513
0
    d->refreshProjObj();
5514
0
    d->demoteFromBoundCRS();
5515
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN)
5516
0
    {
5517
0
        d->setPjCRS(proj_create_geocentric_crs(
5518
0
            d->getPROJContext(), pszName, "World Geodetic System 1984",
5519
0
            "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5520
0
            SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5521
0
            "Metre", 1.0));
5522
0
    }
5523
0
    else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5524
0
    {
5525
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5526
0
    }
5527
0
    else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5528
0
             d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5529
0
    {
5530
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5531
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
5532
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5533
0
        if (datum == nullptr)
5534
0
        {
5535
0
            datum =
5536
0
                proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5537
0
        }
5538
0
#endif
5539
0
        if (datum == nullptr)
5540
0
        {
5541
0
            d->undoDemoteFromBoundCRS();
5542
0
            return OGRERR_FAILURE;
5543
0
        }
5544
5545
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
5546
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5547
0
            0.0);
5548
0
        d->setPjCRS(pj_crs);
5549
5550
0
        proj_destroy(datum);
5551
0
    }
5552
0
    else
5553
0
    {
5554
0
        CPLDebug("OGR",
5555
0
                 "OGRSpatialReference::SetGeocCS(%s) failed.  "
5556
0
                 "It appears an incompatible object already exists.",
5557
0
                 pszName);
5558
0
        eErr = OGRERR_FAILURE;
5559
0
    }
5560
0
    d->undoDemoteFromBoundCRS();
5561
5562
0
    return eErr;
5563
0
}
5564
5565
/************************************************************************/
5566
/*                            OSRSetGeocCS()                            */
5567
/************************************************************************/
5568
5569
/**
5570
 * \brief Set the user visible PROJCS name.
5571
 *
5572
 * This function is the same as OGRSpatialReference::SetGeocCS()
5573
 *
5574
 * @since OGR 1.9.0
5575
 */
5576
OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5577
5578
0
{
5579
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5580
5581
0
    return ToPointer(hSRS)->SetGeocCS(pszName);
5582
0
}
5583
5584
/************************************************************************/
5585
/*                             SetVertCS()                              */
5586
/************************************************************************/
5587
5588
/**
5589
 * \brief Set the user visible VERT_CS name.
5590
 *
5591
 * This method is the same as the C function OSRSetVertCS().
5592
5593
 * This method will ensure a VERT_CS node is created if needed.  If the
5594
 * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5595
 * turned into a COMPD_CS.
5596
 *
5597
 * @param pszVertCSName the user visible name of the vertical coordinate
5598
 * system. Not used as a key.
5599
 *
5600
 * @param pszVertDatumName the user visible name of the vertical datum.  It
5601
 * is helpful if this matches the EPSG name.
5602
 *
5603
 * @param nVertDatumType the OGC vertical datum type. Ignored
5604
 *
5605
 * @return OGRERR_NONE on success.
5606
 *
5607
 * @since OGR 1.9.0
5608
 */
5609
5610
OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5611
                                      const char *pszVertDatumName,
5612
                                      int nVertDatumType)
5613
5614
0
{
5615
0
    TAKE_OPTIONAL_LOCK();
5616
5617
0
    CPL_IGNORE_RET_VAL(nVertDatumType);
5618
5619
0
    d->refreshProjObj();
5620
5621
0
    auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5622
0
                                            pszVertDatumName, nullptr, 0.0);
5623
5624
    /* -------------------------------------------------------------------- */
5625
    /*      Handle the case where we want to make a compound coordinate     */
5626
    /*      system.                                                         */
5627
    /* -------------------------------------------------------------------- */
5628
0
    if (IsProjected() || IsGeographic())
5629
0
    {
5630
0
        auto compoundCRS = proj_create_compound_crs(
5631
0
            d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5632
0
        proj_destroy(vertCRS);
5633
0
        d->setPjCRS(compoundCRS);
5634
0
    }
5635
0
    else
5636
0
    {
5637
0
        d->setPjCRS(vertCRS);
5638
0
    }
5639
0
    return OGRERR_NONE;
5640
0
}
5641
5642
/************************************************************************/
5643
/*                            OSRSetVertCS()                            */
5644
/************************************************************************/
5645
5646
/**
5647
 * \brief Setup the vertical coordinate system.
5648
 *
5649
 * This function is the same as OGRSpatialReference::SetVertCS()
5650
 *
5651
 * @since OGR 1.9.0
5652
 */
5653
OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5654
                    const char *pszVertDatumName, int nVertDatumType)
5655
5656
0
{
5657
0
    VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5658
5659
0
    return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5660
0
                                      nVertDatumType);
5661
0
}
5662
5663
/************************************************************************/
5664
/*                           SetCompoundCS()                            */
5665
/************************************************************************/
5666
5667
/**
5668
 * \brief Setup a compound coordinate system.
5669
 *
5670
 * This method is the same as the C function OSRSetCompoundCS().
5671
5672
 * This method is replace the current SRS with a COMPD_CS coordinate system
5673
 * consisting of the passed in horizontal and vertical coordinate systems.
5674
 *
5675
 * @param pszName the name of the compound coordinate system.
5676
 *
5677
 * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5678
 *
5679
 * @param poVertSRS the vertical SRS (VERT_CS).
5680
 *
5681
 * @return OGRERR_NONE on success.
5682
 */
5683
5684
OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5685
                                          const OGRSpatialReference *poHorizSRS,
5686
                                          const OGRSpatialReference *poVertSRS)
5687
5688
0
{
5689
0
    TAKE_OPTIONAL_LOCK();
5690
5691
    /* -------------------------------------------------------------------- */
5692
    /*      Verify these are legal horizontal and vertical coordinate       */
5693
    /*      systems.                                                        */
5694
    /* -------------------------------------------------------------------- */
5695
0
    if (!poVertSRS->IsVertical())
5696
0
    {
5697
0
        CPLError(CE_Failure, CPLE_AppDefined,
5698
0
                 "SetCompoundCS() fails, vertical component is not VERT_CS.");
5699
0
        return OGRERR_FAILURE;
5700
0
    }
5701
0
    if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5702
0
    {
5703
0
        CPLError(CE_Failure, CPLE_AppDefined,
5704
0
                 "SetCompoundCS() fails, horizontal component is not PROJCS or "
5705
0
                 "GEOGCS.");
5706
0
        return OGRERR_FAILURE;
5707
0
    }
5708
5709
    /* -------------------------------------------------------------------- */
5710
    /*      Replace with compound srs.                                      */
5711
    /* -------------------------------------------------------------------- */
5712
0
    Clear();
5713
5714
0
    auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5715
0
                                                poHorizSRS->d->m_pj_crs,
5716
0
                                                poVertSRS->d->m_pj_crs);
5717
0
    d->setPjCRS(compoundCRS);
5718
5719
0
    return OGRERR_NONE;
5720
0
}
5721
5722
/************************************************************************/
5723
/*                          OSRSetCompoundCS()                          */
5724
/************************************************************************/
5725
5726
/**
5727
 * \brief Setup a compound coordinate system.
5728
 *
5729
 * This function is the same as OGRSpatialReference::SetCompoundCS()
5730
 */
5731
OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5732
                        OGRSpatialReferenceH hHorizSRS,
5733
                        OGRSpatialReferenceH hVertSRS)
5734
5735
0
{
5736
0
    VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5737
0
    VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5738
0
    VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5739
5740
0
    return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5741
0
                                          ToPointer(hVertSRS));
5742
0
}
5743
5744
/************************************************************************/
5745
/*                             SetProjCS()                              */
5746
/************************************************************************/
5747
5748
/**
5749
 * \brief Set the user visible PROJCS name.
5750
 *
5751
 * This method is the same as the C function OSRSetProjCS().
5752
 *
5753
 * This method will ensure a PROJCS node is created as the root,
5754
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5755
 * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5756
 *
5757
 * @param pszName the user visible name to assign.  Not used as a key.
5758
 *
5759
 * @return OGRERR_NONE on success.
5760
 */
5761
5762
OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5763
5764
0
{
5765
0
    TAKE_OPTIONAL_LOCK();
5766
5767
0
    d->refreshProjObj();
5768
0
    d->demoteFromBoundCRS();
5769
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5770
0
    {
5771
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5772
0
    }
5773
0
    else
5774
0
    {
5775
0
        auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5776
0
                                                nullptr, nullptr, nullptr,
5777
0
                                                nullptr, nullptr, 0, nullptr);
5778
0
        auto cs = proj_create_cartesian_2D_cs(
5779
0
            d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5780
5781
0
        auto projCRS = proj_create_projected_crs(
5782
0
            d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5783
0
        proj_destroy(dummyConv);
5784
0
        proj_destroy(cs);
5785
5786
0
        d->setPjCRS(projCRS);
5787
0
    }
5788
0
    d->undoDemoteFromBoundCRS();
5789
0
    return OGRERR_NONE;
5790
0
}
5791
5792
/************************************************************************/
5793
/*                            OSRSetProjCS()                            */
5794
/************************************************************************/
5795
5796
/**
5797
 * \brief Set the user visible PROJCS name.
5798
 *
5799
 * This function is the same as OGRSpatialReference::SetProjCS()
5800
 */
5801
OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5802
5803
0
{
5804
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5805
5806
0
    return ToPointer(hSRS)->SetProjCS(pszName);
5807
0
}
5808
5809
/************************************************************************/
5810
/*                           SetProjection()                            */
5811
/************************************************************************/
5812
5813
/**
5814
 * \brief Set a projection name.
5815
 *
5816
 * This method is the same as the C function OSRSetProjection().
5817
 *
5818
 * @param pszProjection the projection name, which should be selected from
5819
 * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5820
 *
5821
 * @return OGRERR_NONE on success.
5822
 */
5823
5824
OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5825
5826
0
{
5827
0
    TAKE_OPTIONAL_LOCK();
5828
5829
0
    OGR_SRSNode *poGeogCS = nullptr;
5830
5831
0
    if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5832
0
    {
5833
0
        poGeogCS = d->m_poRoot;
5834
0
        d->m_poRoot = nullptr;
5835
0
    }
5836
5837
0
    if (!GetAttrNode("PROJCS"))
5838
0
    {
5839
0
        SetNode("PROJCS", "unnamed");
5840
0
    }
5841
5842
0
    const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5843
0
    if (eErr != OGRERR_NONE)
5844
0
        return eErr;
5845
5846
0
    if (poGeogCS != nullptr)
5847
0
        d->m_poRoot->InsertChild(poGeogCS, 1);
5848
5849
0
    return OGRERR_NONE;
5850
0
}
5851
5852
/************************************************************************/
5853
/*                            OSRSetProjection()                        */
5854
/************************************************************************/
5855
5856
/**
5857
 * \brief Set a projection name.
5858
 *
5859
 * This function is the same as OGRSpatialReference::SetProjection()
5860
 */
5861
OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5862
5863
0
{
5864
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5865
5866
0
    return ToPointer(hSRS)->SetProjection(pszProjection);
5867
0
}
5868
5869
/************************************************************************/
5870
/*                      GetWKT2ProjectionMethod()                       */
5871
/************************************************************************/
5872
5873
/**
5874
 * \brief Returns info on the projection method, based on WKT2 naming
5875
 * conventions.
5876
 *
5877
 * The returned strings are short lived and should be considered to be
5878
 * invalidated by any further call to the GDAL API.
5879
 *
5880
 * @param[out] ppszMethodName Pointer to a string that will receive the
5881
 * projection method name.
5882
 * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5883
 * receive the name of the authority that defines the projection method.
5884
 * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5885
 * an authority.
5886
 * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5887
 * receive the code that defines the projection method.
5888
 * *ppszMethodCode may be nullptr if the projection method is not linked to
5889
 * an authority.
5890
 *
5891
 * @return OGRERR_NONE on success.
5892
 */
5893
OGRErr
5894
OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5895
                                             const char **ppszMethodAuthName,
5896
                                             const char **ppszMethodCode) const
5897
0
{
5898
0
    TAKE_OPTIONAL_LOCK();
5899
5900
0
    auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5901
0
    if (!conv)
5902
0
        return OGRERR_FAILURE;
5903
0
    const char *pszTmpMethodName = "";
5904
0
    const char *pszTmpMethodAuthName = "";
5905
0
    const char *pszTmpMethodCode = "";
5906
0
    int ret = proj_coordoperation_get_method_info(
5907
0
        d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5908
0
        &pszTmpMethodCode);
5909
    // "Internalize" temporary strings returned by PROJ
5910
0
    CPLAssert(pszTmpMethodName);
5911
0
    if (ppszMethodName)
5912
0
        *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5913
0
    if (ppszMethodAuthName)
5914
0
        *ppszMethodAuthName = pszTmpMethodAuthName
5915
0
                                  ? CPLSPrintf("%s", pszTmpMethodAuthName)
5916
0
                                  : nullptr;
5917
0
    if (ppszMethodCode)
5918
0
        *ppszMethodCode =
5919
0
            pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5920
0
    proj_destroy(conv);
5921
0
    return ret ? OGRERR_NONE : OGRERR_FAILURE;
5922
0
}
5923
5924
/************************************************************************/
5925
/*                            SetProjParm()                             */
5926
/************************************************************************/
5927
5928
/**
5929
 * \brief Set a projection parameter value.
5930
 *
5931
 * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5932
 *
5933
 * This method is the same as the C function OSRSetProjParm().
5934
 *
5935
 * Please check https://gdal.org/proj_list pages for
5936
 * legal parameter names for specific projections.
5937
 *
5938
 *
5939
 * @param pszParamName the parameter name, which should be selected from
5940
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5941
 *
5942
 * @param dfValue value to assign.
5943
 *
5944
 * @return OGRERR_NONE on success.
5945
 */
5946
5947
OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5948
                                        double dfValue)
5949
5950
0
{
5951
0
    TAKE_OPTIONAL_LOCK();
5952
5953
0
    OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5954
5955
0
    if (poPROJCS == nullptr)
5956
0
        return OGRERR_FAILURE;
5957
5958
0
    char szValue[64] = {'\0'};
5959
0
    OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5960
5961
    /* -------------------------------------------------------------------- */
5962
    /*      Try to find existing parameter with this name.                  */
5963
    /* -------------------------------------------------------------------- */
5964
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5965
0
    {
5966
0
        OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5967
5968
0
        if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5969
0
            poParam->GetChildCount() == 2 &&
5970
0
            EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5971
0
        {
5972
0
            poParam->GetChild(1)->SetValue(szValue);
5973
0
            return OGRERR_NONE;
5974
0
        }
5975
0
    }
5976
5977
    /* -------------------------------------------------------------------- */
5978
    /*      Otherwise create a new parameter and append.                    */
5979
    /* -------------------------------------------------------------------- */
5980
0
    OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5981
0
    poParam->AddChild(new OGR_SRSNode(pszParamName));
5982
0
    poParam->AddChild(new OGR_SRSNode(szValue));
5983
5984
0
    poPROJCS->AddChild(poParam);
5985
5986
0
    return OGRERR_NONE;
5987
0
}
5988
5989
/************************************************************************/
5990
/*                           OSRSetProjParm()                           */
5991
/************************************************************************/
5992
5993
/**
5994
 * \brief Set a projection parameter value.
5995
 *
5996
 * This function is the same as OGRSpatialReference::SetProjParm()
5997
 */
5998
OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5999
                      double dfValue)
6000
6001
0
{
6002
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6003
6004
0
    return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6005
0
}
6006
6007
/************************************************************************/
6008
/*                            FindProjParm()                            */
6009
/************************************************************************/
6010
6011
/**
6012
 * \brief Return the child index of the named projection parameter on
6013
 * its parent PROJCS node.
6014
 *
6015
 * @param pszParameter projection parameter to look for
6016
 * @param poPROJCS projection CS node to look in. If NULL is passed,
6017
 *        the PROJCS node of the SpatialReference object will be searched.
6018
 *
6019
 * @return the child index of the named projection parameter. -1 on failure
6020
 */
6021
int OGRSpatialReference::FindProjParm(const char *pszParameter,
6022
                                      const OGR_SRSNode *poPROJCS) const
6023
6024
0
{
6025
0
    TAKE_OPTIONAL_LOCK();
6026
6027
0
    if (poPROJCS == nullptr)
6028
0
        poPROJCS = GetAttrNode("PROJCS");
6029
6030
0
    if (poPROJCS == nullptr)
6031
0
        return -1;
6032
6033
    /* -------------------------------------------------------------------- */
6034
    /*      Search for requested parameter.                                 */
6035
    /* -------------------------------------------------------------------- */
6036
0
    bool bIsWKT2 = false;
6037
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6038
0
    {
6039
0
        const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6040
6041
0
        if (poParameter->GetChildCount() >= 2)
6042
0
        {
6043
0
            const char *pszValue = poParameter->GetValue();
6044
0
            if (EQUAL(pszValue, "PARAMETER") &&
6045
0
                EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6046
0
                      pszParameter))
6047
0
            {
6048
0
                return iChild;
6049
0
            }
6050
0
            else if (EQUAL(pszValue, "METHOD"))
6051
0
            {
6052
0
                bIsWKT2 = true;
6053
0
            }
6054
0
        }
6055
0
    }
6056
6057
    /* -------------------------------------------------------------------- */
6058
    /*      Try similar names, for selected parameters.                     */
6059
    /* -------------------------------------------------------------------- */
6060
0
    if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6061
0
    {
6062
0
        if (bIsWKT2)
6063
0
        {
6064
0
            int iChild = FindProjParm(
6065
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6066
0
            if (iChild == -1)
6067
0
                iChild = FindProjParm(
6068
0
                    EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6069
0
            return iChild;
6070
0
        }
6071
0
        return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6072
0
    }
6073
6074
0
    if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6075
0
    {
6076
0
        if (bIsWKT2)
6077
0
        {
6078
0
            int iChild = FindProjParm(
6079
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6080
0
            if (iChild == -1)
6081
0
                iChild = FindProjParm(
6082
0
                    EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6083
0
            return iChild;
6084
0
        }
6085
0
        int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6086
0
        if (iChild == -1)
6087
0
            iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6088
0
        return iChild;
6089
0
    }
6090
6091
0
    return -1;
6092
0
}
6093
6094
/************************************************************************/
6095
/*                            GetProjParm()                             */
6096
/************************************************************************/
6097
6098
/**
6099
 * \brief Fetch a projection parameter value.
6100
 *
6101
 * NOTE: This code should be modified to translate non degree angles into
6102
 * degrees based on the GEOGCS unit.  This has not yet been done.
6103
 *
6104
 * This method is the same as the C function OSRGetProjParm().
6105
 *
6106
 * @param pszName the name of the parameter to fetch, from the set of
6107
 * SRS_PP codes in ogr_srs_api.h.
6108
 *
6109
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6110
 *
6111
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6112
 *
6113
 * @return value of parameter.
6114
 */
6115
6116
double OGRSpatialReference::GetProjParm(const char *pszName,
6117
                                        double dfDefaultValue,
6118
                                        OGRErr *pnErr) const
6119
6120
0
{
6121
0
    TAKE_OPTIONAL_LOCK();
6122
6123
0
    d->refreshProjObj();
6124
0
    GetRoot();  // force update of d->m_bNodesWKT2
6125
6126
0
    if (pnErr != nullptr)
6127
0
        *pnErr = OGRERR_NONE;
6128
6129
    /* -------------------------------------------------------------------- */
6130
    /*      Find the desired parameter.                                     */
6131
    /* -------------------------------------------------------------------- */
6132
0
    const OGR_SRSNode *poPROJCS =
6133
0
        GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6134
0
    if (poPROJCS == nullptr)
6135
0
    {
6136
0
        if (pnErr != nullptr)
6137
0
            *pnErr = OGRERR_FAILURE;
6138
0
        return dfDefaultValue;
6139
0
    }
6140
6141
0
    const int iChild = FindProjParm(pszName, poPROJCS);
6142
0
    if (iChild == -1)
6143
0
    {
6144
0
        if (IsProjected() && GetAxesCount() == 3)
6145
0
        {
6146
0
            OGRSpatialReference *poSRSTmp = Clone();
6147
0
            poSRSTmp->DemoteTo2D(nullptr);
6148
0
            const double dfRet =
6149
0
                poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6150
0
            delete poSRSTmp;
6151
0
            return dfRet;
6152
0
        }
6153
6154
0
        if (pnErr != nullptr)
6155
0
            *pnErr = OGRERR_FAILURE;
6156
0
        return dfDefaultValue;
6157
0
    }
6158
6159
0
    const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6160
0
    return CPLAtof(poParameter->GetChild(1)->GetValue());
6161
0
}
6162
6163
/************************************************************************/
6164
/*                           OSRGetProjParm()                           */
6165
/************************************************************************/
6166
6167
/**
6168
 * \brief Fetch a projection parameter value.
6169
 *
6170
 * This function is the same as OGRSpatialReference::GetProjParm()
6171
 */
6172
double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6173
                      double dfDefaultValue, OGRErr *pnErr)
6174
6175
0
{
6176
0
    VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6177
6178
0
    return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6179
0
}
6180
6181
/************************************************************************/
6182
/*                          GetNormProjParm()                           */
6183
/************************************************************************/
6184
6185
/**
6186
 * \brief Fetch a normalized projection parameter value.
6187
 *
6188
 * This method is the same as GetProjParm() except that the value of
6189
 * the parameter is "normalized" into degrees or meters depending on
6190
 * whether it is linear or angular.
6191
 *
6192
 * This method is the same as the C function OSRGetNormProjParm().
6193
 *
6194
 * @param pszName the name of the parameter to fetch, from the set of
6195
 * SRS_PP codes in ogr_srs_api.h.
6196
 *
6197
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6198
 *
6199
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6200
 *
6201
 * @return value of parameter.
6202
 */
6203
6204
double OGRSpatialReference::GetNormProjParm(const char *pszName,
6205
                                            double dfDefaultValue,
6206
                                            OGRErr *pnErr) const
6207
6208
0
{
6209
0
    TAKE_OPTIONAL_LOCK();
6210
6211
0
    GetNormInfo();
6212
6213
0
    OGRErr nError = OGRERR_NONE;
6214
0
    double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6215
0
    if (pnErr != nullptr)
6216
0
        *pnErr = nError;
6217
6218
    // If we got the default just return it unadjusted.
6219
0
    if (nError != OGRERR_NONE)
6220
0
        return dfRawResult;
6221
6222
0
    if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6223
0
        dfRawResult *= d->dfToDegrees;
6224
6225
0
    if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6226
0
        return dfRawResult * d->dfToMeter;
6227
6228
0
    return dfRawResult;
6229
0
}
6230
6231
/************************************************************************/
6232
/*                         OSRGetNormProjParm()                         */
6233
/************************************************************************/
6234
6235
/**
6236
 * \brief This function is the same as OGRSpatialReference::
6237
 *
6238
 * This function is the same as OGRSpatialReference::GetNormProjParm()
6239
 */
6240
double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6241
                          double dfDefaultValue, OGRErr *pnErr)
6242
6243
0
{
6244
0
    VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6245
6246
0
    return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6247
0
}
6248
6249
/************************************************************************/
6250
/*                          SetNormProjParm()                           */
6251
/************************************************************************/
6252
6253
/**
6254
 * \brief Set a projection parameter with a normalized value.
6255
 *
6256
 * This method is the same as SetProjParm() except that the value of
6257
 * the parameter passed in is assumed to be in "normalized" form (decimal
6258
 * degrees for angular values, meters for linear values.  The values are
6259
 * converted in a form suitable for the GEOGCS and linear units in effect.
6260
 *
6261
 * This method is the same as the C function OSRSetNormProjParm().
6262
 *
6263
 * @param pszName the parameter name, which should be selected from
6264
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6265
 *
6266
 * @param dfValue value to assign.
6267
 *
6268
 * @return OGRERR_NONE on success.
6269
 */
6270
6271
OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6272
6273
0
{
6274
0
    TAKE_OPTIONAL_LOCK();
6275
6276
0
    GetNormInfo();
6277
6278
0
    if (d->dfToDegrees != 0.0 &&
6279
0
        (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6280
0
        IsAngularParameter(pszName))
6281
0
    {
6282
0
        dfValue /= d->dfToDegrees;
6283
0
    }
6284
0
    else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6285
0
             IsLinearParameter(pszName))
6286
0
        dfValue /= d->dfToMeter;
6287
6288
0
    return SetProjParm(pszName, dfValue);
6289
0
}
6290
6291
/************************************************************************/
6292
/*                         OSRSetNormProjParm()                         */
6293
/************************************************************************/
6294
6295
/**
6296
 * \brief Set a projection parameter with a normalized value.
6297
 *
6298
 * This function is the same as OGRSpatialReference::SetNormProjParm()
6299
 */
6300
OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6301
                          double dfValue)
6302
6303
0
{
6304
0
    VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6305
6306
0
    return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6307
0
}
6308
6309
/************************************************************************/
6310
/*                               SetTM()                                */
6311
/************************************************************************/
6312
6313
OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6314
                                  double dfScale, double dfFalseEasting,
6315
                                  double dfFalseNorthing)
6316
6317
0
{
6318
0
    TAKE_OPTIONAL_LOCK();
6319
6320
0
    return d->replaceConversionAndUnref(
6321
0
        proj_create_conversion_transverse_mercator(
6322
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6323
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6324
0
}
6325
6326
/************************************************************************/
6327
/*                              OSRSetTM()                              */
6328
/************************************************************************/
6329
6330
OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6331
                double dfCenterLong, double dfScale, double dfFalseEasting,
6332
                double dfFalseNorthing)
6333
6334
0
{
6335
0
    VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6336
6337
0
    return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6338
0
                                  dfFalseEasting, dfFalseNorthing);
6339
0
}
6340
6341
/************************************************************************/
6342
/*                            SetTMVariant()                            */
6343
/************************************************************************/
6344
6345
OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6346
                                         double dfCenterLat,
6347
                                         double dfCenterLong, double dfScale,
6348
                                         double dfFalseEasting,
6349
                                         double dfFalseNorthing)
6350
6351
0
{
6352
0
    TAKE_OPTIONAL_LOCK();
6353
6354
0
    SetProjection(pszVariantName);
6355
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6356
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6357
0
    SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6358
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6359
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6360
6361
0
    return OGRERR_NONE;
6362
0
}
6363
6364
/************************************************************************/
6365
/*                          OSRSetTMVariant()                           */
6366
/************************************************************************/
6367
6368
OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6369
                       double dfCenterLat, double dfCenterLong, double dfScale,
6370
                       double dfFalseEasting, double dfFalseNorthing)
6371
6372
0
{
6373
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6374
6375
0
    return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6376
0
                                         dfCenterLong, dfScale, dfFalseEasting,
6377
0
                                         dfFalseNorthing);
6378
0
}
6379
6380
/************************************************************************/
6381
/*                              SetTMSO()                               */
6382
/************************************************************************/
6383
6384
OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6385
                                    double dfScale, double dfFalseEasting,
6386
                                    double dfFalseNorthing)
6387
6388
0
{
6389
0
    TAKE_OPTIONAL_LOCK();
6390
6391
0
    auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6392
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6393
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6394
6395
0
    const char *pszName = nullptr;
6396
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6397
0
    CPLString osName = pszName ? pszName : "";
6398
6399
0
    d->refreshProjObj();
6400
6401
0
    d->demoteFromBoundCRS();
6402
6403
0
    auto cs = proj_create_cartesian_2D_cs(
6404
0
        d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6405
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6406
0
    auto projCRS =
6407
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6408
0
                                  d->getGeodBaseCRS(), conv, cs);
6409
0
    proj_destroy(conv);
6410
0
    proj_destroy(cs);
6411
6412
0
    d->setPjCRS(projCRS);
6413
6414
0
    d->undoDemoteFromBoundCRS();
6415
6416
0
    return OGRERR_NONE;
6417
0
}
6418
6419
/************************************************************************/
6420
/*                             OSRSetTMSO()                             */
6421
/************************************************************************/
6422
6423
OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6424
                  double dfCenterLong, double dfScale, double dfFalseEasting,
6425
                  double dfFalseNorthing)
6426
6427
0
{
6428
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6429
6430
0
    return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6431
0
                                    dfFalseEasting, dfFalseNorthing);
6432
0
}
6433
6434
/************************************************************************/
6435
/*                              SetTPED()                               */
6436
/************************************************************************/
6437
6438
OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6439
                                    double dfLat2, double dfLong2,
6440
                                    double dfFalseEasting,
6441
                                    double dfFalseNorthing)
6442
6443
0
{
6444
0
    TAKE_OPTIONAL_LOCK();
6445
6446
0
    return d->replaceConversionAndUnref(
6447
0
        proj_create_conversion_two_point_equidistant(
6448
0
            d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6449
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6450
0
}
6451
6452
/************************************************************************/
6453
/*                             OSRSetTPED()                             */
6454
/************************************************************************/
6455
6456
OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6457
                  double dfLat2, double dfLong2, double dfFalseEasting,
6458
                  double dfFalseNorthing)
6459
6460
0
{
6461
0
    VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6462
6463
0
    return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6464
0
                                    dfFalseEasting, dfFalseNorthing);
6465
0
}
6466
6467
/************************************************************************/
6468
/*                               SetTMG()                               */
6469
/************************************************************************/
6470
6471
OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6472
                                   double dfFalseEasting,
6473
                                   double dfFalseNorthing)
6474
6475
0
{
6476
0
    TAKE_OPTIONAL_LOCK();
6477
6478
0
    return d->replaceConversionAndUnref(
6479
0
        proj_create_conversion_tunisia_mapping_grid(
6480
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6481
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6482
0
}
6483
6484
/************************************************************************/
6485
/*                             OSRSetTMG()                              */
6486
/************************************************************************/
6487
6488
OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6489
                 double dfCenterLong, double dfFalseEasting,
6490
                 double dfFalseNorthing)
6491
6492
0
{
6493
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6494
6495
0
    return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6496
0
                                   dfFalseNorthing);
6497
0
}
6498
6499
/************************************************************************/
6500
/*                              SetACEA()                               */
6501
/************************************************************************/
6502
6503
OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6504
                                    double dfCenterLat, double dfCenterLong,
6505
                                    double dfFalseEasting,
6506
                                    double dfFalseNorthing)
6507
6508
0
{
6509
0
    TAKE_OPTIONAL_LOCK();
6510
6511
    // Note different order of parameters. The one in PROJ is conformant with
6512
    // EPSG
6513
0
    return d->replaceConversionAndUnref(
6514
0
        proj_create_conversion_albers_equal_area(
6515
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6516
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6517
0
}
6518
6519
/************************************************************************/
6520
/*                             OSRSetACEA()                             */
6521
/************************************************************************/
6522
6523
OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6524
                  double dfCenterLat, double dfCenterLong,
6525
                  double dfFalseEasting, double dfFalseNorthing)
6526
6527
0
{
6528
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6529
6530
0
    return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6531
0
                                    dfFalseEasting, dfFalseNorthing);
6532
0
}
6533
6534
/************************************************************************/
6535
/*                               SetAE()                                */
6536
/************************************************************************/
6537
6538
OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6539
                                  double dfFalseEasting, double dfFalseNorthing)
6540
6541
0
{
6542
0
    TAKE_OPTIONAL_LOCK();
6543
6544
0
    return d->replaceConversionAndUnref(
6545
0
        proj_create_conversion_azimuthal_equidistant(
6546
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6547
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6548
0
}
6549
6550
/************************************************************************/
6551
/*                              OSRSetAE()                              */
6552
/************************************************************************/
6553
6554
OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6555
                double dfCenterLong, double dfFalseEasting,
6556
                double dfFalseNorthing)
6557
6558
0
{
6559
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6560
6561
0
    return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6562
0
                                  dfFalseNorthing);
6563
0
}
6564
6565
/************************************************************************/
6566
/*                              SetBonne()                              */
6567
/************************************************************************/
6568
6569
OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6570
                                     double dfFalseEasting,
6571
                                     double dfFalseNorthing)
6572
6573
0
{
6574
0
    TAKE_OPTIONAL_LOCK();
6575
6576
0
    return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6577
0
        d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6578
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6579
0
}
6580
6581
/************************************************************************/
6582
/*                            OSRSetBonne()                             */
6583
/************************************************************************/
6584
6585
OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6586
                   double dfCentralMeridian, double dfFalseEasting,
6587
                   double dfFalseNorthing)
6588
6589
0
{
6590
0
    VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6591
6592
0
    return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6593
0
                                     dfFalseNorthing);
6594
0
}
6595
6596
/************************************************************************/
6597
/*                               SetCEA()                               */
6598
/************************************************************************/
6599
6600
OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6601
                                   double dfFalseEasting,
6602
                                   double dfFalseNorthing)
6603
6604
0
{
6605
0
    TAKE_OPTIONAL_LOCK();
6606
6607
0
    return d->replaceConversionAndUnref(
6608
0
        proj_create_conversion_lambert_cylindrical_equal_area(
6609
0
            d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6610
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6611
0
}
6612
6613
/************************************************************************/
6614
/*                             OSRSetCEA()                              */
6615
/************************************************************************/
6616
6617
OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6618
                 double dfCentralMeridian, double dfFalseEasting,
6619
                 double dfFalseNorthing)
6620
6621
0
{
6622
0
    VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6623
6624
0
    return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6625
0
                                   dfFalseNorthing);
6626
0
}
6627
6628
/************************************************************************/
6629
/*                               SetCS()                                */
6630
/************************************************************************/
6631
6632
OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6633
                                  double dfFalseEasting, double dfFalseNorthing)
6634
6635
0
{
6636
0
    TAKE_OPTIONAL_LOCK();
6637
6638
0
    return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6639
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6640
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6641
0
}
6642
6643
/************************************************************************/
6644
/*                              OSRSetCS()                              */
6645
/************************************************************************/
6646
6647
OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6648
                double dfCenterLong, double dfFalseEasting,
6649
                double dfFalseNorthing)
6650
6651
0
{
6652
0
    VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6653
6654
0
    return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6655
0
                                  dfFalseNorthing);
6656
0
}
6657
6658
/************************************************************************/
6659
/*                               SetEC()                                */
6660
/************************************************************************/
6661
6662
OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6663
                                  double dfCenterLat, double dfCenterLong,
6664
                                  double dfFalseEasting, double dfFalseNorthing)
6665
6666
0
{
6667
0
    TAKE_OPTIONAL_LOCK();
6668
6669
    // Note: different order of arguments
6670
0
    return d->replaceConversionAndUnref(
6671
0
        proj_create_conversion_equidistant_conic(
6672
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6673
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6674
0
}
6675
6676
/************************************************************************/
6677
/*                              OSRSetEC()                              */
6678
/************************************************************************/
6679
6680
OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6681
                double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6682
                double dfFalseNorthing)
6683
6684
0
{
6685
0
    VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6686
6687
0
    return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6688
0
                                  dfFalseEasting, dfFalseNorthing);
6689
0
}
6690
6691
/************************************************************************/
6692
/*                             SetEckert()                              */
6693
/************************************************************************/
6694
6695
OGRErr OGRSpatialReference::SetEckert(int nVariation,  // 1-6.
6696
                                      double dfCentralMeridian,
6697
                                      double dfFalseEasting,
6698
                                      double dfFalseNorthing)
6699
6700
0
{
6701
0
    TAKE_OPTIONAL_LOCK();
6702
6703
0
    PJ *conv;
6704
0
    if (nVariation == 1)
6705
0
    {
6706
0
        conv = proj_create_conversion_eckert_i(
6707
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6708
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6709
0
    }
6710
0
    else if (nVariation == 2)
6711
0
    {
6712
0
        conv = proj_create_conversion_eckert_ii(
6713
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6714
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6715
0
    }
6716
0
    else if (nVariation == 3)
6717
0
    {
6718
0
        conv = proj_create_conversion_eckert_iii(
6719
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6720
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6721
0
    }
6722
0
    else if (nVariation == 4)
6723
0
    {
6724
0
        conv = proj_create_conversion_eckert_iv(
6725
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6726
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6727
0
    }
6728
0
    else if (nVariation == 5)
6729
0
    {
6730
0
        conv = proj_create_conversion_eckert_v(
6731
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6732
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6733
0
    }
6734
0
    else if (nVariation == 6)
6735
0
    {
6736
0
        conv = proj_create_conversion_eckert_vi(
6737
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6738
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6739
0
    }
6740
0
    else
6741
0
    {
6742
0
        CPLError(CE_Failure, CPLE_AppDefined,
6743
0
                 "Unsupported Eckert variation (%d).", nVariation);
6744
0
        return OGRERR_UNSUPPORTED_SRS;
6745
0
    }
6746
6747
0
    return d->replaceConversionAndUnref(conv);
6748
0
}
6749
6750
/************************************************************************/
6751
/*                            OSRSetEckert()                            */
6752
/************************************************************************/
6753
6754
OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6755
                    double dfCentralMeridian, double dfFalseEasting,
6756
                    double dfFalseNorthing)
6757
6758
0
{
6759
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6760
6761
0
    return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6762
0
                                      dfFalseEasting, dfFalseNorthing);
6763
0
}
6764
6765
/************************************************************************/
6766
/*                            SetEckertIV()                             */
6767
/*                                                                      */
6768
/*      Deprecated                                                      */
6769
/************************************************************************/
6770
6771
OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6772
                                        double dfFalseEasting,
6773
                                        double dfFalseNorthing)
6774
6775
0
{
6776
0
    return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6777
0
}
6778
6779
/************************************************************************/
6780
/*                           OSRSetEckertIV()                           */
6781
/************************************************************************/
6782
6783
OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6784
                      double dfFalseEasting, double dfFalseNorthing)
6785
6786
0
{
6787
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6788
6789
0
    return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6790
0
                                        dfFalseNorthing);
6791
0
}
6792
6793
/************************************************************************/
6794
/*                            SetEckertVI()                             */
6795
/*                                                                      */
6796
/*      Deprecated                                                      */
6797
/************************************************************************/
6798
6799
OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6800
                                        double dfFalseEasting,
6801
                                        double dfFalseNorthing)
6802
6803
0
{
6804
0
    return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6805
0
}
6806
6807
/************************************************************************/
6808
/*                           OSRSetEckertVI()                           */
6809
/************************************************************************/
6810
6811
OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6812
                      double dfFalseEasting, double dfFalseNorthing)
6813
6814
0
{
6815
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6816
6817
0
    return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6818
0
                                        dfFalseNorthing);
6819
0
}
6820
6821
/************************************************************************/
6822
/*                         SetEquirectangular()                         */
6823
/************************************************************************/
6824
6825
OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6826
                                               double dfCenterLong,
6827
                                               double dfFalseEasting,
6828
                                               double dfFalseNorthing)
6829
6830
0
{
6831
0
    TAKE_OPTIONAL_LOCK();
6832
6833
0
    if (dfCenterLat == 0.0)
6834
0
    {
6835
0
        return d->replaceConversionAndUnref(
6836
0
            proj_create_conversion_equidistant_cylindrical(
6837
0
                d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6838
0
                dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6839
0
    }
6840
6841
    // Non-standard extension with non-zero latitude of origin
6842
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6843
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6844
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6845
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6846
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6847
6848
0
    return OGRERR_NONE;
6849
0
}
6850
6851
/************************************************************************/
6852
/*                       OSRSetEquirectangular()                        */
6853
/************************************************************************/
6854
6855
OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6856
                             double dfCenterLong, double dfFalseEasting,
6857
                             double dfFalseNorthing)
6858
6859
0
{
6860
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6861
6862
0
    return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6863
0
                                               dfFalseEasting, dfFalseNorthing);
6864
0
}
6865
6866
/************************************************************************/
6867
/*                         SetEquirectangular2()                        */
6868
/* Generalized form                                                     */
6869
/************************************************************************/
6870
6871
OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6872
                                                double dfCenterLong,
6873
                                                double dfStdParallel1,
6874
                                                double dfFalseEasting,
6875
                                                double dfFalseNorthing)
6876
6877
0
{
6878
0
    TAKE_OPTIONAL_LOCK();
6879
6880
0
    if (dfCenterLat == 0.0)
6881
0
    {
6882
0
        return d->replaceConversionAndUnref(
6883
0
            proj_create_conversion_equidistant_cylindrical(
6884
0
                d->getPROJContext(), dfStdParallel1, dfCenterLong,
6885
0
                dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6886
0
    }
6887
6888
    // Non-standard extension with non-zero latitude of origin
6889
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6890
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6891
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6892
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6893
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6894
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6895
6896
0
    return OGRERR_NONE;
6897
0
}
6898
6899
/************************************************************************/
6900
/*                       OSRSetEquirectangular2()                       */
6901
/************************************************************************/
6902
6903
OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6904
                              double dfCenterLong, double dfStdParallel1,
6905
                              double dfFalseEasting, double dfFalseNorthing)
6906
6907
0
{
6908
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6909
6910
0
    return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6911
0
                                                dfStdParallel1, dfFalseEasting,
6912
0
                                                dfFalseNorthing);
6913
0
}
6914
6915
/************************************************************************/
6916
/*                               SetGS()                                */
6917
/************************************************************************/
6918
6919
OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6920
                                  double dfFalseEasting, double dfFalseNorthing)
6921
6922
0
{
6923
0
    return d->replaceConversionAndUnref(proj_create_conversion_gall(
6924
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6925
0
        nullptr, 0.0, nullptr, 0.0));
6926
0
}
6927
6928
/************************************************************************/
6929
/*                              OSRSetGS()                              */
6930
/************************************************************************/
6931
6932
OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6933
                double dfFalseEasting, double dfFalseNorthing)
6934
6935
0
{
6936
0
    VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6937
6938
0
    return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6939
0
                                  dfFalseNorthing);
6940
0
}
6941
6942
/************************************************************************/
6943
/*                               SetGH()                                */
6944
/************************************************************************/
6945
6946
OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6947
                                  double dfFalseEasting, double dfFalseNorthing)
6948
6949
0
{
6950
0
    TAKE_OPTIONAL_LOCK();
6951
6952
0
    return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6953
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6954
0
        nullptr, 0.0, nullptr, 0.0));
6955
0
}
6956
6957
/************************************************************************/
6958
/*                              OSRSetGH()                              */
6959
/************************************************************************/
6960
6961
OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6962
                double dfFalseEasting, double dfFalseNorthing)
6963
6964
0
{
6965
0
    VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6966
6967
0
    return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6968
0
                                  dfFalseNorthing);
6969
0
}
6970
6971
/************************************************************************/
6972
/*                              SetIGH()                                */
6973
/************************************************************************/
6974
6975
OGRErr OGRSpatialReference::SetIGH()
6976
6977
0
{
6978
0
    TAKE_OPTIONAL_LOCK();
6979
6980
0
    return d->replaceConversionAndUnref(
6981
0
        proj_create_conversion_interrupted_goode_homolosine(
6982
0
            d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6983
0
}
6984
6985
/************************************************************************/
6986
/*                              OSRSetIGH()                             */
6987
/************************************************************************/
6988
6989
OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6990
6991
0
{
6992
0
    VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6993
6994
0
    return ToPointer(hSRS)->SetIGH();
6995
0
}
6996
6997
/************************************************************************/
6998
/*                              SetGEOS()                               */
6999
/************************************************************************/
7000
7001
OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7002
                                    double dfSatelliteHeight,
7003
                                    double dfFalseEasting,
7004
                                    double dfFalseNorthing)
7005
7006
0
{
7007
0
    TAKE_OPTIONAL_LOCK();
7008
7009
0
    return d->replaceConversionAndUnref(
7010
0
        proj_create_conversion_geostationary_satellite_sweep_y(
7011
0
            d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7012
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7013
0
}
7014
7015
/************************************************************************/
7016
/*                              OSRSetGEOS()                             */
7017
/************************************************************************/
7018
7019
OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7020
                  double dfSatelliteHeight, double dfFalseEasting,
7021
                  double dfFalseNorthing)
7022
7023
0
{
7024
0
    VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7025
7026
0
    return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7027
0
                                    dfFalseEasting, dfFalseNorthing);
7028
0
}
7029
7030
/************************************************************************/
7031
/*                       SetGaussSchreiberTMercator()                   */
7032
/************************************************************************/
7033
7034
OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7035
                                                       double dfCenterLong,
7036
                                                       double dfScale,
7037
                                                       double dfFalseEasting,
7038
                                                       double dfFalseNorthing)
7039
7040
0
{
7041
0
    TAKE_OPTIONAL_LOCK();
7042
7043
0
    return d->replaceConversionAndUnref(
7044
0
        proj_create_conversion_gauss_schreiber_transverse_mercator(
7045
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7046
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7047
0
}
7048
7049
/************************************************************************/
7050
/*                     OSRSetGaussSchreiberTMercator()                  */
7051
/************************************************************************/
7052
7053
OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7054
                                     double dfCenterLat, double dfCenterLong,
7055
                                     double dfScale, double dfFalseEasting,
7056
                                     double dfFalseNorthing)
7057
7058
0
{
7059
0
    VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7060
7061
0
    return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7062
0
        dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7063
0
}
7064
7065
/************************************************************************/
7066
/*                            SetGnomonic()                             */
7067
/************************************************************************/
7068
7069
OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7070
                                        double dfFalseEasting,
7071
                                        double dfFalseNorthing)
7072
7073
0
{
7074
0
    TAKE_OPTIONAL_LOCK();
7075
7076
0
    return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7077
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7078
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7079
0
}
7080
7081
/************************************************************************/
7082
/*                           OSRSetGnomonic()                           */
7083
/************************************************************************/
7084
7085
OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7086
                      double dfCenterLong, double dfFalseEasting,
7087
                      double dfFalseNorthing)
7088
7089
0
{
7090
0
    VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7091
7092
0
    return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7093
0
                                        dfFalseEasting, dfFalseNorthing);
7094
0
}
7095
7096
/************************************************************************/
7097
/*                              SetHOMAC()                              */
7098
/************************************************************************/
7099
7100
/**
7101
 * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7102
 * azimuth angle.
7103
 *
7104
 * This projection corresponds to EPSG projection method 9815, also
7105
 * sometimes known as hotine oblique mercator (variant B).
7106
 *
7107
 * This method does the same thing as the C function OSRSetHOMAC().
7108
 *
7109
 * @param dfCenterLat Latitude of the projection origin.
7110
 * @param dfCenterLong Longitude of the projection origin.
7111
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7112
 * centerline.
7113
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7114
 * @param dfScale Scale factor applies to the projection origin.
7115
 * @param dfFalseEasting False easting.
7116
 * @param dfFalseNorthing False northing.
7117
 *
7118
 * @return OGRERR_NONE on success.
7119
 */
7120
7121
OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7122
                                     double dfAzimuth, double dfRectToSkew,
7123
                                     double dfScale, double dfFalseEasting,
7124
                                     double dfFalseNorthing)
7125
7126
0
{
7127
0
    TAKE_OPTIONAL_LOCK();
7128
7129
0
    return d->replaceConversionAndUnref(
7130
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7131
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7132
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7133
0
            0.0, nullptr, 0.0));
7134
0
}
7135
7136
/************************************************************************/
7137
/*                            OSRSetHOMAC()                             */
7138
/************************************************************************/
7139
7140
/**
7141
 * \brief Set an Oblique Mercator projection using azimuth angle.
7142
 *
7143
 * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7144
 */
7145
OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7146
                   double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7147
                   double dfScale, double dfFalseEasting,
7148
                   double dfFalseNorthing)
7149
7150
0
{
7151
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7152
7153
0
    return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7154
0
                                     dfRectToSkew, dfScale, dfFalseEasting,
7155
0
                                     dfFalseNorthing);
7156
0
}
7157
7158
/************************************************************************/
7159
/*                               SetHOM()                               */
7160
/************************************************************************/
7161
7162
/**
7163
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7164
 *
7165
 * This projection corresponds to EPSG projection method 9812, also
7166
 * sometimes known as hotine oblique mercator (variant A)..
7167
 *
7168
 * This method does the same thing as the C function OSRSetHOM().
7169
 *
7170
 * @param dfCenterLat Latitude of the projection origin.
7171
 * @param dfCenterLong Longitude of the projection origin.
7172
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7173
 * centerline.
7174
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7175
 * @param dfScale Scale factor applies to the projection origin.
7176
 * @param dfFalseEasting False easting.
7177
 * @param dfFalseNorthing False northing.
7178
 *
7179
 * @return OGRERR_NONE on success.
7180
 */
7181
7182
OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7183
                                   double dfAzimuth, double dfRectToSkew,
7184
                                   double dfScale, double dfFalseEasting,
7185
                                   double dfFalseNorthing)
7186
7187
0
{
7188
0
    TAKE_OPTIONAL_LOCK();
7189
7190
0
    return d->replaceConversionAndUnref(
7191
0
        proj_create_conversion_hotine_oblique_mercator_variant_a(
7192
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7193
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7194
0
            0.0, nullptr, 0.0));
7195
0
}
7196
7197
/************************************************************************/
7198
/*                             OSRSetHOM()                              */
7199
/************************************************************************/
7200
/**
7201
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7202
 *
7203
 * This is the same as the C++ method OGRSpatialReference::SetHOM()
7204
 */
7205
OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7206
                 double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7207
                 double dfScale, double dfFalseEasting, double dfFalseNorthing)
7208
7209
0
{
7210
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7211
7212
0
    return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7213
0
                                   dfRectToSkew, dfScale, dfFalseEasting,
7214
0
                                   dfFalseNorthing);
7215
0
}
7216
7217
/************************************************************************/
7218
/*                             SetHOM2PNO()                             */
7219
/************************************************************************/
7220
7221
/**
7222
 * \brief Set a Hotine Oblique Mercator projection using two points on
7223
 * projection centerline.
7224
 *
7225
 * This method does the same thing as the C function OSRSetHOM2PNO().
7226
 *
7227
 * @param dfCenterLat Latitude of the projection origin.
7228
 * @param dfLat1 Latitude of the first point on center line.
7229
 * @param dfLong1 Longitude of the first point on center line.
7230
 * @param dfLat2 Latitude of the second point on center line.
7231
 * @param dfLong2 Longitude of the second point on center line.
7232
 * @param dfScale Scale factor applies to the projection origin.
7233
 * @param dfFalseEasting False easting.
7234
 * @param dfFalseNorthing False northing.
7235
 *
7236
 * @return OGRERR_NONE on success.
7237
 */
7238
7239
OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7240
                                       double dfLong1, double dfLat2,
7241
                                       double dfLong2, double dfScale,
7242
                                       double dfFalseEasting,
7243
                                       double dfFalseNorthing)
7244
7245
0
{
7246
0
    TAKE_OPTIONAL_LOCK();
7247
7248
0
    return d->replaceConversionAndUnref(
7249
0
        proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7250
0
            d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7251
0
            dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7252
0
            0.0));
7253
0
}
7254
7255
/************************************************************************/
7256
/*                           OSRSetHOM2PNO()                            */
7257
/************************************************************************/
7258
/**
7259
 * \brief Set a Hotine Oblique Mercator projection using two points on
7260
 *  projection centerline.
7261
 *
7262
 * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7263
 */
7264
OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7265
                     double dfLat1, double dfLong1, double dfLat2,
7266
                     double dfLong2, double dfScale, double dfFalseEasting,
7267
                     double dfFalseNorthing)
7268
7269
0
{
7270
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7271
7272
0
    return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7273
0
                                       dfLong2, dfScale, dfFalseEasting,
7274
0
                                       dfFalseNorthing);
7275
0
}
7276
7277
/************************************************************************/
7278
/*                               SetLOM()                               */
7279
/************************************************************************/
7280
7281
/**
7282
 * \brief Set a Laborde Oblique Mercator projection.
7283
 *
7284
 * @param dfCenterLat Latitude of the projection origin.
7285
 * @param dfCenterLong Longitude of the projection origin.
7286
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7287
 * centerline.
7288
 * @param dfScale Scale factor on the initiali line
7289
 * @param dfFalseEasting False easting.
7290
 * @param dfFalseNorthing False northing.
7291
 *
7292
 * @return OGRERR_NONE on success.
7293
 */
7294
7295
OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7296
                                   double dfAzimuth, double dfScale,
7297
                                   double dfFalseEasting,
7298
                                   double dfFalseNorthing)
7299
7300
0
{
7301
0
    TAKE_OPTIONAL_LOCK();
7302
7303
0
    return d->replaceConversionAndUnref(
7304
0
        proj_create_conversion_laborde_oblique_mercator(
7305
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7306
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7307
0
}
7308
7309
/************************************************************************/
7310
/*                            SetIWMPolyconic()                         */
7311
/************************************************************************/
7312
7313
OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7314
                                            double dfCenterLong,
7315
                                            double dfFalseEasting,
7316
                                            double dfFalseNorthing)
7317
7318
0
{
7319
0
    TAKE_OPTIONAL_LOCK();
7320
7321
0
    return d->replaceConversionAndUnref(
7322
0
        proj_create_conversion_international_map_world_polyconic(
7323
0
            d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7324
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7325
0
}
7326
7327
/************************************************************************/
7328
/*                          OSRSetIWMPolyconic()                        */
7329
/************************************************************************/
7330
7331
OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7332
                          double dfLat2, double dfCenterLong,
7333
                          double dfFalseEasting, double dfFalseNorthing)
7334
7335
0
{
7336
0
    VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7337
7338
0
    return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7339
0
                                            dfFalseEasting, dfFalseNorthing);
7340
0
}
7341
7342
/************************************************************************/
7343
/*                             SetKrovak()                              */
7344
/************************************************************************/
7345
7346
/** Krovak east-north projection.
7347
 *
7348
 * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7349
 * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7350
 */
7351
OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7352
                                      double dfAzimuth,
7353
                                      double dfPseudoStdParallel1,
7354
                                      double dfScale, double dfFalseEasting,
7355
                                      double dfFalseNorthing)
7356
7357
0
{
7358
0
    TAKE_OPTIONAL_LOCK();
7359
7360
0
    return d->replaceConversionAndUnref(
7361
0
        proj_create_conversion_krovak_north_oriented(
7362
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7363
0
            dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7364
0
            nullptr, 0.0, nullptr, 0.0));
7365
0
}
7366
7367
/************************************************************************/
7368
/*                            OSRSetKrovak()                            */
7369
/************************************************************************/
7370
7371
OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7372
                    double dfCenterLong, double dfAzimuth,
7373
                    double dfPseudoStdParallel1, double dfScale,
7374
                    double dfFalseEasting, double dfFalseNorthing)
7375
7376
0
{
7377
0
    VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7378
7379
0
    return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7380
0
                                      dfPseudoStdParallel1, dfScale,
7381
0
                                      dfFalseEasting, dfFalseNorthing);
7382
0
}
7383
7384
/************************************************************************/
7385
/*                              SetLAEA()                               */
7386
/************************************************************************/
7387
7388
OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7389
                                    double dfFalseEasting,
7390
                                    double dfFalseNorthing)
7391
7392
0
{
7393
0
    TAKE_OPTIONAL_LOCK();
7394
7395
0
    auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7396
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7397
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7398
7399
0
    const char *pszName = nullptr;
7400
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7401
0
    CPLString osName = pszName ? pszName : "";
7402
7403
0
    d->refreshProjObj();
7404
7405
0
    d->demoteFromBoundCRS();
7406
7407
0
    auto cs = proj_create_cartesian_2D_cs(
7408
0
        d->getPROJContext(),
7409
0
        std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7410
0
            ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7411
0
        : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7412
0
            ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7413
0
            : PJ_CART2D_EASTING_NORTHING,
7414
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7415
0
    auto projCRS =
7416
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7417
0
                                  d->getGeodBaseCRS(), conv, cs);
7418
0
    proj_destroy(conv);
7419
0
    proj_destroy(cs);
7420
7421
0
    d->setPjCRS(projCRS);
7422
7423
0
    d->undoDemoteFromBoundCRS();
7424
7425
0
    return OGRERR_NONE;
7426
0
}
7427
7428
/************************************************************************/
7429
/*                             OSRSetLAEA()                             */
7430
/************************************************************************/
7431
7432
OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7433
                  double dfCenterLong, double dfFalseEasting,
7434
                  double dfFalseNorthing)
7435
7436
0
{
7437
0
    VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7438
7439
0
    return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7440
0
                                    dfFalseNorthing);
7441
0
}
7442
7443
/************************************************************************/
7444
/*                               SetLCC()                               */
7445
/************************************************************************/
7446
7447
OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7448
                                   double dfCenterLat, double dfCenterLong,
7449
                                   double dfFalseEasting,
7450
                                   double dfFalseNorthing)
7451
7452
0
{
7453
0
    TAKE_OPTIONAL_LOCK();
7454
7455
0
    return d->replaceConversionAndUnref(
7456
0
        proj_create_conversion_lambert_conic_conformal_2sp(
7457
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7458
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7459
0
}
7460
7461
/************************************************************************/
7462
/*                             OSRSetLCC()                              */
7463
/************************************************************************/
7464
7465
OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7466
                 double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7467
                 double dfFalseNorthing)
7468
7469
0
{
7470
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7471
7472
0
    return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7473
0
                                   dfFalseEasting, dfFalseNorthing);
7474
0
}
7475
7476
/************************************************************************/
7477
/*                             SetLCC1SP()                              */
7478
/************************************************************************/
7479
7480
OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7481
                                      double dfScale, double dfFalseEasting,
7482
                                      double dfFalseNorthing)
7483
7484
0
{
7485
0
    TAKE_OPTIONAL_LOCK();
7486
7487
0
    return d->replaceConversionAndUnref(
7488
0
        proj_create_conversion_lambert_conic_conformal_1sp(
7489
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7490
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7491
0
}
7492
7493
/************************************************************************/
7494
/*                            OSRSetLCC1SP()                            */
7495
/************************************************************************/
7496
7497
OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7498
                    double dfCenterLong, double dfScale, double dfFalseEasting,
7499
                    double dfFalseNorthing)
7500
7501
0
{
7502
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7503
7504
0
    return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7505
0
                                      dfFalseEasting, dfFalseNorthing);
7506
0
}
7507
7508
/************************************************************************/
7509
/*                              SetLCCB()                               */
7510
/************************************************************************/
7511
7512
OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7513
                                    double dfCenterLat, double dfCenterLong,
7514
                                    double dfFalseEasting,
7515
                                    double dfFalseNorthing)
7516
7517
0
{
7518
0
    TAKE_OPTIONAL_LOCK();
7519
7520
0
    return d->replaceConversionAndUnref(
7521
0
        proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7522
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7523
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7524
0
}
7525
7526
/************************************************************************/
7527
/*                             OSRSetLCCB()                             */
7528
/************************************************************************/
7529
7530
OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7531
                  double dfCenterLat, double dfCenterLong,
7532
                  double dfFalseEasting, double dfFalseNorthing)
7533
7534
0
{
7535
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7536
7537
0
    return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7538
0
                                    dfFalseEasting, dfFalseNorthing);
7539
0
}
7540
7541
/************************************************************************/
7542
/*                               SetMC()                                */
7543
/************************************************************************/
7544
7545
OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7546
                                  double dfFalseEasting, double dfFalseNorthing)
7547
7548
0
{
7549
0
    TAKE_OPTIONAL_LOCK();
7550
7551
0
    (void)dfCenterLat;  // ignored
7552
7553
0
    return d->replaceConversionAndUnref(
7554
0
        proj_create_conversion_miller_cylindrical(
7555
0
            d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7556
0
            nullptr, 0, nullptr, 0));
7557
0
}
7558
7559
/************************************************************************/
7560
/*                              OSRSetMC()                              */
7561
/************************************************************************/
7562
7563
OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7564
                double dfCenterLong, double dfFalseEasting,
7565
                double dfFalseNorthing)
7566
7567
0
{
7568
0
    VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7569
7570
0
    return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7571
0
                                  dfFalseNorthing);
7572
0
}
7573
7574
/************************************************************************/
7575
/*                            SetMercator()                             */
7576
/************************************************************************/
7577
7578
OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7579
                                        double dfScale, double dfFalseEasting,
7580
                                        double dfFalseNorthing)
7581
7582
0
{
7583
0
    TAKE_OPTIONAL_LOCK();
7584
7585
0
    if (dfCenterLat != 0.0 && dfScale == 1.0)
7586
0
    {
7587
        // Not sure this is correct, but this is how it has been used
7588
        // historically
7589
0
        return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7590
0
                              dfFalseNorthing);
7591
0
    }
7592
0
    return d->replaceConversionAndUnref(
7593
0
        proj_create_conversion_mercator_variant_a(
7594
0
            d->getPROJContext(),
7595
0
            dfCenterLat,  // should be zero
7596
0
            dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7597
0
            nullptr, 0));
7598
0
}
7599
7600
/************************************************************************/
7601
/*                           OSRSetMercator()                           */
7602
/************************************************************************/
7603
7604
OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7605
                      double dfCenterLong, double dfScale,
7606
                      double dfFalseEasting, double dfFalseNorthing)
7607
7608
0
{
7609
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7610
7611
0
    return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7612
0
                                        dfFalseEasting, dfFalseNorthing);
7613
0
}
7614
7615
/************************************************************************/
7616
/*                           SetMercator2SP()                           */
7617
/************************************************************************/
7618
7619
OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7620
                                           double dfCenterLong,
7621
                                           double dfFalseEasting,
7622
                                           double dfFalseNorthing)
7623
7624
0
{
7625
0
    if (dfCenterLat == 0.0)
7626
0
    {
7627
0
        return d->replaceConversionAndUnref(
7628
0
            proj_create_conversion_mercator_variant_b(
7629
0
                d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7630
0
                dfFalseNorthing, nullptr, 0, nullptr, 0));
7631
0
    }
7632
7633
0
    TAKE_OPTIONAL_LOCK();
7634
7635
0
    SetProjection(SRS_PT_MERCATOR_2SP);
7636
7637
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7638
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7639
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7640
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7641
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7642
7643
0
    return OGRERR_NONE;
7644
0
}
7645
7646
/************************************************************************/
7647
/*                         OSRSetMercator2SP()                          */
7648
/************************************************************************/
7649
7650
OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7651
                         double dfCenterLat, double dfCenterLong,
7652
                         double dfFalseEasting, double dfFalseNorthing)
7653
7654
0
{
7655
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7656
7657
0
    return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7658
0
                                           dfFalseEasting, dfFalseNorthing);
7659
0
}
7660
7661
/************************************************************************/
7662
/*                            SetMollweide()                            */
7663
/************************************************************************/
7664
7665
OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7666
                                         double dfFalseEasting,
7667
                                         double dfFalseNorthing)
7668
7669
0
{
7670
0
    TAKE_OPTIONAL_LOCK();
7671
7672
0
    return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7673
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7674
0
        nullptr, 0, nullptr, 0));
7675
0
}
7676
7677
/************************************************************************/
7678
/*                          OSRSetMollweide()                           */
7679
/************************************************************************/
7680
7681
OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7682
                       double dfFalseEasting, double dfFalseNorthing)
7683
7684
0
{
7685
0
    VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7686
7687
0
    return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7688
0
                                         dfFalseNorthing);
7689
0
}
7690
7691
/************************************************************************/
7692
/*                              SetNZMG()                               */
7693
/************************************************************************/
7694
7695
OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7696
                                    double dfFalseEasting,
7697
                                    double dfFalseNorthing)
7698
7699
0
{
7700
0
    TAKE_OPTIONAL_LOCK();
7701
7702
0
    return d->replaceConversionAndUnref(
7703
0
        proj_create_conversion_new_zealand_mapping_grid(
7704
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7705
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7706
0
}
7707
7708
/************************************************************************/
7709
/*                             OSRSetNZMG()                             */
7710
/************************************************************************/
7711
7712
OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7713
                  double dfCenterLong, double dfFalseEasting,
7714
                  double dfFalseNorthing)
7715
7716
0
{
7717
0
    VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7718
7719
0
    return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7720
0
                                    dfFalseNorthing);
7721
0
}
7722
7723
/************************************************************************/
7724
/*                               SetOS()                                */
7725
/************************************************************************/
7726
7727
OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7728
                                  double dfScale, double dfFalseEasting,
7729
                                  double dfFalseNorthing)
7730
7731
0
{
7732
0
    TAKE_OPTIONAL_LOCK();
7733
7734
0
    return d->replaceConversionAndUnref(
7735
0
        proj_create_conversion_oblique_stereographic(
7736
0
            d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7737
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7738
0
}
7739
7740
/************************************************************************/
7741
/*                              OSRSetOS()                              */
7742
/************************************************************************/
7743
7744
OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7745
                double dfCMeridian, double dfScale, double dfFalseEasting,
7746
                double dfFalseNorthing)
7747
7748
0
{
7749
0
    VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7750
7751
0
    return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7752
0
                                  dfFalseEasting, dfFalseNorthing);
7753
0
}
7754
7755
/************************************************************************/
7756
/*                          SetOrthographic()                           */
7757
/************************************************************************/
7758
7759
OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7760
                                            double dfCenterLong,
7761
                                            double dfFalseEasting,
7762
                                            double dfFalseNorthing)
7763
7764
0
{
7765
0
    TAKE_OPTIONAL_LOCK();
7766
7767
0
    return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7768
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7769
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7770
0
}
7771
7772
/************************************************************************/
7773
/*                         OSRSetOrthographic()                         */
7774
/************************************************************************/
7775
7776
OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7777
                          double dfCenterLong, double dfFalseEasting,
7778
                          double dfFalseNorthing)
7779
7780
0
{
7781
0
    VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7782
7783
0
    return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7784
0
                                            dfFalseEasting, dfFalseNorthing);
7785
0
}
7786
7787
/************************************************************************/
7788
/*                            SetPolyconic()                            */
7789
/************************************************************************/
7790
7791
OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7792
                                         double dfCenterLong,
7793
                                         double dfFalseEasting,
7794
                                         double dfFalseNorthing)
7795
7796
0
{
7797
0
    TAKE_OPTIONAL_LOCK();
7798
7799
    // note: it seems that by some definitions this should include a
7800
    //       scale_factor parameter.
7801
0
    return d->replaceConversionAndUnref(
7802
0
        proj_create_conversion_american_polyconic(
7803
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7804
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7805
0
}
7806
7807
/************************************************************************/
7808
/*                          OSRSetPolyconic()                           */
7809
/************************************************************************/
7810
7811
OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7812
                       double dfCenterLong, double dfFalseEasting,
7813
                       double dfFalseNorthing)
7814
7815
0
{
7816
0
    VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7817
7818
0
    return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7819
0
                                         dfFalseEasting, dfFalseNorthing);
7820
0
}
7821
7822
/************************************************************************/
7823
/*                               SetPS()                                */
7824
/************************************************************************/
7825
7826
/** Sets a Polar Stereographic projection.
7827
 *
7828
 * Two variants are possible:
7829
 * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7830
 *   interpreted as the latitude of origin, combined with the scale factor
7831
 * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7832
 *   is interpreted as the latitude of true scale. In that situation, dfScale
7833
 *   must be set to 1 (it is ignored in the projection parameters)
7834
 */
7835
OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7836
                                  double dfScale, double dfFalseEasting,
7837
                                  double dfFalseNorthing)
7838
7839
0
{
7840
0
    TAKE_OPTIONAL_LOCK();
7841
7842
0
    PJ *conv;
7843
0
    if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7844
0
    {
7845
0
        conv = proj_create_conversion_polar_stereographic_variant_b(
7846
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7847
0
            dfFalseNorthing, nullptr, 0, nullptr, 0);
7848
0
    }
7849
0
    else
7850
0
    {
7851
0
        conv = proj_create_conversion_polar_stereographic_variant_a(
7852
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7853
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7854
0
    }
7855
7856
0
    const char *pszName = nullptr;
7857
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7858
0
    CPLString osName = pszName ? pszName : "";
7859
7860
0
    d->refreshProjObj();
7861
7862
0
    d->demoteFromBoundCRS();
7863
7864
0
    auto cs = proj_create_cartesian_2D_cs(
7865
0
        d->getPROJContext(),
7866
0
        dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7867
0
                        : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7868
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7869
0
    auto projCRS =
7870
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7871
0
                                  d->getGeodBaseCRS(), conv, cs);
7872
0
    proj_destroy(conv);
7873
0
    proj_destroy(cs);
7874
7875
0
    d->setPjCRS(projCRS);
7876
7877
0
    d->undoDemoteFromBoundCRS();
7878
7879
0
    return OGRERR_NONE;
7880
0
}
7881
7882
/************************************************************************/
7883
/*                              OSRSetPS()                              */
7884
/************************************************************************/
7885
7886
OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7887
                double dfCenterLong, double dfScale, double dfFalseEasting,
7888
                double dfFalseNorthing)
7889
7890
0
{
7891
0
    VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7892
7893
0
    return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7894
0
                                  dfFalseEasting, dfFalseNorthing);
7895
0
}
7896
7897
/************************************************************************/
7898
/*                            SetRobinson()                             */
7899
/************************************************************************/
7900
7901
OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7902
                                        double dfFalseEasting,
7903
                                        double dfFalseNorthing)
7904
7905
0
{
7906
0
    TAKE_OPTIONAL_LOCK();
7907
7908
0
    return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7909
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7910
0
        nullptr, 0, nullptr, 0));
7911
0
}
7912
7913
/************************************************************************/
7914
/*                           OSRSetRobinson()                           */
7915
/************************************************************************/
7916
7917
OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7918
                      double dfFalseEasting, double dfFalseNorthing)
7919
7920
0
{
7921
0
    VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7922
7923
0
    return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7924
0
                                        dfFalseNorthing);
7925
0
}
7926
7927
/************************************************************************/
7928
/*                           SetSinusoidal()                            */
7929
/************************************************************************/
7930
7931
OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7932
                                          double dfFalseEasting,
7933
                                          double dfFalseNorthing)
7934
7935
0
{
7936
0
    TAKE_OPTIONAL_LOCK();
7937
7938
0
    return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7939
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7940
0
        nullptr, 0, nullptr, 0));
7941
0
}
7942
7943
/************************************************************************/
7944
/*                          OSRSetSinusoidal()                          */
7945
/************************************************************************/
7946
7947
OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7948
                        double dfFalseEasting, double dfFalseNorthing)
7949
7950
0
{
7951
0
    VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7952
7953
0
    return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7954
0
                                          dfFalseNorthing);
7955
0
}
7956
7957
/************************************************************************/
7958
/*                          SetStereographic()                          */
7959
/************************************************************************/
7960
7961
OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7962
                                             double dfCMeridian, double dfScale,
7963
                                             double dfFalseEasting,
7964
                                             double dfFalseNorthing)
7965
7966
0
{
7967
0
    TAKE_OPTIONAL_LOCK();
7968
7969
0
    return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7970
0
        d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7971
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7972
0
}
7973
7974
/************************************************************************/
7975
/*                        OSRSetStereographic()                         */
7976
/************************************************************************/
7977
7978
OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7979
                           double dfCMeridian, double dfScale,
7980
                           double dfFalseEasting, double dfFalseNorthing)
7981
7982
0
{
7983
0
    VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7984
7985
0
    return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7986
0
                                             dfFalseEasting, dfFalseNorthing);
7987
0
}
7988
7989
/************************************************************************/
7990
/*                               SetSOC()                               */
7991
/*                                                                      */
7992
/*      NOTE: This definition isn't really used in practice any more    */
7993
/*      and should be considered deprecated.  It seems that swiss       */
7994
/*      oblique mercator is now define as Hotine_Oblique_Mercator       */
7995
/*      with an azimuth of 90 and a rectified_grid_angle of 90.  See    */
7996
/*      EPSG:2056 and Bug 423.                                          */
7997
/************************************************************************/
7998
7999
OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8000
                                   double dfCentralMeridian,
8001
                                   double dfFalseEasting,
8002
                                   double dfFalseNorthing)
8003
8004
0
{
8005
0
    TAKE_OPTIONAL_LOCK();
8006
8007
0
    return d->replaceConversionAndUnref(
8008
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
8009
0
            d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8010
0
            90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8011
0
            0.0));
8012
#if 0
8013
    SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8014
    SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8015
    SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8016
    SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8017
    SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8018
8019
    return OGRERR_NONE;
8020
#endif
8021
0
}
8022
8023
/************************************************************************/
8024
/*                             OSRSetSOC()                              */
8025
/************************************************************************/
8026
8027
OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8028
                 double dfCentralMeridian, double dfFalseEasting,
8029
                 double dfFalseNorthing)
8030
8031
0
{
8032
0
    VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8033
8034
0
    return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8035
0
                                   dfFalseEasting, dfFalseNorthing);
8036
0
}
8037
8038
/************************************************************************/
8039
/*                               SetVDG()                               */
8040
/************************************************************************/
8041
8042
OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8043
                                   double dfFalseNorthing)
8044
8045
0
{
8046
0
    TAKE_OPTIONAL_LOCK();
8047
8048
0
    return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8049
0
        d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8050
0
        nullptr, 0, nullptr, 0));
8051
0
}
8052
8053
/************************************************************************/
8054
/*                             OSRSetVDG()                              */
8055
/************************************************************************/
8056
8057
OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8058
                 double dfFalseEasting, double dfFalseNorthing)
8059
8060
0
{
8061
0
    VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8062
8063
0
    return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8064
0
                                   dfFalseNorthing);
8065
0
}
8066
8067
/************************************************************************/
8068
/*                               SetUTM()                               */
8069
/************************************************************************/
8070
8071
/**
8072
 * \brief Set UTM projection definition.
8073
 *
8074
 * This will generate a projection definition with the full set of
8075
 * transverse mercator projection parameters for the given UTM zone.
8076
 * If no PROJCS[] description is set yet, one will be set to look
8077
 * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8078
 *
8079
 * This method is the same as the C function OSRSetUTM().
8080
 *
8081
 * @param nZone UTM zone.
8082
 *
8083
 * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8084
 * hemisphere.
8085
 *
8086
 * @return OGRERR_NONE on success.
8087
 */
8088
8089
OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8090
8091
0
{
8092
0
    TAKE_OPTIONAL_LOCK();
8093
8094
0
    if (nZone < 0 || nZone > 60)
8095
0
    {
8096
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8097
0
        return OGRERR_FAILURE;
8098
0
    }
8099
8100
0
    return d->replaceConversionAndUnref(
8101
0
        proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8102
0
}
8103
8104
/************************************************************************/
8105
/*                             OSRSetUTM()                              */
8106
/************************************************************************/
8107
8108
/**
8109
 * \brief Set UTM projection definition.
8110
 *
8111
 * This is the same as the C++ method OGRSpatialReference::SetUTM()
8112
 */
8113
OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8114
8115
0
{
8116
0
    VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8117
8118
0
    return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8119
0
}
8120
8121
/************************************************************************/
8122
/*                             GetUTMZone()                             */
8123
/*                                                                      */
8124
/*      Returns zero if it isn't UTM.                                   */
8125
/************************************************************************/
8126
8127
/**
8128
 * \brief Get utm zone information.
8129
 *
8130
 * This is the same as the C function OSRGetUTMZone().
8131
 *
8132
 * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8133
 * zone which is negative in the southern hemisphere instead of having the
8134
 * pbNorth flag used in the C and C++ interface.
8135
 *
8136
 * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8137
 * FALSE if southern.
8138
 *
8139
 * @return UTM zone number or zero if this isn't a UTM definition.
8140
 */
8141
8142
int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8143
8144
0
{
8145
0
    TAKE_OPTIONAL_LOCK();
8146
8147
0
    if (IsProjected() && GetAxesCount() == 3)
8148
0
    {
8149
0
        OGRSpatialReference *poSRSTmp = Clone();
8150
0
        poSRSTmp->DemoteTo2D(nullptr);
8151
0
        const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8152
0
        delete poSRSTmp;
8153
0
        return nZone;
8154
0
    }
8155
8156
0
    const char *pszProjection = GetAttrValue("PROJECTION");
8157
8158
0
    if (pszProjection == nullptr ||
8159
0
        !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8160
0
        return 0;
8161
8162
0
    if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8163
0
        return 0;
8164
8165
0
    if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8166
0
        return 0;
8167
8168
0
    if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8169
0
        return 0;
8170
8171
0
    const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8172
8173
0
    if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8174
0
        return 0;
8175
8176
0
    if (pbNorth != nullptr)
8177
0
        *pbNorth = (dfFalseNorthing == 0);
8178
8179
0
    const double dfCentralMeridian =
8180
0
        GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8181
0
    const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8182
8183
0
    if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8184
0
        std::isnan(dfZone) ||
8185
0
        std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8186
0
        return 0;
8187
8188
0
    return static_cast<int>(dfZone);
8189
0
}
8190
8191
/************************************************************************/
8192
/*                           OSRGetUTMZone()                            */
8193
/************************************************************************/
8194
8195
/**
8196
 * \brief Get utm zone information.
8197
 *
8198
 * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8199
 */
8200
int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8201
8202
0
{
8203
0
    VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8204
8205
0
    return ToPointer(hSRS)->GetUTMZone(pbNorth);
8206
0
}
8207
8208
/************************************************************************/
8209
/*                             SetWagner()                              */
8210
/************************************************************************/
8211
8212
OGRErr OGRSpatialReference::SetWagner(int nVariation,  // 1--7.
8213
                                      double dfCenterLat, double dfFalseEasting,
8214
                                      double dfFalseNorthing)
8215
8216
0
{
8217
0
    TAKE_OPTIONAL_LOCK();
8218
8219
0
    PJ *conv;
8220
0
    if (nVariation == 1)
8221
0
    {
8222
0
        conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8223
0
                                               dfFalseEasting, dfFalseNorthing,
8224
0
                                               nullptr, 0.0, nullptr, 0.0);
8225
0
    }
8226
0
    else if (nVariation == 2)
8227
0
    {
8228
0
        conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8229
0
                                                dfFalseEasting, dfFalseNorthing,
8230
0
                                                nullptr, 0.0, nullptr, 0.0);
8231
0
    }
8232
0
    else if (nVariation == 3)
8233
0
    {
8234
0
        conv = proj_create_conversion_wagner_iii(
8235
0
            d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8236
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8237
0
    }
8238
0
    else if (nVariation == 4)
8239
0
    {
8240
0
        conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8241
0
                                                dfFalseEasting, dfFalseNorthing,
8242
0
                                                nullptr, 0.0, nullptr, 0.0);
8243
0
    }
8244
0
    else if (nVariation == 5)
8245
0
    {
8246
0
        conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8247
0
                                               dfFalseEasting, dfFalseNorthing,
8248
0
                                               nullptr, 0.0, nullptr, 0.0);
8249
0
    }
8250
0
    else if (nVariation == 6)
8251
0
    {
8252
0
        conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8253
0
                                                dfFalseEasting, dfFalseNorthing,
8254
0
                                                nullptr, 0.0, nullptr, 0.0);
8255
0
    }
8256
0
    else if (nVariation == 7)
8257
0
    {
8258
0
        conv = proj_create_conversion_wagner_vii(
8259
0
            d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8260
0
            0.0, nullptr, 0.0);
8261
0
    }
8262
0
    else
8263
0
    {
8264
0
        CPLError(CE_Failure, CPLE_AppDefined,
8265
0
                 "Unsupported Wagner variation (%d).", nVariation);
8266
0
        return OGRERR_UNSUPPORTED_SRS;
8267
0
    }
8268
8269
0
    return d->replaceConversionAndUnref(conv);
8270
0
}
8271
8272
/************************************************************************/
8273
/*                            OSRSetWagner()                            */
8274
/************************************************************************/
8275
8276
OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8277
                    double dfCenterLat, double dfFalseEasting,
8278
                    double dfFalseNorthing)
8279
8280
0
{
8281
0
    VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8282
8283
0
    return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8284
0
                                      dfFalseNorthing);
8285
0
}
8286
8287
/************************************************************************/
8288
/*                            SetQSC()                     */
8289
/************************************************************************/
8290
8291
OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8292
0
{
8293
0
    TAKE_OPTIONAL_LOCK();
8294
8295
0
    return d->replaceConversionAndUnref(
8296
0
        proj_create_conversion_quadrilateralized_spherical_cube(
8297
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8298
0
            0, nullptr, 0));
8299
0
}
8300
8301
/************************************************************************/
8302
/*                           OSRSetQSC()                   */
8303
/************************************************************************/
8304
8305
OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8306
                 double dfCenterLong)
8307
8308
0
{
8309
0
    VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8310
8311
0
    return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8312
0
}
8313
8314
/************************************************************************/
8315
/*                            SetSCH()                     */
8316
/************************************************************************/
8317
8318
OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8319
                                   double dfPegHeading, double dfPegHgt)
8320
8321
0
{
8322
0
    TAKE_OPTIONAL_LOCK();
8323
8324
0
    return d->replaceConversionAndUnref(
8325
0
        proj_create_conversion_spherical_cross_track_height(
8326
0
            d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8327
0
            nullptr, 0, nullptr, 0));
8328
0
}
8329
8330
/************************************************************************/
8331
/*                           OSRSetSCH()                   */
8332
/************************************************************************/
8333
8334
OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8335
                 double dfPegHeading, double dfPegHgt)
8336
8337
0
{
8338
0
    VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8339
8340
0
    return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8341
0
}
8342
8343
/************************************************************************/
8344
/*                         SetVerticalPerspective()                     */
8345
/************************************************************************/
8346
8347
OGRErr OGRSpatialReference::SetVerticalPerspective(
8348
    double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8349
    double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8350
0
{
8351
0
    TAKE_OPTIONAL_LOCK();
8352
8353
0
    return d->replaceConversionAndUnref(
8354
0
        proj_create_conversion_vertical_perspective(
8355
0
            d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8356
0
            dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8357
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
8358
0
}
8359
8360
/************************************************************************/
8361
/*                       OSRSetVerticalPerspective()                    */
8362
/************************************************************************/
8363
8364
OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8365
                                 double dfTopoOriginLat, double dfTopoOriginLon,
8366
                                 double dfTopoOriginHeight,
8367
                                 double dfViewPointHeight,
8368
                                 double dfFalseEasting, double dfFalseNorthing)
8369
8370
0
{
8371
0
    VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8372
8373
0
    return ToPointer(hSRS)->SetVerticalPerspective(
8374
0
        dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8375
0
        dfFalseEasting, dfFalseNorthing);
8376
0
}
8377
8378
/************************************************************************/
8379
/*             SetDerivedGeogCRSWithPoleRotationGRIBConvention()        */
8380
/************************************************************************/
8381
8382
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8383
    const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8384
    double dfAxisRotation)
8385
0
{
8386
0
    TAKE_OPTIONAL_LOCK();
8387
8388
0
    d->refreshProjObj();
8389
0
    if (!d->m_pj_crs)
8390
0
        return OGRERR_FAILURE;
8391
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8392
0
        return OGRERR_FAILURE;
8393
0
    auto ctxt = d->getPROJContext();
8394
0
    auto conv = proj_create_conversion_pole_rotation_grib_convention(
8395
0
        ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8396
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8397
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8398
0
                                                   d->m_pj_crs, conv, cs));
8399
0
    proj_destroy(conv);
8400
0
    proj_destroy(cs);
8401
0
    return OGRERR_NONE;
8402
0
}
8403
8404
/************************************************************************/
8405
/*         SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention()        */
8406
/************************************************************************/
8407
8408
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8409
    const char *pszCRSName, double dfGridNorthPoleLat,
8410
    double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8411
0
{
8412
0
    TAKE_OPTIONAL_LOCK();
8413
8414
0
#if PROJ_VERSION_MAJOR > 8 ||                                                  \
8415
0
    (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8416
0
    d->refreshProjObj();
8417
0
    if (!d->m_pj_crs)
8418
0
        return OGRERR_FAILURE;
8419
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8420
0
        return OGRERR_FAILURE;
8421
0
    auto ctxt = d->getPROJContext();
8422
0
    auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8423
0
        ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8424
0
        nullptr, 0);
8425
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8426
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8427
0
                                                   d->m_pj_crs, conv, cs));
8428
0
    proj_destroy(conv);
8429
0
    proj_destroy(cs);
8430
0
    return OGRERR_NONE;
8431
#else
8432
    (void)pszCRSName;
8433
    SetProjection("Rotated_pole");
8434
    SetExtension(
8435
        "PROJCS", "PROJ4",
8436
        CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8437
                   "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8438
                   "+to_meter=0.0174532925199433 "
8439
                   "+wktext",
8440
                   180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8441
                   dfGridNorthPoleLat, GetSemiMajor(nullptr),
8442
                   GetSemiMinor(nullptr)));
8443
    return OGRERR_NONE;
8444
#endif
8445
0
}
8446
8447
/************************************************************************/
8448
/*                            SetAuthority()                            */
8449
/************************************************************************/
8450
8451
/**
8452
 * \brief Set the authority for a node.
8453
 *
8454
 * This method is the same as the C function OSRSetAuthority().
8455
 *
8456
 * @param pszTargetKey the partial or complete path to the node to
8457
 * set an authority on.  i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8458
 *
8459
 * @param pszAuthority authority name, such as "EPSG".
8460
 *
8461
 * @param nCode code for value with this authority.
8462
 *
8463
 * @return OGRERR_NONE on success.
8464
 */
8465
8466
OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8467
                                         const char *pszAuthority, int nCode)
8468
8469
0
{
8470
0
    TAKE_OPTIONAL_LOCK();
8471
8472
0
    d->refreshProjObj();
8473
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8474
8475
0
    if (pszTargetKey == nullptr)
8476
0
    {
8477
0
        if (!d->m_pj_crs)
8478
0
            return OGRERR_FAILURE;
8479
0
        CPLString osCode;
8480
0
        osCode.Printf("%d", nCode);
8481
0
        d->demoteFromBoundCRS();
8482
0
        d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8483
0
                                  pszAuthority, osCode.c_str()));
8484
0
        d->undoDemoteFromBoundCRS();
8485
0
        return OGRERR_NONE;
8486
0
    }
8487
8488
0
    d->demoteFromBoundCRS();
8489
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8490
0
    {
8491
0
        CPLString osCode;
8492
0
        osCode.Printf("%d", nCode);
8493
0
        auto newGeogCRS =
8494
0
            proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8495
0
                          pszAuthority, osCode.c_str());
8496
8497
0
        auto conv =
8498
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8499
8500
0
        auto projCRS = proj_create_projected_crs(
8501
0
            d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8502
0
            d->getProjCRSCoordSys());
8503
8504
        // Preserve existing id on the PROJCRS
8505
0
        const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8506
0
        const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8507
0
        if (pszProjCRSAuthName && pszProjCRSCode)
8508
0
        {
8509
0
            auto projCRSWithId =
8510
0
                proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8511
0
                              pszProjCRSCode);
8512
0
            proj_destroy(projCRS);
8513
0
            projCRS = projCRSWithId;
8514
0
        }
8515
8516
0
        proj_destroy(newGeogCRS);
8517
0
        proj_destroy(conv);
8518
8519
0
        d->setPjCRS(projCRS);
8520
0
        d->undoDemoteFromBoundCRS();
8521
0
        return OGRERR_NONE;
8522
0
    }
8523
0
    d->undoDemoteFromBoundCRS();
8524
8525
    /* -------------------------------------------------------------------- */
8526
    /*      Find the node below which the authority should be put.          */
8527
    /* -------------------------------------------------------------------- */
8528
0
    OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8529
8530
0
    if (poNode == nullptr)
8531
0
        return OGRERR_FAILURE;
8532
8533
    /* -------------------------------------------------------------------- */
8534
    /*      If there is an existing AUTHORITY child blow it away before     */
8535
    /*      trying to set a new one.                                        */
8536
    /* -------------------------------------------------------------------- */
8537
0
    int iOldChild = poNode->FindChild("AUTHORITY");
8538
0
    if (iOldChild != -1)
8539
0
        poNode->DestroyChild(iOldChild);
8540
8541
    /* -------------------------------------------------------------------- */
8542
    /*      Create a new authority node.                                    */
8543
    /* -------------------------------------------------------------------- */
8544
0
    char szCode[32] = {};
8545
8546
0
    snprintf(szCode, sizeof(szCode), "%d", nCode);
8547
8548
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8549
0
    poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8550
0
    poAuthNode->AddChild(new OGR_SRSNode(szCode));
8551
8552
0
    poNode->AddChild(poAuthNode);
8553
8554
0
    return OGRERR_NONE;
8555
0
}
8556
8557
/************************************************************************/
8558
/*                          OSRSetAuthority()                           */
8559
/************************************************************************/
8560
8561
/**
8562
 * \brief Set the authority for a node.
8563
 *
8564
 * This function is the same as OGRSpatialReference::SetAuthority().
8565
 */
8566
OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8567
                       const char *pszAuthority, int nCode)
8568
8569
0
{
8570
0
    VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8571
8572
0
    return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8573
0
}
8574
8575
/************************************************************************/
8576
/*                          GetAuthorityCode()                          */
8577
/************************************************************************/
8578
8579
/**
8580
 * \brief Get the authority code for a node.
8581
 *
8582
 * This method is used to query an AUTHORITY[] node from within the
8583
 * WKT tree, and fetch the code value.
8584
 *
8585
 * While in theory values may be non-numeric, for the EPSG authority all
8586
 * code values should be integral.
8587
 *
8588
 * This method is the same as the C function OSRGetAuthorityCode().
8589
 *
8590
 * @param pszTargetKey the partial or complete path to the node to
8591
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8592
 * search for an authority node on the root element.
8593
 *
8594
 * @return value code from authority node, or NULL on failure.  The value
8595
 * returned is internal and should not be freed or modified.
8596
 */
8597
8598
const char *
8599
OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8600
8601
0
{
8602
0
    TAKE_OPTIONAL_LOCK();
8603
8604
0
    d->refreshProjObj();
8605
0
    const char *pszInputTargetKey = pszTargetKey;
8606
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8607
0
    if (pszTargetKey == nullptr)
8608
0
    {
8609
0
        if (!d->m_pj_crs)
8610
0
        {
8611
0
            return nullptr;
8612
0
        }
8613
0
        d->demoteFromBoundCRS();
8614
0
        auto ret = proj_get_id_code(d->m_pj_crs, 0);
8615
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8616
0
        {
8617
0
            auto ctxt = d->getPROJContext();
8618
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8619
0
            if (cs)
8620
0
            {
8621
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8622
0
                proj_destroy(cs);
8623
0
                if (axisCount == 3)
8624
0
                {
8625
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8626
                    // 2002 in which case, using the WKT1 representation will
8627
                    // enable us to recover the EPSG code.
8628
0
                    pszTargetKey = pszInputTargetKey;
8629
0
                }
8630
0
            }
8631
0
        }
8632
0
        d->undoDemoteFromBoundCRS();
8633
0
        if (ret != nullptr || pszTargetKey == nullptr)
8634
0
        {
8635
0
            return ret;
8636
0
        }
8637
0
    }
8638
8639
    // Special key for that context
8640
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8641
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8642
0
    {
8643
0
        auto ctxt = d->getPROJContext();
8644
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8645
0
        if (crs)
8646
0
        {
8647
0
            const char *ret = proj_get_id_code(crs, 0);
8648
0
            if (ret)
8649
0
                ret = CPLSPrintf("%s", ret);
8650
0
            proj_destroy(crs);
8651
0
            return ret;
8652
0
        }
8653
0
    }
8654
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8655
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8656
0
    {
8657
0
        auto ctxt = d->getPROJContext();
8658
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8659
0
        if (crs)
8660
0
        {
8661
0
            const char *ret = proj_get_id_code(crs, 0);
8662
0
            if (ret)
8663
0
                ret = CPLSPrintf("%s", ret);
8664
0
            proj_destroy(crs);
8665
0
            return ret;
8666
0
        }
8667
0
    }
8668
8669
    /* -------------------------------------------------------------------- */
8670
    /*      Find the node below which the authority should be put.          */
8671
    /* -------------------------------------------------------------------- */
8672
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8673
8674
0
    if (poNode == nullptr)
8675
0
        return nullptr;
8676
8677
    /* -------------------------------------------------------------------- */
8678
    /*      Fetch AUTHORITY child if there is one.                          */
8679
    /* -------------------------------------------------------------------- */
8680
0
    if (poNode->FindChild("AUTHORITY") == -1)
8681
0
        return nullptr;
8682
8683
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8684
8685
    /* -------------------------------------------------------------------- */
8686
    /*      Create a new authority node.                                    */
8687
    /* -------------------------------------------------------------------- */
8688
0
    if (poNode->GetChildCount() < 2)
8689
0
        return nullptr;
8690
8691
0
    return poNode->GetChild(1)->GetValue();
8692
0
}
8693
8694
/************************************************************************/
8695
/*                          OSRGetAuthorityCode()                       */
8696
/************************************************************************/
8697
8698
/**
8699
 * \brief Get the authority code for a node.
8700
 *
8701
 * This function is the same as OGRSpatialReference::GetAuthorityCode().
8702
 */
8703
const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8704
                                const char *pszTargetKey)
8705
8706
0
{
8707
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8708
8709
0
    return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8710
0
}
8711
8712
/************************************************************************/
8713
/*                          GetAuthorityName()                          */
8714
/************************************************************************/
8715
8716
/**
8717
 * \brief Get the authority name for a node.
8718
 *
8719
 * This method is used to query an AUTHORITY[] node from within the
8720
 * WKT tree, and fetch the authority name value.
8721
 *
8722
 * The most common authority is "EPSG".
8723
 *
8724
 * This method is the same as the C function OSRGetAuthorityName().
8725
 *
8726
 * @param pszTargetKey the partial or complete path to the node to
8727
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8728
 * search for an authority node on the root element.
8729
 *
8730
 * @return value code from authority node, or NULL on failure. The value
8731
 * returned is internal and should not be freed or modified.
8732
 */
8733
8734
const char *
8735
OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8736
8737
0
{
8738
0
    TAKE_OPTIONAL_LOCK();
8739
8740
0
    d->refreshProjObj();
8741
0
    const char *pszInputTargetKey = pszTargetKey;
8742
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8743
0
    if (pszTargetKey == nullptr)
8744
0
    {
8745
0
        if (!d->m_pj_crs)
8746
0
        {
8747
0
            return nullptr;
8748
0
        }
8749
0
        d->demoteFromBoundCRS();
8750
0
        auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8751
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8752
0
        {
8753
0
            auto ctxt = d->getPROJContext();
8754
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8755
0
            if (cs)
8756
0
            {
8757
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8758
0
                proj_destroy(cs);
8759
0
                if (axisCount == 3)
8760
0
                {
8761
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8762
                    // 2002 in which case, using the WKT1 representation will
8763
                    // enable us to recover the EPSG code.
8764
0
                    pszTargetKey = pszInputTargetKey;
8765
0
                }
8766
0
            }
8767
0
        }
8768
0
        d->undoDemoteFromBoundCRS();
8769
0
        if (ret != nullptr || pszTargetKey == nullptr)
8770
0
        {
8771
0
            return ret;
8772
0
        }
8773
0
    }
8774
8775
    // Special key for that context
8776
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8777
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8778
0
    {
8779
0
        auto ctxt = d->getPROJContext();
8780
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8781
0
        if (crs)
8782
0
        {
8783
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8784
0
            if (ret)
8785
0
                ret = CPLSPrintf("%s", ret);
8786
0
            proj_destroy(crs);
8787
0
            return ret;
8788
0
        }
8789
0
    }
8790
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8791
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8792
0
    {
8793
0
        auto ctxt = d->getPROJContext();
8794
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8795
0
        if (crs)
8796
0
        {
8797
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8798
0
            if (ret)
8799
0
                ret = CPLSPrintf("%s", ret);
8800
0
            proj_destroy(crs);
8801
0
            return ret;
8802
0
        }
8803
0
    }
8804
8805
    /* -------------------------------------------------------------------- */
8806
    /*      Find the node below which the authority should be put.          */
8807
    /* -------------------------------------------------------------------- */
8808
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8809
8810
0
    if (poNode == nullptr)
8811
0
        return nullptr;
8812
8813
    /* -------------------------------------------------------------------- */
8814
    /*      Fetch AUTHORITY child if there is one.                          */
8815
    /* -------------------------------------------------------------------- */
8816
0
    if (poNode->FindChild("AUTHORITY") == -1)
8817
0
        return nullptr;
8818
8819
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8820
8821
    /* -------------------------------------------------------------------- */
8822
    /*      Create a new authority node.                                    */
8823
    /* -------------------------------------------------------------------- */
8824
0
    if (poNode->GetChildCount() < 2)
8825
0
        return nullptr;
8826
8827
0
    return poNode->GetChild(0)->GetValue();
8828
0
}
8829
8830
/************************************************************************/
8831
/*                        OSRGetAuthorityName()                         */
8832
/************************************************************************/
8833
8834
/**
8835
 * \brief Get the authority name for a node.
8836
 *
8837
 * This function is the same as OGRSpatialReference::GetAuthorityName().
8838
 */
8839
const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8840
                                const char *pszTargetKey)
8841
8842
0
{
8843
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8844
8845
0
    return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8846
0
}
8847
8848
/************************************************************************/
8849
/*                          GetOGCURN()                                 */
8850
/************************************************************************/
8851
8852
/**
8853
 * \brief Get a OGC URN string describing the CRS, when possible
8854
 *
8855
 * This method assumes that the CRS has a top-level identifier, or is
8856
 * a compound CRS whose horizontal and vertical parts have a top-level
8857
 * identifier.
8858
 *
8859
 * @return a string to free with CPLFree(), or nullptr when no result can be
8860
 * generated
8861
 *
8862
 * @since GDAL 3.5
8863
 */
8864
8865
char *OGRSpatialReference::GetOGCURN() const
8866
8867
0
{
8868
0
    TAKE_OPTIONAL_LOCK();
8869
8870
0
    const char *pszAuthName = GetAuthorityName(nullptr);
8871
0
    const char *pszAuthCode = GetAuthorityCode(nullptr);
8872
0
    if (pszAuthName && pszAuthCode)
8873
0
        return CPLStrdup(
8874
0
            CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8875
0
    if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8876
0
        return nullptr;
8877
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8878
0
    auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8879
0
    char *pszRet = nullptr;
8880
0
    if (horizCRS && vertCRS)
8881
0
    {
8882
0
        auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8883
0
        auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8884
0
        auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8885
0
        auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8886
0
        if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8887
0
        {
8888
0
            pszRet = CPLStrdup(CPLSPrintf(
8889
0
                "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8890
0
                horizAuthCode, vertAuthName, vertAuthCode));
8891
0
        }
8892
0
    }
8893
0
    proj_destroy(horizCRS);
8894
0
    proj_destroy(vertCRS);
8895
0
    return pszRet;
8896
0
}
8897
8898
/************************************************************************/
8899
/*                           StripVertical()                            */
8900
/************************************************************************/
8901
8902
/**
8903
 * \brief Convert a compound cs into a horizontal CS.
8904
 *
8905
 * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8906
 * nodes are stripped resulting and only the horizontal coordinate system
8907
 * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8908
 *
8909
 * If this is not a compound coordinate system then nothing is changed.
8910
 *
8911
 * This method is the same as the C function OSRStripVertical().
8912
 *
8913
 * @since OGR 1.8.0
8914
 */
8915
8916
OGRErr OGRSpatialReference::StripVertical()
8917
8918
0
{
8919
0
    TAKE_OPTIONAL_LOCK();
8920
8921
0
    d->refreshProjObj();
8922
0
    d->demoteFromBoundCRS();
8923
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8924
0
    {
8925
0
        d->undoDemoteFromBoundCRS();
8926
0
        return OGRERR_NONE;
8927
0
    }
8928
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8929
0
    if (!horizCRS)
8930
0
    {
8931
0
        d->undoDemoteFromBoundCRS();
8932
0
        return OGRERR_FAILURE;
8933
0
    }
8934
8935
0
    bool reuseExistingBoundCRS = false;
8936
0
    if (d->m_pj_bound_crs_target)
8937
0
    {
8938
0
        auto type = proj_get_type(d->m_pj_bound_crs_target);
8939
0
        reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8940
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8941
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8942
0
    }
8943
8944
0
    if (reuseExistingBoundCRS)
8945
0
    {
8946
0
        auto newBoundCRS = proj_crs_create_bound_crs(
8947
0
            d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8948
0
            d->m_pj_bound_crs_co);
8949
0
        proj_destroy(horizCRS);
8950
0
        d->undoDemoteFromBoundCRS();
8951
0
        d->setPjCRS(newBoundCRS);
8952
0
    }
8953
0
    else
8954
0
    {
8955
0
        d->undoDemoteFromBoundCRS();
8956
0
        d->setPjCRS(horizCRS);
8957
0
    }
8958
8959
0
    return OGRERR_NONE;
8960
0
}
8961
8962
/************************************************************************/
8963
/*                            OSRStripVertical()                             */
8964
/************************************************************************/
8965
/**
8966
 * \brief Convert a compound cs into a horizontal CS.
8967
 *
8968
 * This function is the same as the C++ method
8969
 * OGRSpatialReference::StripVertical().
8970
 */
8971
OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8972
8973
0
{
8974
0
    VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8975
8976
0
    return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8977
0
}
8978
8979
/************************************************************************/
8980
/*                   StripTOWGS84IfKnownDatumAndAllowed()               */
8981
/************************************************************************/
8982
8983
/**
8984
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8985
 *        and this is allowed by the user.
8986
 *
8987
 * The default behavior is to remove TOWGS84 information if the CRS has a
8988
 * known horizontal datum. This can be disabled by setting the
8989
 * OSR_STRIP_TOWGS84 configuration option to NO.
8990
 *
8991
 * @return true if TOWGS84 has been removed.
8992
 * @since OGR 3.1.0
8993
 */
8994
8995
bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8996
0
{
8997
0
    if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8998
0
    {
8999
0
        if (StripTOWGS84IfKnownDatum())
9000
0
        {
9001
0
            CPLDebug("OSR", "TOWGS84 information has been removed. "
9002
0
                            "It can be kept by setting the OSR_STRIP_TOWGS84 "
9003
0
                            "configuration option to NO");
9004
0
            return true;
9005
0
        }
9006
0
    }
9007
0
    return false;
9008
0
}
9009
9010
/************************************************************************/
9011
/*                      StripTOWGS84IfKnownDatum()                      */
9012
/************************************************************************/
9013
9014
/**
9015
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9016
 *
9017
 * @return true if TOWGS84 has been removed.
9018
 * @since OGR 3.1.0
9019
 */
9020
9021
bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9022
9023
0
{
9024
0
    TAKE_OPTIONAL_LOCK();
9025
9026
0
    d->refreshProjObj();
9027
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9028
0
    {
9029
0
        return false;
9030
0
    }
9031
0
    auto ctxt = d->getPROJContext();
9032
0
    auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9033
0
    if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9034
0
    {
9035
0
        proj_destroy(baseCRS);
9036
0
        return false;
9037
0
    }
9038
9039
    // Known base CRS code ? Return base CRS
9040
0
    const char *pszCode = proj_get_id_code(baseCRS, 0);
9041
0
    if (pszCode)
9042
0
    {
9043
0
        d->setPjCRS(baseCRS);
9044
0
        return true;
9045
0
    }
9046
9047
0
    auto datum = proj_crs_get_datum(ctxt, baseCRS);
9048
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9049
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9050
0
    if (datum == nullptr)
9051
0
    {
9052
0
        datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9053
0
    }
9054
0
#endif
9055
0
    if (!datum)
9056
0
    {
9057
0
        proj_destroy(baseCRS);
9058
0
        return false;
9059
0
    }
9060
9061
    // Known datum code ? Return base CRS
9062
0
    pszCode = proj_get_id_code(datum, 0);
9063
0
    if (pszCode)
9064
0
    {
9065
0
        proj_destroy(datum);
9066
0
        d->setPjCRS(baseCRS);
9067
0
        return true;
9068
0
    }
9069
9070
0
    const char *name = proj_get_name(datum);
9071
0
    if (EQUAL(name, "unknown"))
9072
0
    {
9073
0
        proj_destroy(datum);
9074
0
        proj_destroy(baseCRS);
9075
0
        return false;
9076
0
    }
9077
0
    const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9078
0
    PJ_OBJ_LIST *list =
9079
0
        proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9080
9081
0
    bool knownDatumName = false;
9082
0
    if (list)
9083
0
    {
9084
0
        if (proj_list_get_count(list) == 1)
9085
0
        {
9086
0
            knownDatumName = true;
9087
0
        }
9088
0
        proj_list_destroy(list);
9089
0
    }
9090
9091
0
    proj_destroy(datum);
9092
0
    if (knownDatumName)
9093
0
    {
9094
0
        d->setPjCRS(baseCRS);
9095
0
        return true;
9096
0
    }
9097
0
    proj_destroy(baseCRS);
9098
0
    return false;
9099
0
}
9100
9101
/************************************************************************/
9102
/*                             IsCompound()                             */
9103
/************************************************************************/
9104
9105
/**
9106
 * \brief Check if coordinate system is compound.
9107
 *
9108
 * This method is the same as the C function OSRIsCompound().
9109
 *
9110
 * @return TRUE if this is rooted with a COMPD_CS node.
9111
 */
9112
9113
int OGRSpatialReference::IsCompound() const
9114
9115
0
{
9116
0
    TAKE_OPTIONAL_LOCK();
9117
9118
0
    d->refreshProjObj();
9119
0
    d->demoteFromBoundCRS();
9120
0
    bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9121
0
    d->undoDemoteFromBoundCRS();
9122
0
    return isCompound;
9123
0
}
9124
9125
/************************************************************************/
9126
/*                           OSRIsCompound()                            */
9127
/************************************************************************/
9128
9129
/**
9130
 * \brief Check if the coordinate system is compound.
9131
 *
9132
 * This function is the same as OGRSpatialReference::IsCompound().
9133
 */
9134
int OSRIsCompound(OGRSpatialReferenceH hSRS)
9135
9136
0
{
9137
0
    VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9138
9139
0
    return ToPointer(hSRS)->IsCompound();
9140
0
}
9141
9142
/************************************************************************/
9143
/*                            IsProjected()                             */
9144
/************************************************************************/
9145
9146
/**
9147
 * \brief Check if projected coordinate system.
9148
 *
9149
 * This method is the same as the C function OSRIsProjected().
9150
 *
9151
 * @return TRUE if this contains a PROJCS node indicating a it is a
9152
 * projected coordinate system. Also if it is a CompoundCRS made of a
9153
 * ProjectedCRS
9154
 */
9155
9156
int OGRSpatialReference::IsProjected() const
9157
9158
0
{
9159
0
    TAKE_OPTIONAL_LOCK();
9160
9161
0
    d->refreshProjObj();
9162
0
    d->demoteFromBoundCRS();
9163
0
    bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9164
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9165
0
    {
9166
0
        auto horizCRS =
9167
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9168
0
        if (horizCRS)
9169
0
        {
9170
0
            auto horizCRSType = proj_get_type(horizCRS);
9171
0
            isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9172
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9173
0
            {
9174
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9175
0
                if (base)
9176
0
                {
9177
0
                    isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9178
0
                    proj_destroy(base);
9179
0
                }
9180
0
            }
9181
0
            proj_destroy(horizCRS);
9182
0
        }
9183
0
    }
9184
0
    d->undoDemoteFromBoundCRS();
9185
0
    return isProjected;
9186
0
}
9187
9188
/************************************************************************/
9189
/*                           OSRIsProjected()                           */
9190
/************************************************************************/
9191
/**
9192
 * \brief Check if projected coordinate system.
9193
 *
9194
 * This function is the same as OGRSpatialReference::IsProjected().
9195
 */
9196
int OSRIsProjected(OGRSpatialReferenceH hSRS)
9197
9198
0
{
9199
0
    VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9200
9201
0
    return ToPointer(hSRS)->IsProjected();
9202
0
}
9203
9204
/************************************************************************/
9205
/*                            IsGeocentric()                            */
9206
/************************************************************************/
9207
9208
/**
9209
 * \brief Check if geocentric coordinate system.
9210
 *
9211
 * This method is the same as the C function OSRIsGeocentric().
9212
 *
9213
 * @return TRUE if this contains a GEOCCS node indicating a it is a
9214
 * geocentric coordinate system.
9215
 *
9216
 * @since OGR 1.9.0
9217
 */
9218
9219
int OGRSpatialReference::IsGeocentric() const
9220
9221
0
{
9222
0
    TAKE_OPTIONAL_LOCK();
9223
9224
0
    d->refreshProjObj();
9225
0
    d->demoteFromBoundCRS();
9226
0
    bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9227
0
    d->undoDemoteFromBoundCRS();
9228
0
    return isGeocentric;
9229
0
}
9230
9231
/************************************************************************/
9232
/*                           OSRIsGeocentric()                          */
9233
/************************************************************************/
9234
/**
9235
 * \brief Check if geocentric coordinate system.
9236
 *
9237
 * This function is the same as OGRSpatialReference::IsGeocentric().
9238
 *
9239
 * @since OGR 1.9.0
9240
 */
9241
int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9242
9243
0
{
9244
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9245
9246
0
    return ToPointer(hSRS)->IsGeocentric();
9247
0
}
9248
9249
/************************************************************************/
9250
/*                            IsEmpty()                                 */
9251
/************************************************************************/
9252
9253
/**
9254
 * \brief Return if the SRS is not set.
9255
 */
9256
9257
bool OGRSpatialReference::IsEmpty() const
9258
0
{
9259
0
    TAKE_OPTIONAL_LOCK();
9260
9261
0
    d->refreshProjObj();
9262
0
    return d->m_pj_crs == nullptr;
9263
0
}
9264
9265
/************************************************************************/
9266
/*                            IsGeographic()                            */
9267
/************************************************************************/
9268
9269
/**
9270
 * \brief Check if geographic coordinate system.
9271
 *
9272
 * This method is the same as the C function OSRIsGeographic().
9273
 *
9274
 * @return TRUE if this spatial reference is geographic ... that is the
9275
 * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9276
 * GeographicCRS
9277
 */
9278
9279
int OGRSpatialReference::IsGeographic() const
9280
9281
0
{
9282
0
    TAKE_OPTIONAL_LOCK();
9283
9284
0
    d->refreshProjObj();
9285
0
    d->demoteFromBoundCRS();
9286
0
    bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9287
0
                  d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9288
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9289
0
    {
9290
0
        auto horizCRS =
9291
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9292
0
        if (horizCRS)
9293
0
        {
9294
0
            auto horizCRSType = proj_get_type(horizCRS);
9295
0
            isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9296
0
                     horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9297
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9298
0
            {
9299
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9300
0
                if (base)
9301
0
                {
9302
0
                    horizCRSType = proj_get_type(base);
9303
0
                    isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9304
0
                             horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9305
0
                    proj_destroy(base);
9306
0
                }
9307
0
            }
9308
0
            proj_destroy(horizCRS);
9309
0
        }
9310
0
    }
9311
0
    d->undoDemoteFromBoundCRS();
9312
0
    return isGeog;
9313
0
}
9314
9315
/************************************************************************/
9316
/*                          OSRIsGeographic()                           */
9317
/************************************************************************/
9318
/**
9319
 * \brief Check if geographic coordinate system.
9320
 *
9321
 * This function is the same as OGRSpatialReference::IsGeographic().
9322
 */
9323
int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9324
9325
0
{
9326
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9327
9328
0
    return ToPointer(hSRS)->IsGeographic();
9329
0
}
9330
9331
/************************************************************************/
9332
/*                      IsDerivedGeographic()                           */
9333
/************************************************************************/
9334
9335
/**
9336
 * \brief Check if the CRS is a derived geographic coordinate system.
9337
 * (for example a rotated long/lat grid)
9338
 *
9339
 * This method is the same as the C function OSRIsDerivedGeographic().
9340
 *
9341
 * @since GDAL 3.1.0 and PROJ 6.3.0
9342
 */
9343
9344
int OGRSpatialReference::IsDerivedGeographic() const
9345
9346
0
{
9347
0
    TAKE_OPTIONAL_LOCK();
9348
9349
0
    d->refreshProjObj();
9350
0
    d->demoteFromBoundCRS();
9351
0
    const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9352
0
                        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9353
0
    const bool isDerivedGeographic =
9354
0
        isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9355
0
    d->undoDemoteFromBoundCRS();
9356
0
    return isDerivedGeographic ? TRUE : FALSE;
9357
0
}
9358
9359
/************************************************************************/
9360
/*                      OSRIsDerivedGeographic()                        */
9361
/************************************************************************/
9362
/**
9363
 * \brief Check if the CRS is a derived geographic coordinate system.
9364
 * (for example a rotated long/lat grid)
9365
 *
9366
 * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9367
 */
9368
int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9369
9370
0
{
9371
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9372
9373
0
    return ToPointer(hSRS)->IsDerivedGeographic();
9374
0
}
9375
9376
/************************************************************************/
9377
/*                      IsDerivedProjected()                            */
9378
/************************************************************************/
9379
9380
/**
9381
 * \brief Check if the CRS is a derived projected coordinate system.
9382
 *
9383
 * This method is the same as the C function OSRIsDerivedGeographic().
9384
 *
9385
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9386
 */
9387
9388
int OGRSpatialReference::IsDerivedProjected() const
9389
9390
0
{
9391
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
9392
0
    TAKE_OPTIONAL_LOCK();
9393
0
    d->refreshProjObj();
9394
0
    d->demoteFromBoundCRS();
9395
0
    const bool isDerivedProjected =
9396
0
        d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9397
0
    d->undoDemoteFromBoundCRS();
9398
0
    return isDerivedProjected ? TRUE : FALSE;
9399
#else
9400
    return FALSE;
9401
#endif
9402
0
}
9403
9404
/************************************************************************/
9405
/*                      OSRIsDerivedProjected()                         */
9406
/************************************************************************/
9407
/**
9408
 * \brief Check if the CRS is a derived projected coordinate system.
9409
 *
9410
 * This function is the same as OGRSpatialReference::IsDerivedProjected().
9411
 *
9412
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9413
 */
9414
int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9415
9416
0
{
9417
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9418
9419
0
    return ToPointer(hSRS)->IsDerivedProjected();
9420
0
}
9421
9422
/************************************************************************/
9423
/*                              IsLocal()                               */
9424
/************************************************************************/
9425
9426
/**
9427
 * \brief Check if local coordinate system.
9428
 *
9429
 * This method is the same as the C function OSRIsLocal().
9430
 *
9431
 * @return TRUE if this spatial reference is local ... that is the
9432
 * root is a LOCAL_CS node.
9433
 */
9434
9435
int OGRSpatialReference::IsLocal() const
9436
9437
0
{
9438
0
    TAKE_OPTIONAL_LOCK();
9439
0
    d->refreshProjObj();
9440
0
    return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9441
0
}
9442
9443
/************************************************************************/
9444
/*                          OSRIsLocal()                                */
9445
/************************************************************************/
9446
/**
9447
 * \brief Check if local coordinate system.
9448
 *
9449
 * This function is the same as OGRSpatialReference::IsLocal().
9450
 */
9451
int OSRIsLocal(OGRSpatialReferenceH hSRS)
9452
9453
0
{
9454
0
    VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9455
9456
0
    return ToPointer(hSRS)->IsLocal();
9457
0
}
9458
9459
/************************************************************************/
9460
/*                            IsVertical()                              */
9461
/************************************************************************/
9462
9463
/**
9464
 * \brief Check if vertical coordinate system.
9465
 *
9466
 * This method is the same as the C function OSRIsVertical().
9467
 *
9468
 * @return TRUE if this contains a VERT_CS node indicating a it is a
9469
 * vertical coordinate system. Also if it is a CompoundCRS made of a
9470
 * VerticalCRS
9471
 *
9472
 * @since OGR 1.8.0
9473
 */
9474
9475
int OGRSpatialReference::IsVertical() const
9476
9477
0
{
9478
0
    TAKE_OPTIONAL_LOCK();
9479
0
    d->refreshProjObj();
9480
0
    d->demoteFromBoundCRS();
9481
0
    bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9482
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9483
0
    {
9484
0
        auto vertCRS =
9485
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9486
0
        if (vertCRS)
9487
0
        {
9488
0
            const auto vertCRSType = proj_get_type(vertCRS);
9489
0
            isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9490
0
            if (vertCRSType == PJ_TYPE_BOUND_CRS)
9491
0
            {
9492
0
                auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9493
0
                if (base)
9494
0
                {
9495
0
                    isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9496
0
                    proj_destroy(base);
9497
0
                }
9498
0
            }
9499
0
            proj_destroy(vertCRS);
9500
0
        }
9501
0
    }
9502
0
    d->undoDemoteFromBoundCRS();
9503
0
    return isVertical;
9504
0
}
9505
9506
/************************************************************************/
9507
/*                           OSRIsVertical()                            */
9508
/************************************************************************/
9509
/**
9510
 * \brief Check if vertical coordinate system.
9511
 *
9512
 * This function is the same as OGRSpatialReference::IsVertical().
9513
 *
9514
 * @since OGR 1.8.0
9515
 */
9516
int OSRIsVertical(OGRSpatialReferenceH hSRS)
9517
9518
0
{
9519
0
    VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9520
9521
0
    return ToPointer(hSRS)->IsVertical();
9522
0
}
9523
9524
/************************************************************************/
9525
/*                            IsDynamic()                               */
9526
/************************************************************************/
9527
9528
/**
9529
 * \brief Check if a CRS is a dynamic CRS.
9530
 *
9531
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9532
 * plate-fixed.
9533
 *
9534
 * This method is the same as the C function OSRIsDynamic().
9535
 *
9536
 * @return true if the CRS is dynamic
9537
 *
9538
 * @since OGR 3.4.0
9539
 *
9540
 * @see HasPointMotionOperation()
9541
 */
9542
9543
bool OGRSpatialReference::IsDynamic() const
9544
9545
0
{
9546
0
    TAKE_OPTIONAL_LOCK();
9547
0
    bool isDynamic = false;
9548
0
    d->refreshProjObj();
9549
0
    d->demoteFromBoundCRS();
9550
0
    auto ctxt = d->getPROJContext();
9551
0
    PJ *horiz = nullptr;
9552
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9553
0
    {
9554
0
        horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9555
0
    }
9556
0
    else if (d->m_pj_crs)
9557
0
    {
9558
0
        horiz = proj_clone(ctxt, d->m_pj_crs);
9559
0
    }
9560
0
    if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9561
0
    {
9562
0
        auto baseCRS = proj_get_source_crs(ctxt, horiz);
9563
0
        if (baseCRS)
9564
0
        {
9565
0
            proj_destroy(horiz);
9566
0
            horiz = baseCRS;
9567
0
        }
9568
0
    }
9569
0
    auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9570
0
    if (datum)
9571
0
    {
9572
0
        const auto type = proj_get_type(datum);
9573
0
        isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9574
0
                    type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9575
0
        if (!isDynamic)
9576
0
        {
9577
0
            const char *auth_name = proj_get_id_auth_name(datum, 0);
9578
0
            const char *code = proj_get_id_code(datum, 0);
9579
0
            if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9580
0
                EQUAL(code, "6326"))
9581
0
            {
9582
0
                isDynamic = true;
9583
0
            }
9584
0
        }
9585
0
        proj_destroy(datum);
9586
0
    }
9587
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9588
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9589
0
    else
9590
0
    {
9591
0
        auto ensemble =
9592
0
            horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9593
0
        if (ensemble)
9594
0
        {
9595
0
            auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9596
0
            if (member)
9597
0
            {
9598
0
                const auto type = proj_get_type(member);
9599
0
                isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9600
0
                            type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9601
0
                proj_destroy(member);
9602
0
            }
9603
0
            proj_destroy(ensemble);
9604
0
        }
9605
0
    }
9606
0
#endif
9607
0
    proj_destroy(horiz);
9608
0
    d->undoDemoteFromBoundCRS();
9609
0
    return isDynamic;
9610
0
}
9611
9612
/************************************************************************/
9613
/*                           OSRIsDynamic()                             */
9614
/************************************************************************/
9615
/**
9616
 * \brief Check if a CRS is a dynamic CRS.
9617
 *
9618
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9619
 * plate-fixed.
9620
 *
9621
 * This function is the same as OGRSpatialReference::IsDynamic().
9622
 *
9623
 * @since OGR 3.4.0
9624
 */
9625
int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9626
9627
0
{
9628
0
    VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9629
9630
0
    return ToPointer(hSRS)->IsDynamic();
9631
0
}
9632
9633
/************************************************************************/
9634
/*                         HasPointMotionOperation()                    */
9635
/************************************************************************/
9636
9637
/**
9638
 * \brief Check if a CRS has at least an associated point motion operation.
9639
 *
9640
 * Some CRS are not formally declared as dynamic, but may behave as such
9641
 * in practice due to the presence of point motion operation, to perform
9642
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9643
 *
9644
 * @return true if the CRS has at least an associated point motion operation.
9645
 *
9646
 * @since OGR 3.8.0 and PROJ 9.4.0
9647
 *
9648
 * @see IsDynamic()
9649
 */
9650
9651
bool OGRSpatialReference::HasPointMotionOperation() const
9652
9653
0
{
9654
0
#if PROJ_VERSION_MAJOR > 9 ||                                                  \
9655
0
    (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9656
0
    TAKE_OPTIONAL_LOCK();
9657
0
    d->refreshProjObj();
9658
0
    d->demoteFromBoundCRS();
9659
0
    auto ctxt = d->getPROJContext();
9660
0
    auto res =
9661
0
        CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9662
0
    d->undoDemoteFromBoundCRS();
9663
0
    return res;
9664
#else
9665
    return false;
9666
#endif
9667
0
}
9668
9669
/************************************************************************/
9670
/*                      OSRHasPointMotionOperation()                    */
9671
/************************************************************************/
9672
9673
/**
9674
 * \brief Check if a CRS has at least an associated point motion operation.
9675
 *
9676
 * Some CRS are not formally declared as dynamic, but may behave as such
9677
 * in practice due to the presence of point motion operation, to perform
9678
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9679
 *
9680
 * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9681
 *
9682
 * @since OGR 3.8.0 and PROJ 9.4.0
9683
 */
9684
int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9685
9686
0
{
9687
0
    VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9688
9689
0
    return ToPointer(hSRS)->HasPointMotionOperation();
9690
0
}
9691
9692
/************************************************************************/
9693
/*                            CloneGeogCS()                             */
9694
/************************************************************************/
9695
9696
/**
9697
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9698
 * object.
9699
 *
9700
 * @return a new SRS, which becomes the responsibility of the caller.
9701
 */
9702
OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9703
9704
0
{
9705
0
    TAKE_OPTIONAL_LOCK();
9706
0
    d->refreshProjObj();
9707
0
    if (d->m_pj_crs)
9708
0
    {
9709
0
        if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9710
0
            return nullptr;
9711
9712
0
        auto geodCRS =
9713
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9714
0
        if (geodCRS)
9715
0
        {
9716
0
            OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9717
0
            if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9718
0
            {
9719
0
                PJ *hub_crs =
9720
0
                    proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9721
0
                PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9722
0
                                                     d->m_pj_crs);
9723
0
                auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9724
0
                                                      geodCRS, hub_crs, co);
9725
0
                proj_destroy(geodCRS);
9726
0
                geodCRS = temp;
9727
0
                proj_destroy(hub_crs);
9728
0
                proj_destroy(co);
9729
0
            }
9730
9731
            /* --------------------------------------------------------------------
9732
             */
9733
            /*      We have to reconstruct the GEOGCS node for geocentric */
9734
            /*      coordinate systems. */
9735
            /* --------------------------------------------------------------------
9736
             */
9737
0
            if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9738
0
            {
9739
0
                auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9740
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9741
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9742
0
                if (datum == nullptr)
9743
0
                {
9744
0
                    datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9745
0
                                                        geodCRS);
9746
0
                }
9747
0
#endif
9748
0
                if (datum)
9749
0
                {
9750
0
                    auto cs = proj_create_ellipsoidal_2D_cs(
9751
0
                        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9752
0
                        nullptr, 0);
9753
0
                    auto temp = proj_create_geographic_crs_from_datum(
9754
0
                        d->getPROJContext(), "unnamed", datum, cs);
9755
0
                    proj_destroy(datum);
9756
0
                    proj_destroy(cs);
9757
0
                    proj_destroy(geodCRS);
9758
0
                    geodCRS = temp;
9759
0
                }
9760
0
            }
9761
9762
0
            poNewSRS->d->setPjCRS(geodCRS);
9763
0
            if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9764
0
                poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9765
0
            return poNewSRS;
9766
0
        }
9767
0
    }
9768
0
    return nullptr;
9769
0
}
9770
9771
/************************************************************************/
9772
/*                           OSRCloneGeogCS()                           */
9773
/************************************************************************/
9774
/**
9775
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9776
 * object.
9777
 *
9778
 * This function is the same as OGRSpatialReference::CloneGeogCS().
9779
 */
9780
OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9781
9782
0
{
9783
0
    VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9784
9785
0
    return ToHandle(ToPointer(hSource)->CloneGeogCS());
9786
0
}
9787
9788
/************************************************************************/
9789
/*                            IsSameGeogCS()                            */
9790
/************************************************************************/
9791
9792
/**
9793
 * \brief Do the GeogCS'es match?
9794
 *
9795
 * This method is the same as the C function OSRIsSameGeogCS().
9796
 *
9797
 * @param poOther the SRS being compared against.
9798
 *
9799
 * @return TRUE if they are the same or FALSE otherwise.
9800
 */
9801
9802
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9803
9804
0
{
9805
0
    return IsSameGeogCS(poOther, nullptr);
9806
0
}
9807
9808
/**
9809
 * \brief Do the GeogCS'es match?
9810
 *
9811
 * This method is the same as the C function OSRIsSameGeogCS().
9812
 *
9813
 * @param poOther the SRS being compared against.
9814
 * @param papszOptions options. ignored
9815
 *
9816
 * @return TRUE if they are the same or FALSE otherwise.
9817
 */
9818
9819
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9820
                                      const char *const *papszOptions) const
9821
9822
0
{
9823
0
    TAKE_OPTIONAL_LOCK();
9824
9825
0
    CPL_IGNORE_RET_VAL(papszOptions);
9826
9827
0
    d->refreshProjObj();
9828
0
    poOther->d->refreshProjObj();
9829
0
    if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9830
0
        return FALSE;
9831
0
    if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9832
0
        d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9833
0
        poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9834
0
        poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9835
0
    {
9836
0
        return FALSE;
9837
0
    }
9838
9839
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9840
0
    auto otherGeodCRS =
9841
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9842
0
    if (!geodCRS || !otherGeodCRS)
9843
0
    {
9844
0
        proj_destroy(geodCRS);
9845
0
        proj_destroy(otherGeodCRS);
9846
0
        return FALSE;
9847
0
    }
9848
9849
0
    int ret = proj_is_equivalent_to(
9850
0
        geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9851
9852
0
    proj_destroy(geodCRS);
9853
0
    proj_destroy(otherGeodCRS);
9854
0
    return ret;
9855
0
}
9856
9857
/************************************************************************/
9858
/*                          OSRIsSameGeogCS()                           */
9859
/************************************************************************/
9860
9861
/**
9862
 * \brief Do the GeogCS'es match?
9863
 *
9864
 * This function is the same as OGRSpatialReference::IsSameGeogCS().
9865
 */
9866
int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9867
9868
0
{
9869
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9870
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9871
9872
0
    return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9873
0
}
9874
9875
/************************************************************************/
9876
/*                            IsSameVertCS()                            */
9877
/************************************************************************/
9878
9879
/**
9880
 * \brief Do the VertCS'es match?
9881
 *
9882
 * This method is the same as the C function OSRIsSameVertCS().
9883
 *
9884
 * @param poOther the SRS being compared against.
9885
 *
9886
 * @return TRUE if they are the same or FALSE otherwise.
9887
 */
9888
9889
int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9890
9891
0
{
9892
0
    TAKE_OPTIONAL_LOCK();
9893
9894
    /* -------------------------------------------------------------------- */
9895
    /*      Does the datum name match?                                      */
9896
    /* -------------------------------------------------------------------- */
9897
0
    const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9898
0
    const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9899
9900
0
    if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9901
0
        !EQUAL(pszThisValue, pszOtherValue))
9902
0
        return FALSE;
9903
9904
    /* -------------------------------------------------------------------- */
9905
    /*      Do the units match?                                             */
9906
    /* -------------------------------------------------------------------- */
9907
0
    pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9908
0
    if (pszThisValue == nullptr)
9909
0
        pszThisValue = "1.0";
9910
9911
0
    pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9912
0
    if (pszOtherValue == nullptr)
9913
0
        pszOtherValue = "1.0";
9914
9915
0
    if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9916
0
        return FALSE;
9917
9918
0
    return TRUE;
9919
0
}
9920
9921
/************************************************************************/
9922
/*                          OSRIsSameVertCS()                           */
9923
/************************************************************************/
9924
9925
/**
9926
 * \brief Do the VertCS'es match?
9927
 *
9928
 * This function is the same as OGRSpatialReference::IsSameVertCS().
9929
 */
9930
int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9931
9932
0
{
9933
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9934
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9935
9936
0
    return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9937
0
}
9938
9939
/************************************************************************/
9940
/*                               IsSame()                               */
9941
/************************************************************************/
9942
9943
/**
9944
 * \brief Do these two spatial references describe the same system ?
9945
 *
9946
 * @param poOtherSRS the SRS being compared to.
9947
 *
9948
 * @return TRUE if equivalent or FALSE otherwise.
9949
 */
9950
9951
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9952
9953
0
{
9954
0
    return IsSame(poOtherSRS, nullptr);
9955
0
}
9956
9957
/**
9958
 * \brief Do these two spatial references describe the same system ?
9959
 *
9960
 * This also takes into account the data axis to CRS axis mapping by default
9961
 *
9962
 * @param poOtherSRS the SRS being compared to.
9963
 * @param papszOptions options. NULL or NULL terminated list of options.
9964
 * Currently supported options are:
9965
 * <ul>
9966
 * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9967
 * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9968
 *     Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9969
 * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9970
 * </ul>
9971
 *
9972
 * @return TRUE if equivalent or FALSE otherwise.
9973
 */
9974
9975
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9976
                                const char *const *papszOptions) const
9977
9978
0
{
9979
0
    TAKE_OPTIONAL_LOCK();
9980
9981
0
    d->refreshProjObj();
9982
0
    poOtherSRS->d->refreshProjObj();
9983
0
    if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9984
0
        return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9985
0
    if (!CPLTestBool(CSLFetchNameValueDef(
9986
0
            papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9987
0
    {
9988
0
        if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9989
0
            return false;
9990
0
    }
9991
9992
0
    if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9993
0
                                          "IGNORE_COORDINATE_EPOCH", "NO")))
9994
0
    {
9995
0
        if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9996
0
            return false;
9997
0
    }
9998
9999
0
    bool reboundSelf = false;
10000
0
    bool reboundOther = false;
10001
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10002
0
        poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10003
0
    {
10004
0
        d->demoteFromBoundCRS();
10005
0
        reboundSelf = true;
10006
0
    }
10007
0
    else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10008
0
             poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10009
0
    {
10010
0
        poOtherSRS->d->demoteFromBoundCRS();
10011
0
        reboundOther = true;
10012
0
    }
10013
10014
0
    PJ_COMPARISON_CRITERION criterion =
10015
0
        PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10016
0
    const char *pszCriterion = CSLFetchNameValueDef(
10017
0
        papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10018
0
    if (EQUAL(pszCriterion, "STRICT"))
10019
0
        criterion = PJ_COMP_STRICT;
10020
0
    else if (EQUAL(pszCriterion, "EQUIVALENT"))
10021
0
        criterion = PJ_COMP_EQUIVALENT;
10022
0
    else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10023
0
    {
10024
0
        CPLError(CE_Warning, CPLE_NotSupported,
10025
0
                 "Unsupported value for CRITERION: %s", pszCriterion);
10026
0
    }
10027
0
    int ret =
10028
0
        proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10029
0
    if (reboundSelf)
10030
0
        d->undoDemoteFromBoundCRS();
10031
0
    if (reboundOther)
10032
0
        poOtherSRS->d->undoDemoteFromBoundCRS();
10033
10034
0
    return ret;
10035
0
}
10036
10037
/************************************************************************/
10038
/*                             OSRIsSame()                              */
10039
/************************************************************************/
10040
10041
/**
10042
 * \brief Do these two spatial references describe the same system ?
10043
 *
10044
 * This function is the same as OGRSpatialReference::IsSame().
10045
 */
10046
int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10047
10048
0
{
10049
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10050
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10051
10052
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10053
0
}
10054
10055
/************************************************************************/
10056
/*                             OSRIsSameEx()                            */
10057
/************************************************************************/
10058
10059
/**
10060
 * \brief Do these two spatial references describe the same system ?
10061
 *
10062
 * This function is the same as OGRSpatialReference::IsSame().
10063
 */
10064
int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10065
                const char *const *papszOptions)
10066
0
{
10067
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10068
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10069
10070
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10071
0
}
10072
10073
/************************************************************************/
10074
/*                    convertToOtherProjection()                        */
10075
/************************************************************************/
10076
10077
/**
10078
 * \brief Convert to another equivalent projection
10079
 *
10080
 * Currently implemented:
10081
 * <ul>
10082
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10083
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10084
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10085
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10086
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10087
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10088
 * </ul>
10089
 *
10090
 * @param pszTargetProjection target projection.
10091
 * @param papszOptions lists of options. None supported currently.
10092
 * @return a new SRS, or NULL in case of error.
10093
 *
10094
 * @since GDAL 2.3
10095
 */
10096
OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10097
    const char *pszTargetProjection,
10098
    CPL_UNUSED const char *const *papszOptions) const
10099
0
{
10100
0
    TAKE_OPTIONAL_LOCK();
10101
10102
0
    if (pszTargetProjection == nullptr)
10103
0
        return nullptr;
10104
0
    int new_code;
10105
0
    if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10106
0
    {
10107
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10108
0
    }
10109
0
    else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10110
0
    {
10111
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10112
0
    }
10113
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10114
0
    {
10115
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10116
0
    }
10117
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10118
0
    {
10119
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10120
0
    }
10121
0
    else
10122
0
    {
10123
0
        return nullptr;
10124
0
    }
10125
10126
0
    d->refreshProjObj();
10127
0
    d->demoteFromBoundCRS();
10128
0
    OGRSpatialReference *poNewSRS = nullptr;
10129
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10130
0
    {
10131
0
        auto conv =
10132
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10133
0
        auto new_conv = proj_convert_conversion_to_other_method(
10134
0
            d->getPROJContext(), conv, new_code, nullptr);
10135
0
        proj_destroy(conv);
10136
0
        if (new_conv)
10137
0
        {
10138
0
            auto geodCRS =
10139
0
                proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10140
0
            auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10141
0
                                                     d->m_pj_crs);
10142
0
            if (geodCRS && cs)
10143
0
            {
10144
0
                auto new_proj_crs = proj_create_projected_crs(
10145
0
                    d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10146
0
                    new_conv, cs);
10147
0
                proj_destroy(new_conv);
10148
0
                if (new_proj_crs)
10149
0
                {
10150
0
                    poNewSRS = new OGRSpatialReference();
10151
10152
0
                    if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10153
0
                    {
10154
0
                        auto boundCRS = proj_crs_create_bound_crs(
10155
0
                            d->getPROJContext(), new_proj_crs,
10156
0
                            d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10157
0
                        if (boundCRS)
10158
0
                        {
10159
0
                            proj_destroy(new_proj_crs);
10160
0
                            new_proj_crs = boundCRS;
10161
0
                        }
10162
0
                    }
10163
10164
0
                    poNewSRS->d->setPjCRS(new_proj_crs);
10165
0
                }
10166
0
            }
10167
0
            proj_destroy(geodCRS);
10168
0
            proj_destroy(cs);
10169
0
        }
10170
0
    }
10171
0
    d->undoDemoteFromBoundCRS();
10172
0
    return poNewSRS;
10173
0
}
10174
10175
/************************************************************************/
10176
/*                    OSRConvertToOtherProjection()                     */
10177
/************************************************************************/
10178
10179
/**
10180
 * \brief Convert to another equivalent projection
10181
 *
10182
 * Currently implemented:
10183
 * <ul>
10184
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10185
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10186
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10187
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10188
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10189
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10190
 * </ul>
10191
 *
10192
 * @param hSRS source SRS
10193
 * @param pszTargetProjection target projection.
10194
 * @param papszOptions lists of options. None supported currently.
10195
 * @return a new SRS, or NULL in case of error.
10196
 *
10197
 * @since GDAL 2.3
10198
 */
10199
OGRSpatialReferenceH
10200
OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10201
                            const char *pszTargetProjection,
10202
                            const char *const *papszOptions)
10203
0
{
10204
0
    VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10205
0
    return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10206
0
        pszTargetProjection, papszOptions));
10207
0
}
10208
10209
/************************************************************************/
10210
/*                           OSRFindMatches()                           */
10211
/************************************************************************/
10212
10213
/**
10214
 * \brief Try to identify a match between the passed SRS and a related SRS
10215
 * in a catalog.
10216
 *
10217
 * Matching may be partial, or may fail.
10218
 * Returned entries will be sorted by decreasing match confidence (first
10219
 * entry has the highest match confidence).
10220
 *
10221
 * The exact way matching is done may change in future versions. Starting with
10222
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
10223
 *
10224
 * This function is the same as OGRSpatialReference::FindMatches().
10225
 *
10226
 * @param hSRS SRS to match
10227
 * @param papszOptions NULL terminated list of options or NULL
10228
 * @param pnEntries Output parameter. Number of values in the returned array.
10229
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10230
 * will be allocated to an array of *pnEntries whose values between 0 and 100
10231
 * indicate the confidence in the match. 100 is the highest confidence level.
10232
 * The array must be freed with CPLFree().
10233
 *
10234
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10235
 * with OSRFreeSRSArray()
10236
 *
10237
 * @since GDAL 2.3
10238
 */
10239
OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10240
                                     char **papszOptions, int *pnEntries,
10241
                                     int **ppanMatchConfidence)
10242
0
{
10243
0
    if (pnEntries)
10244
0
        *pnEntries = 0;
10245
0
    if (ppanMatchConfidence)
10246
0
        *ppanMatchConfidence = nullptr;
10247
0
    VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10248
10249
0
    OGRSpatialReference *poSRS = ToPointer(hSRS);
10250
0
    return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10251
0
}
10252
10253
/************************************************************************/
10254
/*                           OSRFreeSRSArray()                          */
10255
/************************************************************************/
10256
10257
/**
10258
 * \brief Free return of OSRIdentifyMatches()
10259
 *
10260
 * @param pahSRS array of SRS (must be NULL terminated)
10261
 * @since GDAL 2.3
10262
 */
10263
void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10264
0
{
10265
0
    if (pahSRS != nullptr)
10266
0
    {
10267
0
        for (int i = 0; pahSRS[i] != nullptr; ++i)
10268
0
        {
10269
0
            OSRRelease(pahSRS[i]);
10270
0
        }
10271
0
        CPLFree(pahSRS);
10272
0
    }
10273
0
}
10274
10275
/************************************************************************/
10276
/*                         FindBestMatch()                              */
10277
/************************************************************************/
10278
10279
/**
10280
 * \brief Try to identify the best match between the passed SRS and a related
10281
 * SRS in a catalog.
10282
 *
10283
 * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10284
 * of filtering its output.
10285
 * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10286
 * will be considered. If there is a single match, it is returned.
10287
 * If there are several matches, only return the one under the
10288
 * pszPreferredAuthority, if there is a single one under that authority.
10289
 *
10290
 * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10291
 * 100). If set to 0, 90 is used.
10292
 * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10293
 * "EPSG" is used.
10294
 * @param papszOptions NULL terminated list of options or NULL. No option is
10295
 * defined at time of writing.
10296
 *
10297
 * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10298
 *
10299
 * @since GDAL 3.6
10300
 * @see OGRSpatialReference::FindMatches()
10301
 */
10302
OGRSpatialReference *
10303
OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10304
                                   const char *pszPreferredAuthority,
10305
                                   CSLConstList papszOptions) const
10306
0
{
10307
0
    TAKE_OPTIONAL_LOCK();
10308
10309
0
    CPL_IGNORE_RET_VAL(papszOptions);  // ignored for now.
10310
10311
0
    if (nMinimumMatchConfidence == 0)
10312
0
        nMinimumMatchConfidence = 90;
10313
0
    if (pszPreferredAuthority == nullptr)
10314
0
        pszPreferredAuthority = "EPSG";
10315
10316
    // Try to identify the CRS with the database
10317
0
    int nEntries = 0;
10318
0
    int *panConfidence = nullptr;
10319
0
    OGRSpatialReferenceH *pahSRS =
10320
0
        FindMatches(nullptr, &nEntries, &panConfidence);
10321
0
    if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10322
0
    {
10323
0
        std::vector<double> adfTOWGS84(7);
10324
0
        if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10325
0
        {
10326
0
            adfTOWGS84.clear();
10327
0
        }
10328
10329
0
        auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10330
10331
0
        auto poBaseGeogCRS =
10332
0
            std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10333
10334
        // If the base geographic SRS of the SRS is EPSG:4326
10335
        // with TOWGS84[0,0,0,0,0,0], then just use the official
10336
        // SRS code
10337
        // Same with EPSG:4258 (ETRS89), since it's the only known
10338
        // TOWGS84[] style transformation to WGS 84, and given the
10339
        // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10340
        // chance that a non-NULL TOWGS84[] will emerge.
10341
0
        const char *pszAuthorityName = nullptr;
10342
0
        const char *pszAuthorityCode = nullptr;
10343
0
        const char *pszBaseAuthorityName = nullptr;
10344
0
        const char *pszBaseAuthorityCode = nullptr;
10345
0
        if (adfTOWGS84 == std::vector<double>(7) &&
10346
0
            (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10347
0
            EQUAL(pszAuthorityName, "EPSG") &&
10348
0
            (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10349
0
            (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10350
0
                nullptr &&
10351
0
            EQUAL(pszBaseAuthorityName, "EPSG") &&
10352
0
            (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10353
0
                nullptr &&
10354
0
            (EQUAL(pszBaseAuthorityCode, "4326") ||
10355
0
             EQUAL(pszBaseAuthorityCode, "4258")))
10356
0
        {
10357
0
            poSRS->importFromEPSG(atoi(pszAuthorityCode));
10358
0
        }
10359
10360
0
        CPLFree(pahSRS);
10361
0
        CPLFree(panConfidence);
10362
10363
0
        return poSRS;
10364
0
    }
10365
0
    else
10366
0
    {
10367
        // If there are several matches >= nMinimumMatchConfidence, take the
10368
        // only one that is under pszPreferredAuthority
10369
0
        int iBestEntry = -1;
10370
0
        for (int i = 0; i < nEntries; i++)
10371
0
        {
10372
0
            if (panConfidence[i] >= nMinimumMatchConfidence)
10373
0
            {
10374
0
                const char *pszAuthName =
10375
0
                    OGRSpatialReference::FromHandle(pahSRS[i])
10376
0
                        ->GetAuthorityName(nullptr);
10377
0
                if (pszAuthName != nullptr &&
10378
0
                    EQUAL(pszAuthName, pszPreferredAuthority))
10379
0
                {
10380
0
                    if (iBestEntry < 0)
10381
0
                        iBestEntry = i;
10382
0
                    else
10383
0
                    {
10384
0
                        iBestEntry = -1;
10385
0
                        break;
10386
0
                    }
10387
0
                }
10388
0
            }
10389
0
        }
10390
0
        if (iBestEntry >= 0)
10391
0
        {
10392
0
            auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10393
0
            OSRFreeSRSArray(pahSRS);
10394
0
            CPLFree(panConfidence);
10395
0
            return poRet;
10396
0
        }
10397
0
    }
10398
0
    OSRFreeSRSArray(pahSRS);
10399
0
    CPLFree(panConfidence);
10400
0
    return nullptr;
10401
0
}
10402
10403
/************************************************************************/
10404
/*                             SetTOWGS84()                             */
10405
/************************************************************************/
10406
10407
/**
10408
 * \brief Set the Bursa-Wolf conversion to WGS84.
10409
 *
10410
 * This will create the TOWGS84 node as a child of the DATUM.  It will fail
10411
 * if there is no existing DATUM node. It will replace
10412
 * an existing TOWGS84 node if there is one.
10413
 *
10414
 * The parameters have the same meaning as EPSG transformation 9606
10415
 * (Position Vector 7-param. transformation).
10416
 *
10417
 * This method is the same as the C function OSRSetTOWGS84().
10418
 *
10419
 * @param dfDX X child in meters.
10420
 * @param dfDY Y child in meters.
10421
 * @param dfDZ Z child in meters.
10422
 * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10423
 * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10424
 * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10425
 * @param dfPPM scaling factor (parts per million).
10426
 *
10427
 * @return OGRERR_NONE on success.
10428
 */
10429
10430
OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10431
                                       double dfEX, double dfEY, double dfEZ,
10432
                                       double dfPPM)
10433
10434
0
{
10435
0
    TAKE_OPTIONAL_LOCK();
10436
10437
0
    d->refreshProjObj();
10438
0
    if (d->m_pj_crs == nullptr)
10439
0
    {
10440
0
        return OGRERR_FAILURE;
10441
0
    }
10442
10443
    // Remove existing BoundCRS
10444
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10445
0
    {
10446
0
        auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10447
0
        if (!baseCRS)
10448
0
            return OGRERR_FAILURE;
10449
0
        d->setPjCRS(baseCRS);
10450
0
    }
10451
10452
0
    PJ_PARAM_DESCRIPTION params[7];
10453
10454
0
    params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10455
0
    params[0].auth_name = "EPSG";
10456
0
    params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10457
0
    params[0].value = dfDX;
10458
0
    params[0].unit_name = "metre";
10459
0
    params[0].unit_conv_factor = 1.0;
10460
0
    params[0].unit_type = PJ_UT_LINEAR;
10461
10462
0
    params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10463
0
    params[1].auth_name = "EPSG";
10464
0
    params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10465
0
    params[1].value = dfDY;
10466
0
    params[1].unit_name = "metre";
10467
0
    params[1].unit_conv_factor = 1.0;
10468
0
    params[1].unit_type = PJ_UT_LINEAR;
10469
10470
0
    params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10471
0
    params[2].auth_name = "EPSG";
10472
0
    params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10473
0
    params[2].value = dfDZ;
10474
0
    params[2].unit_name = "metre";
10475
0
    params[2].unit_conv_factor = 1.0;
10476
0
    params[2].unit_type = PJ_UT_LINEAR;
10477
10478
0
    params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10479
0
    params[3].auth_name = "EPSG";
10480
0
    params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10481
0
    params[3].value = dfEX;
10482
0
    params[3].unit_name = "arc-second";
10483
0
    params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10484
0
    params[3].unit_type = PJ_UT_ANGULAR;
10485
10486
0
    params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10487
0
    params[4].auth_name = "EPSG";
10488
0
    params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10489
0
    params[4].value = dfEY;
10490
0
    params[4].unit_name = "arc-second";
10491
0
    params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10492
0
    params[4].unit_type = PJ_UT_ANGULAR;
10493
10494
0
    params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10495
0
    params[5].auth_name = "EPSG";
10496
0
    params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10497
0
    params[5].value = dfEZ;
10498
0
    params[5].unit_name = "arc-second";
10499
0
    params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10500
0
    params[5].unit_type = PJ_UT_ANGULAR;
10501
10502
0
    params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10503
0
    params[6].auth_name = "EPSG";
10504
0
    params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10505
0
    params[6].value = dfPPM;
10506
0
    params[6].unit_name = "parts per million";
10507
0
    params[6].unit_conv_factor = 1e-6;
10508
0
    params[6].unit_type = PJ_UT_SCALE;
10509
10510
0
    auto sourceCRS =
10511
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10512
0
    if (!sourceCRS)
10513
0
    {
10514
0
        return OGRERR_FAILURE;
10515
0
    }
10516
10517
0
    const auto sourceType = proj_get_type(sourceCRS);
10518
10519
0
    auto targetCRS = proj_create_from_database(
10520
0
        d->getPROJContext(), "EPSG",
10521
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS   ? "4326"
10522
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10523
0
                                                  : "4978",
10524
0
        PJ_CATEGORY_CRS, false, nullptr);
10525
0
    if (!targetCRS)
10526
0
    {
10527
0
        proj_destroy(sourceCRS);
10528
0
        return OGRERR_FAILURE;
10529
0
    }
10530
10531
0
    CPLString osMethodCode;
10532
0
    osMethodCode.Printf("%d",
10533
0
                        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10534
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10535
0
                        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10536
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10537
0
                            : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10538
10539
0
    auto transf = proj_create_transformation(
10540
0
        d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10541
0
        sourceCRS, targetCRS, nullptr,
10542
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10543
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10544
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10545
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10546
0
            : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10547
0
        "EPSG", osMethodCode.c_str(), 7, params, -1);
10548
0
    proj_destroy(sourceCRS);
10549
0
    if (!transf)
10550
0
    {
10551
0
        proj_destroy(targetCRS);
10552
0
        return OGRERR_FAILURE;
10553
0
    }
10554
10555
0
    auto newBoundCRS = proj_crs_create_bound_crs(
10556
0
        d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10557
0
    proj_destroy(transf);
10558
0
    proj_destroy(targetCRS);
10559
0
    if (!newBoundCRS)
10560
0
    {
10561
0
        return OGRERR_FAILURE;
10562
0
    }
10563
10564
0
    d->setPjCRS(newBoundCRS);
10565
0
    return OGRERR_NONE;
10566
0
}
10567
10568
/************************************************************************/
10569
/*                           OSRSetTOWGS84()                            */
10570
/************************************************************************/
10571
10572
/**
10573
 * \brief Set the Bursa-Wolf conversion to WGS84.
10574
 *
10575
 * This function is the same as OGRSpatialReference::SetTOWGS84().
10576
 */
10577
OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10578
                     double dfDZ, double dfEX, double dfEY, double dfEZ,
10579
                     double dfPPM)
10580
10581
0
{
10582
0
    VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10583
10584
0
    return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10585
0
                                       dfPPM);
10586
0
}
10587
10588
/************************************************************************/
10589
/*                             GetTOWGS84()                             */
10590
/************************************************************************/
10591
10592
/**
10593
 * \brief Fetch TOWGS84 parameters, if available.
10594
 *
10595
 * The parameters have the same meaning as EPSG transformation 9606
10596
 * (Position Vector 7-param. transformation).
10597
 *
10598
 * @param padfCoeff array into which up to 7 coefficients are placed.
10599
 * @param nCoeffCount size of padfCoeff - defaults to 7.
10600
 *
10601
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10602
 * TOWGS84 node available.
10603
 */
10604
10605
OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10606
10607
0
{
10608
0
    TAKE_OPTIONAL_LOCK();
10609
10610
0
    d->refreshProjObj();
10611
0
    if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10612
0
        return OGRERR_FAILURE;
10613
10614
0
    memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10615
10616
0
    auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10617
0
    int success = proj_coordoperation_get_towgs84_values(
10618
0
        d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10619
0
    proj_destroy(transf);
10620
10621
0
    return success ? OGRERR_NONE : OGRERR_FAILURE;
10622
0
}
10623
10624
/************************************************************************/
10625
/*                           OSRGetTOWGS84()                            */
10626
/************************************************************************/
10627
10628
/**
10629
 * \brief Fetch TOWGS84 parameters, if available.
10630
 *
10631
 * This function is the same as OGRSpatialReference::GetTOWGS84().
10632
 */
10633
OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10634
                     int nCoeffCount)
10635
10636
0
{
10637
0
    VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10638
10639
0
    return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10640
0
}
10641
10642
/************************************************************************/
10643
/*                         IsAngularParameter()                         */
10644
/************************************************************************/
10645
10646
/** Is the passed projection parameter an angular one?
10647
 *
10648
 * @return TRUE or FALSE
10649
 */
10650
10651
/* static */
10652
int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10653
10654
0
{
10655
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10656
0
        STARTS_WITH_CI(pszParameterName, "lati") ||
10657
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10658
0
        STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10659
0
        EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10660
0
        EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10661
0
        return TRUE;
10662
10663
0
    return FALSE;
10664
0
}
10665
10666
/************************************************************************/
10667
/*                        IsLongitudeParameter()                        */
10668
/************************************************************************/
10669
10670
/** Is the passed projection parameter an angular longitude
10671
 * (relative to a prime meridian)?
10672
 *
10673
 * @return TRUE or FALSE
10674
 */
10675
10676
/* static */
10677
int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10678
10679
0
{
10680
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10681
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10682
0
        return TRUE;
10683
10684
0
    return FALSE;
10685
0
}
10686
10687
/************************************************************************/
10688
/*                         IsLinearParameter()                          */
10689
/************************************************************************/
10690
10691
/** Is the passed projection parameter an linear one measured in meters or
10692
 * some similar linear measure.
10693
 *
10694
 * @return TRUE or FALSE
10695
 */
10696
10697
/* static */
10698
int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10699
10700
0
{
10701
0
    if (STARTS_WITH_CI(pszParameterName, "false_") ||
10702
0
        EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10703
0
        return TRUE;
10704
10705
0
    return FALSE;
10706
0
}
10707
10708
/************************************************************************/
10709
/*                            GetNormInfo()                             */
10710
/************************************************************************/
10711
10712
/**
10713
 * \brief Set the internal information for normalizing linear, and angular
10714
 * values.
10715
 */
10716
void OGRSpatialReference::GetNormInfo() const
10717
10718
0
{
10719
0
    TAKE_OPTIONAL_LOCK();
10720
10721
0
    if (d->bNormInfoSet)
10722
0
        return;
10723
10724
    /* -------------------------------------------------------------------- */
10725
    /*      Initialize values.                                              */
10726
    /* -------------------------------------------------------------------- */
10727
0
    d->bNormInfoSet = TRUE;
10728
10729
0
    d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10730
0
    d->dfToMeter = GetLinearUnits(nullptr);
10731
0
    d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10732
0
    if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10733
0
        d->dfToDegrees = 1.0;
10734
0
}
10735
10736
/************************************************************************/
10737
/*                            GetExtension()                            */
10738
/************************************************************************/
10739
10740
/**
10741
 * \brief Fetch extension value.
10742
 *
10743
 * Fetch the value of the named EXTENSION item for the identified
10744
 * target node.
10745
 *
10746
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10747
 * @param pszName the name of the extension being fetched.
10748
 * @param pszDefault the value to return if the extension is not found.
10749
 *
10750
 * @return node value if successful or pszDefault on failure.
10751
 */
10752
10753
const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10754
                                              const char *pszName,
10755
                                              const char *pszDefault) const
10756
10757
0
{
10758
0
    TAKE_OPTIONAL_LOCK();
10759
10760
    /* -------------------------------------------------------------------- */
10761
    /*      Find the target node.                                           */
10762
    /* -------------------------------------------------------------------- */
10763
0
    const OGR_SRSNode *poNode =
10764
0
        pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10765
10766
0
    if (poNode == nullptr)
10767
0
        return nullptr;
10768
10769
    /* -------------------------------------------------------------------- */
10770
    /*      Fetch matching EXTENSION if there is one.                       */
10771
    /* -------------------------------------------------------------------- */
10772
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10773
0
    {
10774
0
        const OGR_SRSNode *poChild = poNode->GetChild(i);
10775
10776
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10777
0
            poChild->GetChildCount() >= 2)
10778
0
        {
10779
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10780
0
                return poChild->GetChild(1)->GetValue();
10781
0
        }
10782
0
    }
10783
10784
0
    return pszDefault;
10785
0
}
10786
10787
/************************************************************************/
10788
/*                            SetExtension()                            */
10789
/************************************************************************/
10790
/**
10791
 * \brief Set extension value.
10792
 *
10793
 * Set the value of the named EXTENSION item for the identified
10794
 * target node.
10795
 *
10796
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10797
 * @param pszName the name of the extension being fetched.
10798
 * @param pszValue the value to set
10799
 *
10800
 * @return OGRERR_NONE on success
10801
 */
10802
10803
OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10804
                                         const char *pszName,
10805
                                         const char *pszValue)
10806
10807
0
{
10808
0
    TAKE_OPTIONAL_LOCK();
10809
10810
    /* -------------------------------------------------------------------- */
10811
    /*      Find the target node.                                           */
10812
    /* -------------------------------------------------------------------- */
10813
0
    OGR_SRSNode *poNode = nullptr;
10814
10815
0
    if (pszTargetKey == nullptr)
10816
0
        poNode = GetRoot();
10817
0
    else
10818
0
        poNode = GetAttrNode(pszTargetKey);
10819
10820
0
    if (poNode == nullptr)
10821
0
        return OGRERR_FAILURE;
10822
10823
    /* -------------------------------------------------------------------- */
10824
    /*      Fetch matching EXTENSION if there is one.                       */
10825
    /* -------------------------------------------------------------------- */
10826
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10827
0
    {
10828
0
        OGR_SRSNode *poChild = poNode->GetChild(i);
10829
10830
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10831
0
            poChild->GetChildCount() >= 2)
10832
0
        {
10833
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10834
0
            {
10835
0
                poChild->GetChild(1)->SetValue(pszValue);
10836
0
                return OGRERR_NONE;
10837
0
            }
10838
0
        }
10839
0
    }
10840
10841
    /* -------------------------------------------------------------------- */
10842
    /*      Create a new EXTENSION node.                                    */
10843
    /* -------------------------------------------------------------------- */
10844
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10845
0
    poAuthNode->AddChild(new OGR_SRSNode(pszName));
10846
0
    poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10847
10848
0
    poNode->AddChild(poAuthNode);
10849
10850
0
    return OGRERR_NONE;
10851
0
}
10852
10853
/************************************************************************/
10854
/*                             OSRCleanup()                             */
10855
/************************************************************************/
10856
10857
static void CleanupSRSWGS84Mutex();
10858
10859
/**
10860
 * \brief Cleanup cached SRS related memory.
10861
 *
10862
 * This function will attempt to cleanup any cache spatial reference
10863
 * related information, such as cached tables of coordinate systems.
10864
 *
10865
 * This function should not be called concurrently with any other GDAL/OGR
10866
 * function. It is meant at being called once before process termination
10867
 * (typically from the main thread). CPLCleanupTLS() might be used to clean
10868
 * thread-specific resources before thread termination.
10869
 */
10870
void OSRCleanup(void)
10871
10872
0
{
10873
0
    OGRCTDumpStatistics();
10874
0
    CSVDeaccess(nullptr);
10875
0
    CleanupSRSWGS84Mutex();
10876
0
    OSRCTCleanCache();
10877
0
    OSRCleanupTLSContext();
10878
0
}
10879
10880
/************************************************************************/
10881
/*                              GetAxesCount()                          */
10882
/************************************************************************/
10883
10884
/**
10885
 * \brief Return the number of axis of the coordinate system of the CRS.
10886
 *
10887
 * @since GDAL 3.0
10888
 */
10889
int OGRSpatialReference::GetAxesCount() const
10890
0
{
10891
0
    TAKE_OPTIONAL_LOCK();
10892
10893
0
    int axisCount = 0;
10894
0
    d->refreshProjObj();
10895
0
    if (d->m_pj_crs == nullptr)
10896
0
    {
10897
0
        return 0;
10898
0
    }
10899
0
    d->demoteFromBoundCRS();
10900
0
    auto ctxt = d->getPROJContext();
10901
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10902
0
    {
10903
0
        for (int i = 0;; i++)
10904
0
        {
10905
0
            auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10906
0
            if (!subCRS)
10907
0
                break;
10908
0
            if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10909
0
            {
10910
0
                auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10911
0
                if (baseCRS)
10912
0
                {
10913
0
                    proj_destroy(subCRS);
10914
0
                    subCRS = baseCRS;
10915
0
                }
10916
0
            }
10917
0
            auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10918
0
            if (cs)
10919
0
            {
10920
0
                axisCount += proj_cs_get_axis_count(ctxt, cs);
10921
0
                proj_destroy(cs);
10922
0
            }
10923
0
            proj_destroy(subCRS);
10924
0
        }
10925
0
    }
10926
0
    else
10927
0
    {
10928
0
        auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10929
0
        if (cs)
10930
0
        {
10931
0
            axisCount = proj_cs_get_axis_count(ctxt, cs);
10932
0
            proj_destroy(cs);
10933
0
        }
10934
0
    }
10935
0
    d->undoDemoteFromBoundCRS();
10936
0
    return axisCount;
10937
0
}
10938
10939
/************************************************************************/
10940
/*                           OSRGetAxesCount()                          */
10941
/************************************************************************/
10942
10943
/**
10944
 * \brief Return the number of axis of the coordinate system of the CRS.
10945
 *
10946
 * This method is the equivalent of the C++ method
10947
 * OGRSpatialReference::GetAxesCount()
10948
 *
10949
 * @since GDAL 3.1
10950
 */
10951
int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10952
10953
0
{
10954
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10955
10956
0
    return ToPointer(hSRS)->GetAxesCount();
10957
0
}
10958
10959
/************************************************************************/
10960
/*                              GetAxis()                               */
10961
/************************************************************************/
10962
10963
/**
10964
 * \brief Fetch the orientation of one axis.
10965
 *
10966
 * Fetches the request axis (iAxis - zero based) from the
10967
 * indicated portion of the coordinate system (pszTargetKey) which
10968
 * should be either "GEOGCS" or "PROJCS".
10969
 *
10970
 * No CPLError is issued on routine failures (such as not finding the AXIS).
10971
 *
10972
 * This method is equivalent to the C function OSRGetAxis().
10973
 *
10974
 * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10975
 * "GEOGCS").
10976
 * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10977
 * @param peOrientation location into which to place the fetch orientation, may
10978
 * be NULL.
10979
 * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10980
 * factor. May be NULL. Only set if pszTargetKey == NULL
10981
 *
10982
 * @return the name of the axis or NULL on failure.
10983
 */
10984
10985
const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10986
                                         OGRAxisOrientation *peOrientation,
10987
                                         double *pdfConvUnit) const
10988
10989
0
{
10990
0
    TAKE_OPTIONAL_LOCK();
10991
10992
0
    if (peOrientation != nullptr)
10993
0
        *peOrientation = OAO_Other;
10994
0
    if (pdfConvUnit != nullptr)
10995
0
        *pdfConvUnit = 0;
10996
10997
0
    d->refreshProjObj();
10998
0
    if (d->m_pj_crs == nullptr)
10999
0
    {
11000
0
        return nullptr;
11001
0
    }
11002
11003
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11004
0
    if (pszTargetKey == nullptr && iAxis <= 2)
11005
0
    {
11006
0
        auto ctxt = d->getPROJContext();
11007
11008
0
        int iAxisModified = iAxis;
11009
11010
0
        d->demoteFromBoundCRS();
11011
11012
0
        PJ *cs = nullptr;
11013
0
        if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11014
0
        {
11015
0
            auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11016
0
            if (horizCRS)
11017
0
            {
11018
0
                if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11019
0
                {
11020
0
                    auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11021
0
                    if (baseCRS)
11022
0
                    {
11023
0
                        proj_destroy(horizCRS);
11024
0
                        horizCRS = baseCRS;
11025
0
                    }
11026
0
                }
11027
0
                cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11028
0
                proj_destroy(horizCRS);
11029
0
                if (cs)
11030
0
                {
11031
0
                    if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11032
0
                    {
11033
0
                        iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11034
0
                        proj_destroy(cs);
11035
0
                        cs = nullptr;
11036
0
                    }
11037
0
                }
11038
0
            }
11039
11040
0
            if (cs == nullptr)
11041
0
            {
11042
0
                auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11043
0
                if (vertCRS)
11044
0
                {
11045
0
                    if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11046
0
                    {
11047
0
                        auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11048
0
                        if (baseCRS)
11049
0
                        {
11050
0
                            proj_destroy(vertCRS);
11051
0
                            vertCRS = baseCRS;
11052
0
                        }
11053
0
                    }
11054
11055
0
                    cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11056
0
                    proj_destroy(vertCRS);
11057
0
                }
11058
0
            }
11059
0
        }
11060
0
        else
11061
0
        {
11062
0
            cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11063
0
        }
11064
11065
0
        if (cs)
11066
0
        {
11067
0
            const char *pszName = nullptr;
11068
0
            const char *pszOrientation = nullptr;
11069
0
            double dfConvFactor = 0.0;
11070
0
            proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11071
0
                                  &pszOrientation, &dfConvFactor, nullptr,
11072
0
                                  nullptr, nullptr);
11073
11074
0
            if (pdfConvUnit != nullptr)
11075
0
            {
11076
0
                *pdfConvUnit = dfConvFactor;
11077
0
            }
11078
11079
0
            if (pszName && pszOrientation)
11080
0
            {
11081
0
                d->m_osAxisName[iAxis] = pszName;
11082
0
                if (peOrientation)
11083
0
                {
11084
0
                    if (EQUAL(pszOrientation, "NORTH"))
11085
0
                        *peOrientation = OAO_North;
11086
0
                    else if (EQUAL(pszOrientation, "EAST"))
11087
0
                        *peOrientation = OAO_East;
11088
0
                    else if (EQUAL(pszOrientation, "SOUTH"))
11089
0
                        *peOrientation = OAO_South;
11090
0
                    else if (EQUAL(pszOrientation, "WEST"))
11091
0
                        *peOrientation = OAO_West;
11092
0
                    else if (EQUAL(pszOrientation, "UP"))
11093
0
                        *peOrientation = OAO_Up;
11094
0
                    else if (EQUAL(pszOrientation, "DOWN"))
11095
0
                        *peOrientation = OAO_Down;
11096
0
                }
11097
0
                proj_destroy(cs);
11098
0
                d->undoDemoteFromBoundCRS();
11099
0
                return d->m_osAxisName[iAxis].c_str();
11100
0
            }
11101
0
            proj_destroy(cs);
11102
0
        }
11103
0
        d->undoDemoteFromBoundCRS();
11104
0
    }
11105
11106
    /* -------------------------------------------------------------------- */
11107
    /*      Find the target node.                                           */
11108
    /* -------------------------------------------------------------------- */
11109
0
    const OGR_SRSNode *poNode = nullptr;
11110
11111
0
    if (pszTargetKey == nullptr)
11112
0
        poNode = GetRoot();
11113
0
    else
11114
0
        poNode = GetAttrNode(pszTargetKey);
11115
11116
0
    if (poNode == nullptr)
11117
0
        return nullptr;
11118
11119
    /* -------------------------------------------------------------------- */
11120
    /*      Find desired child AXIS.                                        */
11121
    /* -------------------------------------------------------------------- */
11122
0
    const OGR_SRSNode *poAxis = nullptr;
11123
0
    const int nChildCount = poNode->GetChildCount();
11124
11125
0
    for (int iChild = 0; iChild < nChildCount; iChild++)
11126
0
    {
11127
0
        const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11128
11129
0
        if (!EQUAL(poChild->GetValue(), "AXIS"))
11130
0
            continue;
11131
11132
0
        if (iAxis == 0)
11133
0
        {
11134
0
            poAxis = poChild;
11135
0
            break;
11136
0
        }
11137
0
        iAxis--;
11138
0
    }
11139
11140
0
    if (poAxis == nullptr)
11141
0
        return nullptr;
11142
11143
0
    if (poAxis->GetChildCount() < 2)
11144
0
        return nullptr;
11145
11146
    /* -------------------------------------------------------------------- */
11147
    /*      Extract name and orientation if possible.                       */
11148
    /* -------------------------------------------------------------------- */
11149
0
    if (peOrientation != nullptr)
11150
0
    {
11151
0
        const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11152
11153
0
        if (EQUAL(pszOrientation, "NORTH"))
11154
0
            *peOrientation = OAO_North;
11155
0
        else if (EQUAL(pszOrientation, "EAST"))
11156
0
            *peOrientation = OAO_East;
11157
0
        else if (EQUAL(pszOrientation, "SOUTH"))
11158
0
            *peOrientation = OAO_South;
11159
0
        else if (EQUAL(pszOrientation, "WEST"))
11160
0
            *peOrientation = OAO_West;
11161
0
        else if (EQUAL(pszOrientation, "UP"))
11162
0
            *peOrientation = OAO_Up;
11163
0
        else if (EQUAL(pszOrientation, "DOWN"))
11164
0
            *peOrientation = OAO_Down;
11165
0
        else if (EQUAL(pszOrientation, "OTHER"))
11166
0
            *peOrientation = OAO_Other;
11167
0
        else
11168
0
        {
11169
0
            CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11170
0
                     pszOrientation);
11171
0
        }
11172
0
    }
11173
11174
0
    return poAxis->GetChild(0)->GetValue();
11175
0
}
11176
11177
/************************************************************************/
11178
/*                             OSRGetAxis()                             */
11179
/************************************************************************/
11180
11181
/**
11182
 * \brief Fetch the orientation of one axis.
11183
 *
11184
 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11185
 */
11186
const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11187
                       int iAxis, OGRAxisOrientation *peOrientation)
11188
11189
0
{
11190
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11191
11192
0
    return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11193
0
}
11194
11195
/************************************************************************/
11196
/*                         OSRAxisEnumToName()                          */
11197
/************************************************************************/
11198
11199
/**
11200
 * \brief Return the string representation for the OGRAxisOrientation
11201
 * enumeration.
11202
 *
11203
 * For example "NORTH" for OAO_North.
11204
 *
11205
 * @return an internal string
11206
 */
11207
const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11208
11209
0
{
11210
0
    if (eOrientation == OAO_North)
11211
0
        return "NORTH";
11212
0
    if (eOrientation == OAO_East)
11213
0
        return "EAST";
11214
0
    if (eOrientation == OAO_South)
11215
0
        return "SOUTH";
11216
0
    if (eOrientation == OAO_West)
11217
0
        return "WEST";
11218
0
    if (eOrientation == OAO_Up)
11219
0
        return "UP";
11220
0
    if (eOrientation == OAO_Down)
11221
0
        return "DOWN";
11222
0
    if (eOrientation == OAO_Other)
11223
0
        return "OTHER";
11224
11225
0
    return "UNKNOWN";
11226
0
}
11227
11228
/************************************************************************/
11229
/*                              SetAxes()                               */
11230
/************************************************************************/
11231
11232
/**
11233
 * \brief Set the axes for a coordinate system.
11234
 *
11235
 * Set the names, and orientations of the axes for either a projected
11236
 * (PROJCS) or geographic (GEOGCS) coordinate system.
11237
 *
11238
 * This method is equivalent to the C function OSRSetAxes().
11239
 *
11240
 * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11241
 * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11242
 * @param eXAxisOrientation normally OAO_East.
11243
 * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11244
 * @param eYAxisOrientation normally OAO_North.
11245
 *
11246
 * @return OGRERR_NONE on success or an error code.
11247
 */
11248
11249
OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11250
                                    const char *pszXAxisName,
11251
                                    OGRAxisOrientation eXAxisOrientation,
11252
                                    const char *pszYAxisName,
11253
                                    OGRAxisOrientation eYAxisOrientation)
11254
11255
0
{
11256
0
    TAKE_OPTIONAL_LOCK();
11257
11258
    /* -------------------------------------------------------------------- */
11259
    /*      Find the target node.                                           */
11260
    /* -------------------------------------------------------------------- */
11261
0
    OGR_SRSNode *poNode = nullptr;
11262
11263
0
    if (pszTargetKey == nullptr)
11264
0
        poNode = GetRoot();
11265
0
    else
11266
0
        poNode = GetAttrNode(pszTargetKey);
11267
11268
0
    if (poNode == nullptr)
11269
0
        return OGRERR_FAILURE;
11270
11271
    /* -------------------------------------------------------------------- */
11272
    /*      Strip any existing AXIS children.                               */
11273
    /* -------------------------------------------------------------------- */
11274
0
    while (poNode->FindChild("AXIS") >= 0)
11275
0
        poNode->DestroyChild(poNode->FindChild("AXIS"));
11276
11277
    /* -------------------------------------------------------------------- */
11278
    /*      Insert desired axes                                             */
11279
    /* -------------------------------------------------------------------- */
11280
0
    OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11281
11282
0
    poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11283
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11284
11285
0
    poNode->AddChild(poAxis);
11286
11287
0
    poAxis = new OGR_SRSNode("AXIS");
11288
11289
0
    poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11290
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11291
11292
0
    poNode->AddChild(poAxis);
11293
11294
0
    return OGRERR_NONE;
11295
0
}
11296
11297
/************************************************************************/
11298
/*                             OSRSetAxes()                             */
11299
/************************************************************************/
11300
/**
11301
 * \brief Set the axes for a coordinate system.
11302
 *
11303
 * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11304
 */
11305
OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11306
                  const char *pszXAxisName,
11307
                  OGRAxisOrientation eXAxisOrientation,
11308
                  const char *pszYAxisName,
11309
                  OGRAxisOrientation eYAxisOrientation)
11310
0
{
11311
0
    VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11312
11313
0
    return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11314
0
                                    eXAxisOrientation, pszYAxisName,
11315
0
                                    eYAxisOrientation);
11316
0
}
11317
11318
/************************************************************************/
11319
/*                       OSRExportToMICoordSys()                        */
11320
/************************************************************************/
11321
/**
11322
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11323
 *
11324
 * This method is the equivalent of the C++ method
11325
 * OGRSpatialReference::exportToMICoordSys
11326
 */
11327
OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11328
11329
0
{
11330
0
    VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11331
11332
0
    *ppszReturn = nullptr;
11333
11334
0
    return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11335
0
}
11336
11337
/************************************************************************/
11338
/*                         exportToMICoordSys()                         */
11339
/************************************************************************/
11340
11341
/**
11342
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11343
 *
11344
 * Note that the returned WKT string should be freed with
11345
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
11346
 *
11347
 * This method is the same as the C function OSRExportToMICoordSys().
11348
 *
11349
 * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11350
 * definition will be assigned.
11351
 *
11352
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11353
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11354
 */
11355
11356
OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11357
11358
0
{
11359
0
    *ppszResult = MITABSpatialRef2CoordSys(this);
11360
0
    if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11361
0
        return OGRERR_NONE;
11362
11363
0
    return OGRERR_FAILURE;
11364
0
}
11365
11366
/************************************************************************/
11367
/*                       OSRImportFromMICoordSys()                      */
11368
/************************************************************************/
11369
/**
11370
 * \brief Import Mapinfo style CoordSys definition.
11371
 *
11372
 * This method is the equivalent of the C++ method
11373
 * OGRSpatialReference::importFromMICoordSys
11374
 */
11375
11376
OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11377
                               const char *pszCoordSys)
11378
11379
0
{
11380
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11381
11382
0
    return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11383
0
}
11384
11385
/************************************************************************/
11386
/*                        importFromMICoordSys()                        */
11387
/************************************************************************/
11388
11389
/**
11390
 * \brief Import Mapinfo style CoordSys definition.
11391
 *
11392
 * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11393
 * definition string.
11394
 *
11395
 * This method is the equivalent of the C function OSRImportFromMICoordSys().
11396
 *
11397
 * @param pszCoordSys Mapinfo style CoordSys definition string.
11398
 *
11399
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11400
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11401
 */
11402
11403
OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11404
11405
0
{
11406
0
    OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11407
11408
0
    if (poResult == nullptr)
11409
0
        return OGRERR_FAILURE;
11410
11411
0
    *this = *poResult;
11412
0
    delete poResult;
11413
11414
0
    return OGRERR_NONE;
11415
0
}
11416
11417
/************************************************************************/
11418
/*                        OSRCalcInvFlattening()                        */
11419
/************************************************************************/
11420
11421
/**
11422
 * \brief Compute inverse flattening from semi-major and semi-minor axis
11423
 *
11424
 * @param dfSemiMajor Semi-major axis length.
11425
 * @param dfSemiMinor Semi-minor axis length.
11426
 *
11427
 * @return inverse flattening, or 0 if both axis are equal.
11428
 * @since GDAL 2.0
11429
 */
11430
11431
double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11432
0
{
11433
0
    if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11434
0
        return 0;
11435
0
    if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11436
0
    {
11437
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11438
0
                 "OSRCalcInvFlattening(): Wrong input values");
11439
0
        return 0;
11440
0
    }
11441
11442
0
    return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11443
0
}
11444
11445
/************************************************************************/
11446
/*                        OSRCalcInvFlattening()                        */
11447
/************************************************************************/
11448
11449
/**
11450
 * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11451
 *
11452
 * @param dfSemiMajor Semi-major axis length.
11453
 * @param dfInvFlattening Inverse flattening or 0 for sphere.
11454
 *
11455
 * @return semi-minor axis
11456
 * @since GDAL 2.0
11457
 */
11458
11459
double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11460
                                         double dfInvFlattening)
11461
0
{
11462
0
    if (fabs(dfInvFlattening) < 0.000000000001)
11463
0
        return dfSemiMajor;
11464
0
    if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11465
0
    {
11466
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11467
0
                 "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11468
0
        return dfSemiMajor;
11469
0
    }
11470
11471
0
    return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11472
0
}
11473
11474
/************************************************************************/
11475
/*                        GetWGS84SRS()                                 */
11476
/************************************************************************/
11477
11478
static OGRSpatialReference *poSRSWGS84 = nullptr;
11479
static CPLMutex *hMutex = nullptr;
11480
11481
/**
11482
 * \brief Returns an instance of a SRS object with WGS84 WKT.
11483
 *
11484
 * Note: the instance will have
11485
 * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11486
 *
11487
 * The reference counter of the returned object is not increased by this
11488
 * operation.
11489
 *
11490
 * @return instance.
11491
 * @since GDAL 2.0
11492
 */
11493
11494
OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11495
0
{
11496
0
    CPLMutexHolderD(&hMutex);
11497
0
    if (poSRSWGS84 == nullptr)
11498
0
    {
11499
0
        poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11500
0
        poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11501
0
    }
11502
0
    return poSRSWGS84;
11503
0
}
11504
11505
/************************************************************************/
11506
/*                        CleanupSRSWGS84Mutex()                       */
11507
/************************************************************************/
11508
11509
static void CleanupSRSWGS84Mutex()
11510
0
{
11511
0
    if (hMutex != nullptr)
11512
0
    {
11513
0
        poSRSWGS84->Release();
11514
0
        poSRSWGS84 = nullptr;
11515
0
        CPLDestroyMutex(hMutex);
11516
0
        hMutex = nullptr;
11517
0
    }
11518
0
}
11519
11520
/************************************************************************/
11521
/*                         OSRImportFromProj4()                         */
11522
/************************************************************************/
11523
/**
11524
 * \brief Import PROJ coordinate string.
11525
 *
11526
 * This function is the same as OGRSpatialReference::importFromProj4().
11527
 */
11528
OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11529
11530
0
{
11531
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11532
11533
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11534
0
}
11535
11536
/************************************************************************/
11537
/*                          importFromProj4()                           */
11538
/************************************************************************/
11539
11540
/**
11541
 * \brief Import PROJ coordinate string.
11542
 *
11543
 * The OGRSpatialReference is initialized from the passed PROJs style
11544
 * coordinate system string.
11545
 *
11546
 * Example:
11547
 *   pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11548
 *
11549
 * It is also possible to import "+init=epsg:n" style definitions. Those are
11550
 * a legacy syntax that should be avoided in the future. In particular they will
11551
 * result in CRS objects whose axis order might not correspond to the official
11552
 * EPSG axis order.
11553
 *
11554
 * This method is the equivalent of the C function OSRImportFromProj4().
11555
 *
11556
 * @param pszProj4 the PROJ style string.
11557
 *
11558
 * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11559
 */
11560
11561
OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11562
11563
0
{
11564
0
    TAKE_OPTIONAL_LOCK();
11565
11566
0
    if (strlen(pszProj4) >= 10000)
11567
0
    {
11568
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11569
0
        return OGRERR_CORRUPT_DATA;
11570
0
    }
11571
11572
    /* -------------------------------------------------------------------- */
11573
    /*      Clear any existing definition.                                  */
11574
    /* -------------------------------------------------------------------- */
11575
0
    Clear();
11576
11577
0
    CPLString osProj4(pszProj4);
11578
0
    if (osProj4.find("type=crs") == std::string::npos)
11579
0
    {
11580
0
        osProj4 += " +type=crs";
11581
0
    }
11582
11583
0
    if (osProj4.find("+init=epsg:") != std::string::npos &&
11584
0
        getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11585
0
    {
11586
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11587
0
                     "+init=epsg:XXXX syntax is deprecated. It might return "
11588
0
                     "a CRS with a non-EPSG compliant axis order.");
11589
0
    }
11590
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11591
0
    d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11592
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11593
0
    return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11594
0
}
11595
11596
/************************************************************************/
11597
/*                          OSRExportToProj4()                          */
11598
/************************************************************************/
11599
/**
11600
 * \brief Export coordinate system in PROJ.4 legacy format.
11601
 *
11602
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11603
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11604
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11605
 * will be missing most of the time. PROJ strings to encode CRS should be
11606
 * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11607
 * is the recommended way.
11608
 *
11609
 * This function is the same as OGRSpatialReference::exportToProj4().
11610
 */
11611
OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11612
                                    char **ppszReturn)
11613
11614
0
{
11615
0
    VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11616
11617
0
    *ppszReturn = nullptr;
11618
11619
0
    return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11620
0
}
11621
11622
/************************************************************************/
11623
/*                           exportToProj4()                            */
11624
/************************************************************************/
11625
11626
/**
11627
 * \brief Export coordinate system in PROJ.4 legacy format.
11628
 *
11629
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11630
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11631
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11632
 * will be missing most of the time. PROJ strings to encode CRS should be
11633
 * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11634
 * representation is the recommended way.
11635
 *
11636
 * Converts the loaded coordinate reference system into PROJ format
11637
 * to the extent possible.  The string returned in ppszProj4 should be
11638
 * deallocated by the caller with CPLFree() when no longer needed.
11639
 *
11640
 * LOCAL_CS coordinate systems are not translatable.  An empty string
11641
 * will be returned along with OGRERR_NONE.
11642
 *
11643
 * Special processing for Transverse Mercator:
11644
 * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11645
 * set to YES, the PROJ definition built from the SRS will use the +approx flag
11646
 * for the tmerc and utm projection methods, rather than the more accurate
11647
 * method.
11648
 *
11649
 * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11650
 * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11651
 * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11652
 * added. This automatic addition may be disabled by setting the
11653
 * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11654
 *
11655
 * This method is the equivalent of the C function OSRExportToProj4().
11656
 *
11657
 * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11658
 * will be assigned.
11659
 *
11660
 * @return OGRERR_NONE on success or an error code on failure.
11661
 */
11662
11663
OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11664
11665
0
{
11666
    // In the past calling this method was thread-safe, even if we never
11667
    // guaranteed it. Now proj_as_proj_string() will cache the result
11668
    // internally, so this is no longer thread-safe.
11669
0
    std::lock_guard oLock(d->m_mutex);
11670
11671
0
    d->refreshProjObj();
11672
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11673
0
    {
11674
0
        *ppszProj4 = CPLStrdup("");
11675
0
        return OGRERR_FAILURE;
11676
0
    }
11677
11678
    // OSR_USE_ETMERC is here just for legacy
11679
0
    bool bForceApproxTMerc = false;
11680
0
    const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11681
0
    if (pszUseETMERC && pszUseETMERC[0])
11682
0
    {
11683
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11684
0
                     "OSR_USE_ETMERC is a legacy configuration option, which "
11685
0
                     "now has only effect when set to NO (YES is the default). "
11686
0
                     "Use OSR_USE_APPROX_TMERC=YES instead");
11687
0
        bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11688
0
    }
11689
0
    else
11690
0
    {
11691
0
        const char *pszUseApproxTMERC =
11692
0
            CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11693
0
        if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11694
0
        {
11695
0
            bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11696
0
        }
11697
0
    }
11698
0
    const char *options[] = {
11699
0
        bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11700
11701
0
    const char *projString = proj_as_proj_string(
11702
0
        d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11703
11704
0
    PJ *boundCRS = nullptr;
11705
0
    if (projString &&
11706
0
        (strstr(projString, "+datum=") == nullptr ||
11707
0
         d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11708
0
        CPLTestBool(
11709
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11710
0
    {
11711
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11712
0
            d->getPROJContext(), d->m_pj_crs, true,
11713
0
            strstr(projString, "+datum=") == nullptr);
11714
0
        if (boundCRS)
11715
0
        {
11716
0
            projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11717
0
                                             PJ_PROJ_4, options);
11718
0
        }
11719
0
    }
11720
11721
0
    if (projString == nullptr)
11722
0
    {
11723
0
        *ppszProj4 = CPLStrdup("");
11724
0
        proj_destroy(boundCRS);
11725
0
        return OGRERR_FAILURE;
11726
0
    }
11727
0
    *ppszProj4 = CPLStrdup(projString);
11728
0
    proj_destroy(boundCRS);
11729
0
    char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11730
0
    if (pszTypeCrs)
11731
0
        *pszTypeCrs = '\0';
11732
0
    return OGRERR_NONE;
11733
0
}
11734
11735
/************************************************************************/
11736
/*                            morphToESRI()                             */
11737
/************************************************************************/
11738
/**
11739
 * \brief Convert in place to ESRI WKT format.
11740
 *
11741
 * The value nodes of this coordinate system are modified in various manners
11742
 * more closely map onto the ESRI concept of WKT format.  This includes
11743
 * renaming a variety of projections and arguments, and stripping out
11744
 * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11745
 *
11746
 * \note Since GDAL 3.0, this function has only user-visible effects at
11747
 * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11748
 * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11749
 *
11750
 * This does the same as the C function OSRMorphToESRI().
11751
 *
11752
 * @return OGRERR_NONE unless something goes badly wrong.
11753
 * @deprecated
11754
 */
11755
11756
OGRErr OGRSpatialReference::morphToESRI()
11757
11758
0
{
11759
0
    TAKE_OPTIONAL_LOCK();
11760
11761
0
    d->refreshProjObj();
11762
0
    d->setMorphToESRI(true);
11763
11764
0
    return OGRERR_NONE;
11765
0
}
11766
11767
/************************************************************************/
11768
/*                           OSRMorphToESRI()                           */
11769
/************************************************************************/
11770
11771
/**
11772
 * \brief Convert in place to ESRI WKT format.
11773
 *
11774
 * This function is the same as the C++ method
11775
 * OGRSpatialReference::morphToESRI().
11776
 */
11777
OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11778
11779
0
{
11780
0
    VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11781
11782
0
    return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11783
0
}
11784
11785
/************************************************************************/
11786
/*                           morphFromESRI()                            */
11787
/************************************************************************/
11788
11789
/**
11790
 * \brief Convert in place from ESRI WKT format.
11791
 *
11792
 * The value notes of this coordinate system are modified in various manners
11793
 * to adhere more closely to the WKT standard.  This mostly involves
11794
 * translating a variety of ESRI names for projections, arguments and
11795
 * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11796
 * translation of EPSG to WKT for the CT specification.
11797
 *
11798
 * \note Since GDAL 3.0, this function is essentially a no-operation, since
11799
 * morphing from ESRI is automatically done by importFromWkt(). Its only
11800
 * effect is to undo the effect of a potential prior call to morphToESRI().
11801
 *
11802
 * This does the same as the C function OSRMorphFromESRI().
11803
 *
11804
 * @return OGRERR_NONE unless something goes badly wrong.
11805
 * @deprecated
11806
 */
11807
11808
OGRErr OGRSpatialReference::morphFromESRI()
11809
11810
0
{
11811
0
    TAKE_OPTIONAL_LOCK();
11812
11813
0
    d->refreshProjObj();
11814
0
    d->setMorphToESRI(false);
11815
11816
0
    return OGRERR_NONE;
11817
0
}
11818
11819
/************************************************************************/
11820
/*                          OSRMorphFromESRI()                          */
11821
/************************************************************************/
11822
11823
/**
11824
 * \brief Convert in place from ESRI WKT format.
11825
 *
11826
 * This function is the same as the C++ method
11827
 * OGRSpatialReference::morphFromESRI().
11828
 */
11829
OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11830
11831
0
{
11832
0
    VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11833
11834
0
    return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11835
0
}
11836
11837
/************************************************************************/
11838
/*                            FindMatches()                             */
11839
/************************************************************************/
11840
11841
/**
11842
 * \brief Try to identify a match between the passed SRS and a related SRS
11843
 * in a catalog.
11844
 *
11845
 * Matching may be partial, or may fail.
11846
 * Returned entries will be sorted by decreasing match confidence (first
11847
 * entry has the highest match confidence).
11848
 *
11849
 * The exact way matching is done may change in future versions. Starting with
11850
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
11851
 *
11852
 * This method is the same as OSRFindMatches().
11853
 *
11854
 * @param papszOptions NULL terminated list of options or NULL
11855
 * @param pnEntries Output parameter. Number of values in the returned array.
11856
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11857
 * will be allocated to an array of *pnEntries whose values between 0 and 100
11858
 * indicate the confidence in the match. 100 is the highest confidence level.
11859
 * The array must be freed with CPLFree().
11860
 *
11861
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11862
 * with OSRFreeSRSArray()
11863
 *
11864
 * @since GDAL 2.3
11865
 *
11866
 * @see OGRSpatialReference::FindBestMatch()
11867
 */
11868
OGRSpatialReferenceH *
11869
OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11870
                                 int **ppanMatchConfidence) const
11871
0
{
11872
0
    TAKE_OPTIONAL_LOCK();
11873
11874
0
    CPL_IGNORE_RET_VAL(papszOptions);
11875
11876
0
    if (pnEntries)
11877
0
        *pnEntries = 0;
11878
0
    if (ppanMatchConfidence)
11879
0
        *ppanMatchConfidence = nullptr;
11880
11881
0
    d->refreshProjObj();
11882
0
    if (!d->m_pj_crs)
11883
0
        return nullptr;
11884
11885
0
    int *panConfidence = nullptr;
11886
0
    auto ctxt = d->getPROJContext();
11887
0
    auto list =
11888
0
        proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11889
0
    if (!list)
11890
0
        return nullptr;
11891
11892
0
    const int nMatches = proj_list_get_count(list);
11893
11894
0
    if (pnEntries)
11895
0
        *pnEntries = static_cast<int>(nMatches);
11896
0
    OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11897
0
        CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11898
0
    if (ppanMatchConfidence)
11899
0
    {
11900
0
        *ppanMatchConfidence =
11901
0
            static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11902
0
    }
11903
11904
0
    bool bSortAgain = false;
11905
11906
0
    for (int i = 0; i < nMatches; i++)
11907
0
    {
11908
0
        PJ *obj = proj_list_get(ctxt, list, i);
11909
0
        CPLAssert(obj);
11910
0
        OGRSpatialReference *poSRS = new OGRSpatialReference();
11911
0
        poSRS->d->setPjCRS(obj);
11912
0
        pahRet[i] = ToHandle(poSRS);
11913
11914
        // Identify matches that only differ by axis order
11915
0
        if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11916
0
            poSRS->GetAxesCount() == 2 &&
11917
0
            GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11918
0
        {
11919
0
            OGRAxisOrientation eThisAxis0 = OAO_Other;
11920
0
            OGRAxisOrientation eThisAxis1 = OAO_Other;
11921
0
            OGRAxisOrientation eSRSAxis0 = OAO_Other;
11922
0
            OGRAxisOrientation eSRSAxis1 = OAO_Other;
11923
0
            GetAxis(nullptr, 0, &eThisAxis0);
11924
0
            GetAxis(nullptr, 1, &eThisAxis1);
11925
0
            poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11926
0
            poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11927
0
            if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11928
0
                eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11929
0
            {
11930
0
                auto pj_crs_normalized =
11931
0
                    proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11932
0
                if (pj_crs_normalized)
11933
0
                {
11934
0
                    if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11935
0
                                              PJ_COMP_EQUIVALENT))
11936
0
                    {
11937
0
                        bSortAgain = true;
11938
0
                        panConfidence[i] = 90;
11939
0
                        poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11940
0
                    }
11941
0
                    proj_destroy(pj_crs_normalized);
11942
0
                }
11943
0
            }
11944
0
        }
11945
11946
0
        if (ppanMatchConfidence)
11947
0
            (*ppanMatchConfidence)[i] = panConfidence[i];
11948
0
    }
11949
11950
0
    if (bSortAgain)
11951
0
    {
11952
0
        std::vector<int> anIndices;
11953
0
        for (int i = 0; i < nMatches; ++i)
11954
0
            anIndices.push_back(i);
11955
11956
0
        std::stable_sort(anIndices.begin(), anIndices.end(),
11957
0
                         [&panConfidence](int i, int j)
11958
0
                         { return panConfidence[i] > panConfidence[j]; });
11959
11960
0
        OGRSpatialReferenceH *pahRetSorted =
11961
0
            static_cast<OGRSpatialReferenceH *>(
11962
0
                CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11963
0
        for (int i = 0; i < nMatches; ++i)
11964
0
        {
11965
0
            pahRetSorted[i] = pahRet[anIndices[i]];
11966
0
            if (ppanMatchConfidence)
11967
0
                (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11968
0
        }
11969
0
        CPLFree(pahRet);
11970
0
        pahRet = pahRetSorted;
11971
0
    }
11972
11973
0
    pahRet[nMatches] = nullptr;
11974
0
    proj_list_destroy(list);
11975
0
    proj_int_list_destroy(panConfidence);
11976
11977
0
    return pahRet;
11978
0
}
11979
11980
/************************************************************************/
11981
/*                          importFromEPSGA()                           */
11982
/************************************************************************/
11983
11984
/**
11985
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
11986
 * code.
11987
 *
11988
 * This method will initialize the spatial reference based on the
11989
 * passed in EPSG CRS code found in the PROJ database.
11990
 *
11991
 * Since GDAL 3.0, this method is identical to importFromEPSG().
11992
 *
11993
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11994
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
11995
 * such method available for the CRS. This behavior might not always be
11996
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11997
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11998
 * The AddGuessedTOWGS84() method can also be used for that purpose.
11999
 *
12000
 * The method will also by default substitute a deprecated EPSG code by its
12001
 * non-deprecated replacement. If this behavior is not desired, the
12002
 * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12003
 *
12004
 * This method is the same as the C function OSRImportFromEPSGA().
12005
 *
12006
 * @param nCode a CRS code.
12007
 *
12008
 * @return OGRERR_NONE on success, or an error code on failure.
12009
 */
12010
12011
OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12012
12013
0
{
12014
0
    TAKE_OPTIONAL_LOCK();
12015
12016
0
    Clear();
12017
12018
0
    const char *pszUseNonDeprecated =
12019
0
        CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12020
0
    const bool bUseNonDeprecated =
12021
0
        CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12022
0
    const bool bAddTOWGS84 = CPLTestBool(
12023
0
        CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12024
0
    auto tlsCache = OSRGetProjTLSCache();
12025
0
    if (tlsCache)
12026
0
    {
12027
0
        auto cachedObj =
12028
0
            tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12029
0
        if (cachedObj)
12030
0
        {
12031
0
            d->setPjCRS(cachedObj);
12032
0
            return OGRERR_NONE;
12033
0
        }
12034
0
    }
12035
12036
0
    CPLString osCode;
12037
0
    osCode.Printf("%d", nCode);
12038
0
    PJ *obj;
12039
0
    constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12040
0
    if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12041
0
    {
12042
0
        obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12043
0
                                        osCode.c_str(), PJ_CATEGORY_CRS, true,
12044
0
                                        nullptr);
12045
0
        if (!obj)
12046
0
        {
12047
0
            return OGRERR_FAILURE;
12048
0
        }
12049
0
    }
12050
0
    else
12051
0
    {
12052
        // Likely to be an ESRI CRS...
12053
0
        CPLErr eLastErrorType = CE_None;
12054
0
        CPLErrorNum eLastErrorNum = CPLE_None;
12055
0
        std::string osLastErrorMsg;
12056
0
        bool bIsESRI = false;
12057
0
        {
12058
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12059
0
            CPLErrorReset();
12060
0
            obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12061
0
                                            osCode.c_str(), PJ_CATEGORY_CRS,
12062
0
                                            true, nullptr);
12063
0
            if (!obj)
12064
0
            {
12065
0
                eLastErrorType = CPLGetLastErrorType();
12066
0
                eLastErrorNum = CPLGetLastErrorNo();
12067
0
                osLastErrorMsg = CPLGetLastErrorMsg();
12068
0
                obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12069
0
                                                osCode.c_str(), PJ_CATEGORY_CRS,
12070
0
                                                true, nullptr);
12071
0
                if (obj)
12072
0
                    bIsESRI = true;
12073
0
            }
12074
0
        }
12075
0
        if (!obj)
12076
0
        {
12077
0
            if (eLastErrorType != CE_None)
12078
0
                CPLError(eLastErrorType, eLastErrorNum, "%s",
12079
0
                         osLastErrorMsg.c_str());
12080
0
            return OGRERR_FAILURE;
12081
0
        }
12082
0
        if (bIsESRI)
12083
0
        {
12084
0
            CPLError(CE_Warning, CPLE_AppDefined,
12085
0
                     "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12086
0
                     "Assuming ESRI:%d was meant",
12087
0
                     nCode, nCode, nCode);
12088
0
        }
12089
0
    }
12090
12091
0
    if (bUseNonDeprecated && proj_is_deprecated(obj))
12092
0
    {
12093
0
        auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12094
0
        if (list)
12095
0
        {
12096
0
            const auto count = proj_list_get_count(list);
12097
0
            if (count == 1)
12098
0
            {
12099
0
                auto nonDeprecated =
12100
0
                    proj_list_get(d->getPROJContext(), list, 0);
12101
0
                if (nonDeprecated)
12102
0
                {
12103
0
                    if (pszUseNonDeprecated == nullptr)
12104
0
                    {
12105
0
                        const char *pszNewAuth =
12106
0
                            proj_get_id_auth_name(nonDeprecated, 0);
12107
0
                        const char *pszNewCode =
12108
0
                            proj_get_id_code(nonDeprecated, 0);
12109
0
                        CPLError(CE_Warning, CPLE_AppDefined,
12110
0
                                 "CRS EPSG:%d is deprecated. "
12111
0
                                 "Its non-deprecated replacement %s:%s "
12112
0
                                 "will be used instead. "
12113
0
                                 "To use the original CRS, set the "
12114
0
                                 "OSR_USE_NON_DEPRECATED "
12115
0
                                 "configuration option to NO.",
12116
0
                                 nCode, pszNewAuth ? pszNewAuth : "(null)",
12117
0
                                 pszNewCode ? pszNewCode : "(null)");
12118
0
                    }
12119
0
                    proj_destroy(obj);
12120
0
                    obj = nonDeprecated;
12121
0
                }
12122
0
            }
12123
0
        }
12124
0
        proj_list_destroy(list);
12125
0
    }
12126
12127
0
    if (bAddTOWGS84)
12128
0
    {
12129
0
        auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12130
0
                                                           obj, nullptr);
12131
0
        if (boundCRS)
12132
0
        {
12133
0
            proj_destroy(obj);
12134
0
            obj = boundCRS;
12135
0
        }
12136
0
    }
12137
12138
0
    d->setPjCRS(obj);
12139
12140
0
    if (tlsCache)
12141
0
    {
12142
0
        tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12143
0
                                     obj);
12144
0
    }
12145
12146
0
    return OGRERR_NONE;
12147
0
}
12148
12149
/************************************************************************/
12150
/*                          AddGuessedTOWGS84()                         */
12151
/************************************************************************/
12152
12153
/**
12154
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12155
 * to WGS84.
12156
 *
12157
 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12158
 * to WGS84 when there is one and only one such method available for the CRS.
12159
 * Note: this is more restrictive to how GDAL < 3 worked.
12160
 *
12161
 * This method is the same as the C function OSRAddGuessedTOWGS84().
12162
 *
12163
 * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12164
 * already a transformation to WGS84 or none matching could be found).
12165
 *
12166
 * @since GDAL 3.0.3
12167
 */
12168
OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12169
0
{
12170
0
    TAKE_OPTIONAL_LOCK();
12171
12172
0
    d->refreshProjObj();
12173
0
    if (!d->m_pj_crs)
12174
0
        return OGRERR_FAILURE;
12175
0
    auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12176
0
        d->getPROJContext(), d->m_pj_crs, false, true);
12177
0
    if (!boundCRS)
12178
0
    {
12179
0
        return OGRERR_FAILURE;
12180
0
    }
12181
0
    d->setPjCRS(boundCRS);
12182
0
    return OGRERR_NONE;
12183
0
}
12184
12185
/************************************************************************/
12186
/*                         OSRImportFromEPSGA()                         */
12187
/************************************************************************/
12188
12189
/**
12190
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12191
 * to WGS84.
12192
 *
12193
 * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12194
 *
12195
 * @since GDAL 3.0.3
12196
 */
12197
12198
OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12199
12200
0
{
12201
0
    VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12202
12203
0
    return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12204
0
}
12205
12206
/************************************************************************/
12207
/*                         OSRImportFromEPSGA()                         */
12208
/************************************************************************/
12209
12210
/**
12211
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12212
 * code.
12213
 *
12214
 * This function is the same as OGRSpatialReference::importFromEPSGA().
12215
 */
12216
12217
OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12218
12219
0
{
12220
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12221
12222
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12223
0
}
12224
12225
/************************************************************************/
12226
/*                           importFromEPSG()                           */
12227
/************************************************************************/
12228
12229
/**
12230
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12231
 * code.
12232
 *
12233
 * This method will initialize the spatial reference based on the
12234
 * passed in EPSG CRS code found in the PROJ database.
12235
 *
12236
 * This method is the same as the C function OSRImportFromEPSG().
12237
 *
12238
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12239
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12240
 * such method available for the CRS. This behavior might not always be
12241
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12242
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12243
 *
12244
 * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12245
 *
12246
 * @return OGRERR_NONE on success, or an error code on failure.
12247
 */
12248
12249
OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12250
12251
0
{
12252
0
    return importFromEPSGA(nCode);
12253
0
}
12254
12255
/************************************************************************/
12256
/*                         OSRImportFromEPSG()                          */
12257
/************************************************************************/
12258
12259
/**
12260
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12261
 * code.
12262
 *
12263
 * This function is the same as OGRSpatialReference::importFromEPSG().
12264
 */
12265
12266
OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12267
12268
0
{
12269
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12270
12271
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12272
0
}
12273
12274
/************************************************************************/
12275
/*                        EPSGTreatsAsLatLong()                         */
12276
/************************************************************************/
12277
12278
/**
12279
 * \brief This method returns TRUE if this geographic coordinate
12280
 * system should be treated as having lat/long coordinate ordering.
12281
 *
12282
 * Currently this returns TRUE for all geographic coordinate systems
12283
 * with axes set defining it as lat, long (prior to GDAL 3.10, it
12284
 * also checked that the CRS had belonged to EPSG authority, but this check
12285
 * has now been removed).
12286
 *
12287
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12288
 * geographic CRS imported with 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 geographic,
12293
 * or whose axes ordering is not latitude, longitude.
12294
 *
12295
 * This method is the same as the C function OSREPSGTreatsAsLatLong().
12296
 *
12297
 * @return TRUE or FALSE.
12298
 */
12299
12300
int OGRSpatialReference::EPSGTreatsAsLatLong() const
12301
12302
0
{
12303
0
    TAKE_OPTIONAL_LOCK();
12304
12305
0
    if (!IsGeographic())
12306
0
        return FALSE;
12307
12308
0
    d->demoteFromBoundCRS();
12309
12310
0
    bool ret = false;
12311
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12312
0
    {
12313
0
        auto horizCRS =
12314
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12315
0
        if (horizCRS)
12316
0
        {
12317
0
            auto cs =
12318
0
                proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12319
0
            if (cs)
12320
0
            {
12321
0
                const char *pszDirection = nullptr;
12322
0
                if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12323
0
                                          nullptr, &pszDirection, nullptr,
12324
0
                                          nullptr, nullptr, nullptr))
12325
0
                {
12326
0
                    if (EQUAL(pszDirection, "north"))
12327
0
                    {
12328
0
                        ret = true;
12329
0
                    }
12330
0
                }
12331
12332
0
                proj_destroy(cs);
12333
0
            }
12334
12335
0
            proj_destroy(horizCRS);
12336
0
        }
12337
0
    }
12338
0
    else
12339
0
    {
12340
0
        auto cs =
12341
0
            proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12342
0
        if (cs)
12343
0
        {
12344
0
            const char *pszDirection = nullptr;
12345
0
            if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12346
0
                                      nullptr, &pszDirection, nullptr, nullptr,
12347
0
                                      nullptr, nullptr))
12348
0
            {
12349
0
                if (EQUAL(pszDirection, "north"))
12350
0
                {
12351
0
                    ret = true;
12352
0
                }
12353
0
            }
12354
12355
0
            proj_destroy(cs);
12356
0
        }
12357
0
    }
12358
0
    d->undoDemoteFromBoundCRS();
12359
12360
0
    return ret;
12361
0
}
12362
12363
/************************************************************************/
12364
/*                       OSREPSGTreatsAsLatLong()                       */
12365
/************************************************************************/
12366
12367
/**
12368
 * \brief This function returns TRUE if this geographic coordinate
12369
 * system should be treated as having lat/long coordinate ordering.
12370
 *
12371
 * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12372
 */
12373
12374
int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12375
12376
0
{
12377
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12378
12379
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12380
0
}
12381
12382
/************************************************************************/
12383
/*                     EPSGTreatsAsNorthingEasting()                    */
12384
/************************************************************************/
12385
12386
/**
12387
 * \brief This method returns TRUE if this projected coordinate
12388
 * system should be treated as having northing/easting coordinate ordering.
12389
 *
12390
 * Currently this returns TRUE for all projected coordinate systems
12391
 * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12392
 * also checked that the CRS had belonged to EPSG authority, but this check
12393
 * has now been removed).
12394
 *
12395
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12396
 * projected CRS with northing, easting axis order imported with
12397
 * importFromEPSG() would cause this method to
12398
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12399
 * is now equivalent to importFromEPSGA().
12400
 *
12401
 * FALSE will be returned for all coordinate systems that are not projected,
12402
 * or whose axes ordering is not northing, easting.
12403
 *
12404
 * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12405
 *
12406
 * @return TRUE or FALSE.
12407
 *
12408
 * @since OGR 1.10.0
12409
 */
12410
12411
int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12412
12413
0
{
12414
0
    TAKE_OPTIONAL_LOCK();
12415
12416
0
    if (!IsProjected())
12417
0
        return FALSE;
12418
12419
0
    d->demoteFromBoundCRS();
12420
0
    PJ *projCRS;
12421
0
    const auto ctxt = d->getPROJContext();
12422
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12423
0
    {
12424
0
        projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12425
0
        if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12426
0
        {
12427
0
            d->undoDemoteFromBoundCRS();
12428
0
            proj_destroy(projCRS);
12429
0
            return FALSE;
12430
0
        }
12431
0
    }
12432
0
    else
12433
0
    {
12434
0
        projCRS = proj_clone(ctxt, d->m_pj_crs);
12435
0
    }
12436
12437
0
    bool ret = false;
12438
0
    auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12439
0
    proj_destroy(projCRS);
12440
0
    d->undoDemoteFromBoundCRS();
12441
12442
0
    if (cs)
12443
0
    {
12444
0
        ret = isNorthEastAxisOrder(ctxt, cs);
12445
0
        proj_destroy(cs);
12446
0
    }
12447
12448
0
    return ret;
12449
0
}
12450
12451
/************************************************************************/
12452
/*                     OSREPSGTreatsAsNorthingEasting()                 */
12453
/************************************************************************/
12454
12455
/**
12456
 * \brief This function returns TRUE if this projected coordinate
12457
 * system should be treated as having northing/easting coordinate ordering.
12458
 *
12459
 * This function is the same as
12460
 * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12461
 *
12462
 * @since OGR 1.10.0
12463
 */
12464
12465
int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12466
12467
0
{
12468
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12469
12470
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12471
0
}
12472
12473
/************************************************************************/
12474
/*                     ImportFromESRIWisconsinWKT()                     */
12475
/*                                                                      */
12476
/*      Search a ESRI State Plane WKT and import it.                    */
12477
/************************************************************************/
12478
12479
// This is only used by the HFA driver and somewhat dubious we really need that
12480
// Coming from an old ESRI merge
12481
12482
OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12483
                                                       double centralMeridian,
12484
                                                       double latOfOrigin,
12485
                                                       const char *unitsName,
12486
                                                       const char *crsName)
12487
0
{
12488
0
    TAKE_OPTIONAL_LOCK();
12489
12490
0
    if (centralMeridian < -93 || centralMeridian > -87)
12491
0
        return OGRERR_FAILURE;
12492
0
    if (latOfOrigin < 40 || latOfOrigin > 47)
12493
0
        return OGRERR_FAILURE;
12494
12495
    // If the CS name is known.
12496
0
    if (!prjName && !unitsName && crsName)
12497
0
    {
12498
0
        const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12499
0
        PJ_OBJ_LIST *list = proj_create_from_name(
12500
0
            d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12501
0
        if (list)
12502
0
        {
12503
0
            if (proj_list_get_count(list) == 1)
12504
0
            {
12505
0
                auto crs = proj_list_get(d->getPROJContext(), list, 0);
12506
0
                if (crs)
12507
0
                {
12508
0
                    Clear();
12509
0
                    d->setPjCRS(crs);
12510
0
                    proj_list_destroy(list);
12511
0
                    return OGRERR_NONE;
12512
0
                }
12513
0
            }
12514
0
            proj_list_destroy(list);
12515
0
        }
12516
0
        return OGRERR_FAILURE;
12517
0
    }
12518
12519
0
    if (prjName == nullptr || unitsName == nullptr)
12520
0
    {
12521
0
        return OGRERR_FAILURE;
12522
0
    }
12523
12524
0
    const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12525
0
    PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12526
0
                                              "NAD_1983_HARN_WISCRS_", &type, 1,
12527
0
                                              true, 0, nullptr);
12528
0
    if (list)
12529
0
    {
12530
0
        const auto listSize = proj_list_get_count(list);
12531
0
        for (int i = 0; i < listSize; i++)
12532
0
        {
12533
0
            auto crs = proj_list_get(d->getPROJContext(), list, i);
12534
0
            if (!crs)
12535
0
            {
12536
0
                continue;
12537
0
            }
12538
12539
0
            auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12540
0
            if (!conv)
12541
0
            {
12542
0
                proj_destroy(crs);
12543
0
                continue;
12544
0
            }
12545
0
            const char *pszMethodCode = nullptr;
12546
0
            proj_coordoperation_get_method_info(
12547
0
                d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12548
0
            const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12549
0
            if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12550
0
                   nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12551
0
                  (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12552
0
                   nMethodCode ==
12553
0
                       EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12554
0
            {
12555
0
                proj_destroy(crs);
12556
0
                proj_destroy(conv);
12557
0
                continue;
12558
0
            }
12559
12560
0
            auto coordSys =
12561
0
                proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12562
0
            if (!coordSys)
12563
0
            {
12564
0
                proj_destroy(crs);
12565
0
                proj_destroy(conv);
12566
0
                continue;
12567
0
            }
12568
12569
0
            double dfConvFactor = 0.0;
12570
0
            proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12571
0
                                  nullptr, nullptr, &dfConvFactor, nullptr,
12572
0
                                  nullptr, nullptr);
12573
0
            proj_destroy(coordSys);
12574
12575
0
            if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12576
0
                (!EQUAL(unitsName, "meters") &&
12577
0
                 std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12578
0
                     1e-10))
12579
0
            {
12580
0
                proj_destroy(crs);
12581
0
                proj_destroy(conv);
12582
0
                continue;
12583
0
            }
12584
12585
0
            int idx_lat = proj_coordoperation_get_param_index(
12586
0
                d->getPROJContext(), conv,
12587
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12588
0
            double valueLat = -1000;
12589
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12590
0
                                          nullptr, nullptr, nullptr, &valueLat,
12591
0
                                          nullptr, nullptr, nullptr, nullptr,
12592
0
                                          nullptr, nullptr);
12593
0
            int idx_lon = proj_coordoperation_get_param_index(
12594
0
                d->getPROJContext(), conv,
12595
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12596
0
            double valueLong = -1000;
12597
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12598
0
                                          nullptr, nullptr, nullptr, &valueLong,
12599
0
                                          nullptr, nullptr, nullptr, nullptr,
12600
0
                                          nullptr, nullptr);
12601
0
            if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12602
0
                std::fabs(latOfOrigin - valueLat) <= 1e-10)
12603
0
            {
12604
0
                Clear();
12605
0
                d->setPjCRS(crs);
12606
0
                proj_list_destroy(list);
12607
0
                proj_destroy(conv);
12608
0
                return OGRERR_NONE;
12609
0
            }
12610
12611
0
            proj_destroy(crs);
12612
0
            proj_destroy(conv);
12613
0
        }
12614
0
        proj_list_destroy(list);
12615
0
    }
12616
12617
0
    return OGRERR_FAILURE;
12618
0
}
12619
12620
/************************************************************************/
12621
/*                      GetAxisMappingStrategy()                        */
12622
/************************************************************************/
12623
12624
/** \brief Return the data axis to CRS axis mapping strategy.
12625
 *
12626
 * <ul>
12627
 * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12628
 *     lat/long order, the data will still be long/lat ordered. Similarly for
12629
 *     a projected CRS with northing/easting order, the data will still be
12630
 *     easting/northing ordered.
12631
 * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12632
 *     the CRS axis.
12633
 * <li>OAMS_CUSTOM means that the data axis are customly defined with
12634
 *     SetDataAxisToSRSAxisMapping()
12635
 * </ul>
12636
 * @return the data axis to CRS axis mapping strategy.
12637
 * @since GDAL 3.0
12638
 */
12639
OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12640
0
{
12641
0
    TAKE_OPTIONAL_LOCK();
12642
12643
0
    return d->m_axisMappingStrategy;
12644
0
}
12645
12646
/************************************************************************/
12647
/*                      OSRGetAxisMappingStrategy()                     */
12648
/************************************************************************/
12649
12650
/** \brief Return the data axis to CRS axis mapping strategy.
12651
 *
12652
 * See OGRSpatialReference::GetAxisMappingStrategy()
12653
 * @since GDAL 3.0
12654
 */
12655
OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12656
0
{
12657
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12658
12659
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12660
0
}
12661
12662
/************************************************************************/
12663
/*                      SetAxisMappingStrategy()                        */
12664
/************************************************************************/
12665
12666
/** \brief Set the data axis to CRS axis mapping strategy.
12667
 *
12668
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12669
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12670
 * later being the default value when the option is not set) to control the
12671
 * value of the data axis to CRS axis mapping strategy when a
12672
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12673
 * override this default value.
12674
 *
12675
 * See OGRSpatialReference::GetAxisMappingStrategy()
12676
 * @since GDAL 3.0
12677
 */
12678
void OGRSpatialReference::SetAxisMappingStrategy(
12679
    OSRAxisMappingStrategy strategy)
12680
0
{
12681
0
    TAKE_OPTIONAL_LOCK();
12682
12683
0
    d->m_axisMappingStrategy = strategy;
12684
0
    d->refreshAxisMapping();
12685
0
}
12686
12687
/************************************************************************/
12688
/*                      OSRSetAxisMappingStrategy()                     */
12689
/************************************************************************/
12690
12691
/** \brief Set the data axis to CRS axis mapping strategy.
12692
 *
12693
 * See OGRSpatialReference::SetAxisMappingStrategy()
12694
 * @since GDAL 3.0
12695
 */
12696
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12697
                               OSRAxisMappingStrategy strategy)
12698
0
{
12699
0
    VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12700
12701
0
    OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12702
0
}
12703
12704
/************************************************************************/
12705
/*                      GetDataAxisToSRSAxisMapping()                   */
12706
/************************************************************************/
12707
12708
/** \brief Return the data axis to SRS axis mapping.
12709
 *
12710
 * The number of elements of the vector will be the number of axis of the CRS.
12711
 * Values start at 1.
12712
 *
12713
 * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12714
 * for the first axis of the CRS.
12715
 *
12716
 * @since GDAL 3.0
12717
 */
12718
const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12719
0
{
12720
0
    TAKE_OPTIONAL_LOCK();
12721
12722
0
    return d->m_axisMapping;
12723
0
}
12724
12725
/************************************************************************/
12726
/*                     OSRGetDataAxisToSRSAxisMapping()                 */
12727
/************************************************************************/
12728
12729
/** \brief Return the data axis to SRS axis mapping.
12730
 *
12731
 * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12732
 *
12733
 * @since GDAL 3.0
12734
 */
12735
const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12736
                                          int *pnCount)
12737
0
{
12738
0
    VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12739
0
    VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12740
12741
0
    const auto &v =
12742
0
        OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12743
0
    *pnCount = static_cast<int>(v.size());
12744
0
    return v.data();
12745
0
}
12746
12747
/************************************************************************/
12748
/*                      SetDataAxisToSRSAxisMapping()                   */
12749
/************************************************************************/
12750
12751
/** \brief Set a custom data axis to CRS axis mapping.
12752
 *
12753
 * The number of elements of the mapping vector should be the number of axis
12754
 * of the CRS (as returned by GetAxesCount()) (although this method does not
12755
 * check that, beyond checking there are at least 2 elements, so that this
12756
 * method and setting the CRS can be done in any order).
12757
 * This is taken into account by OGRCoordinateTransformation to transform the
12758
 * order of coordinates to the order expected by the CRS before
12759
 * transformation, and back to the data order after transformation.
12760
 *
12761
 * The mapping[i] value (one based) represents the data axis number for the i(th)
12762
 * axis of the CRS. A negative value can also be used to ask for a sign
12763
 * reversal during coordinate transformation (to deal with northing vs southing,
12764
 * easting vs westing, heights vs depths).
12765
 *
12766
 * When used with OGRCoordinateTransformation,
12767
 * - the only valid values for mapping[0] (data axis number for the first axis
12768
 *   of the CRS) are 1, 2, -1, -2.
12769
 * - the only valid values for mapping[1] (data axis number for the second axis
12770
 *   of the CRS) are 1, 2, -1, -2.
12771
 *  - the only valid values mapping[2] are 3 or -3.
12772
 * Note: this method does not validate the values of mapping[].
12773
 *
12774
 * mapping=[2,1] typically expresses the inversion of axis between the data
12775
 * axis and the CRS axis for a 2D CRS.
12776
 *
12777
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12778
 *
12779
 * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12780
 *
12781
 * @param mapping The new data axis to CRS axis mapping.
12782
 *
12783
 * @since GDAL 3.0
12784
 * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12785
 */
12786
OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12787
    const std::vector<int> &mapping)
12788
0
{
12789
0
    TAKE_OPTIONAL_LOCK();
12790
12791
0
    if (mapping.size() < 2)
12792
0
        return OGRERR_FAILURE;
12793
0
    d->m_axisMappingStrategy = OAMS_CUSTOM;
12794
0
    d->m_axisMapping = mapping;
12795
0
    return OGRERR_NONE;
12796
0
}
12797
12798
/************************************************************************/
12799
/*                     OSRSetDataAxisToSRSAxisMapping()                 */
12800
/************************************************************************/
12801
12802
/** \brief Set a custom data axis to CRS axis mapping.
12803
 *
12804
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12805
 *
12806
 * This is the same as the C++ method
12807
 * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12808
 *
12809
 * @since GDAL 3.1
12810
 */
12811
OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12812
                                      int nMappingSize, const int *panMapping)
12813
0
{
12814
0
    VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12815
0
    VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12816
0
                      OGRERR_FAILURE);
12817
12818
0
    if (nMappingSize < 0)
12819
0
        return OGRERR_FAILURE;
12820
12821
0
    std::vector<int> mapping(nMappingSize);
12822
0
    if (nMappingSize)
12823
0
        memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12824
0
    return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12825
0
        mapping);
12826
0
}
12827
12828
/************************************************************************/
12829
/*                               GetAreaOfUse()                         */
12830
/************************************************************************/
12831
12832
/** \brief Return the area of use of the CRS.
12833
 *
12834
 * This method is the same as the OSRGetAreaOfUse() function.
12835
 *
12836
 * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12837
 * longitude, expressed in degree. Might be NULL. If the returned value is
12838
 * -1000, the bounding box is unknown.
12839
 * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12840
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12841
 * the bounding box is unknown.
12842
 * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12843
 * longitude, expressed in degree. Might be NULL. If the returned value is
12844
 * -1000, the bounding box is unknown.
12845
 * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12846
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12847
 * the bounding box is unknown.
12848
 * @param ppszAreaName Pointer to a string to receive the name of the area of
12849
 * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12850
 * invalidated by further calls.
12851
 * @return true in case of success
12852
 * @since GDAL 3.0
12853
 */
12854
bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12855
                                       double *pdfSouthLatitudeDeg,
12856
                                       double *pdfEastLongitudeDeg,
12857
                                       double *pdfNorthLatitudeDeg,
12858
                                       const char **ppszAreaName) const
12859
0
{
12860
0
    TAKE_OPTIONAL_LOCK();
12861
12862
0
    d->refreshProjObj();
12863
0
    if (!d->m_pj_crs)
12864
0
    {
12865
0
        return false;
12866
0
    }
12867
0
    d->demoteFromBoundCRS();
12868
0
    const char *pszAreaName = nullptr;
12869
0
    int bSuccess = proj_get_area_of_use(
12870
0
        d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12871
0
        pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12872
0
        &pszAreaName);
12873
0
    d->undoDemoteFromBoundCRS();
12874
0
    d->m_osAreaName = pszAreaName ? pszAreaName : "";
12875
0
    if (ppszAreaName)
12876
0
        *ppszAreaName = d->m_osAreaName.c_str();
12877
0
    return CPL_TO_BOOL(bSuccess);
12878
0
}
12879
12880
/************************************************************************/
12881
/*                               GetAreaOfUse()                         */
12882
/************************************************************************/
12883
12884
/** \brief Return the area of use of the CRS.
12885
 *
12886
 * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12887
 *
12888
 * @since GDAL 3.0
12889
 */
12890
int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12891
                    double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12892
                    double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12893
0
{
12894
0
    VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12895
12896
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12897
0
        pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12898
0
        pdfNorthLatitudeDeg, ppszAreaName);
12899
0
}
12900
12901
/************************************************************************/
12902
/*                     OSRGetCRSInfoListFromDatabase()                  */
12903
/************************************************************************/
12904
12905
/** \brief Enumerate CRS objects from the database.
12906
 *
12907
 * The returned object is an array of OSRCRSInfo* pointers, whose last
12908
 * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12909
 *
12910
 * @param pszAuthName Authority name, used to restrict the search.
12911
 * Or NULL for all authorities.
12912
 * @param params Additional criteria. Must be set to NULL for now.
12913
 * @param pnOutResultCount Output parameter pointing to an integer to receive
12914
 * the size of the result list. Might be NULL
12915
 * @return an array of OSRCRSInfo* pointers to be freed with
12916
 * OSRDestroyCRSInfoList(), or NULL in case of error.
12917
 *
12918
 * @since GDAL 3.0
12919
 */
12920
OSRCRSInfo **
12921
OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12922
                              CPL_UNUSED const OSRCRSListParameters *params,
12923
                              int *pnOutResultCount)
12924
0
{
12925
0
    int nResultCount = 0;
12926
0
    auto projList = proj_get_crs_info_list_from_database(
12927
0
        OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12928
0
    if (pnOutResultCount)
12929
0
        *pnOutResultCount = nResultCount;
12930
0
    if (!projList)
12931
0
    {
12932
0
        return nullptr;
12933
0
    }
12934
0
    auto res = new OSRCRSInfo *[nResultCount + 1];
12935
0
    for (int i = 0; i < nResultCount; i++)
12936
0
    {
12937
0
        res[i] = new OSRCRSInfo;
12938
0
        res[i]->pszAuthName = projList[i]->auth_name
12939
0
                                  ? CPLStrdup(projList[i]->auth_name)
12940
0
                                  : nullptr;
12941
0
        res[i]->pszCode =
12942
0
            projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12943
0
        res[i]->pszName =
12944
0
            projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12945
0
        res[i]->eType = OSR_CRS_TYPE_OTHER;
12946
0
        switch (projList[i]->type)
12947
0
        {
12948
0
            case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12949
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12950
0
                break;
12951
0
            case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12952
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12953
0
                break;
12954
0
            case PJ_TYPE_GEOCENTRIC_CRS:
12955
0
                res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12956
0
                break;
12957
0
            case PJ_TYPE_PROJECTED_CRS:
12958
0
                res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12959
0
                break;
12960
0
            case PJ_TYPE_VERTICAL_CRS:
12961
0
                res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12962
0
                break;
12963
0
            case PJ_TYPE_COMPOUND_CRS:
12964
0
                res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12965
0
                break;
12966
0
            default:
12967
0
                break;
12968
0
        }
12969
0
        res[i]->bDeprecated = projList[i]->deprecated;
12970
0
        res[i]->bBboxValid = projList[i]->bbox_valid;
12971
0
        res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12972
0
        res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12973
0
        res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12974
0
        res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12975
0
        res[i]->pszAreaName = projList[i]->area_name
12976
0
                                  ? CPLStrdup(projList[i]->area_name)
12977
0
                                  : nullptr;
12978
0
        res[i]->pszProjectionMethod =
12979
0
            projList[i]->projection_method_name
12980
0
                ? CPLStrdup(projList[i]->projection_method_name)
12981
0
                : nullptr;
12982
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
12983
0
        res[i]->pszCelestialBodyName =
12984
0
            projList[i]->celestial_body_name
12985
0
                ? CPLStrdup(projList[i]->celestial_body_name)
12986
0
                : nullptr;
12987
#else
12988
        res[i]->pszCelestialBodyName =
12989
            res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
12990
                ? CPLStrdup("Earth")
12991
                : nullptr;
12992
#endif
12993
0
    }
12994
0
    res[nResultCount] = nullptr;
12995
0
    proj_crs_info_list_destroy(projList);
12996
0
    return res;
12997
0
}
12998
12999
/************************************************************************/
13000
/*                        OSRDestroyCRSInfoList()                       */
13001
/************************************************************************/
13002
13003
/** \brief Destroy the result returned by
13004
 * OSRGetCRSInfoListFromDatabase().
13005
 *
13006
 * @since GDAL 3.0
13007
 */
13008
void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13009
0
{
13010
0
    if (list)
13011
0
    {
13012
0
        for (int i = 0; list[i] != nullptr; i++)
13013
0
        {
13014
0
            CPLFree(list[i]->pszAuthName);
13015
0
            CPLFree(list[i]->pszCode);
13016
0
            CPLFree(list[i]->pszName);
13017
0
            CPLFree(list[i]->pszAreaName);
13018
0
            CPLFree(list[i]->pszProjectionMethod);
13019
0
            CPLFree(list[i]->pszCelestialBodyName);
13020
0
            delete list[i];
13021
0
        }
13022
0
        delete[] list;
13023
0
    }
13024
0
}
13025
13026
/************************************************************************/
13027
/*                   OSRGetAuthorityListFromDatabase()                  */
13028
/************************************************************************/
13029
13030
/** \brief Return the list of CRS authorities used in the PROJ database.
13031
 *
13032
 * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13033
 *
13034
 * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13035
 *
13036
 * @return nullptr in case of error, or a NULL terminated list of strings to
13037
 * free with CSLDestroy()
13038
 * @since GDAL 3.10
13039
 */
13040
char **OSRGetAuthorityListFromDatabase()
13041
0
{
13042
0
    PROJ_STRING_LIST list =
13043
0
        proj_get_authorities_from_database(OSRGetProjTLSContext());
13044
0
    if (!list)
13045
0
    {
13046
0
        return nullptr;
13047
0
    }
13048
0
    int count = 0;
13049
0
    while (list[count])
13050
0
        ++count;
13051
0
    char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13052
0
    for (int i = 0; i < count; ++i)
13053
0
        res[i] = CPLStrdup(list[i]);
13054
0
    proj_string_list_destroy(list);
13055
0
    return res;
13056
0
}
13057
13058
/************************************************************************/
13059
/*                    UpdateCoordinateSystemFromGeogCRS()               */
13060
/************************************************************************/
13061
13062
/*! @cond Doxygen_Suppress */
13063
/** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13064
 *
13065
 * @since GDAL 3.1
13066
 */
13067
void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13068
0
{
13069
0
    TAKE_OPTIONAL_LOCK();
13070
13071
0
    d->refreshProjObj();
13072
0
    if (!d->m_pj_crs)
13073
0
        return;
13074
0
    if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13075
0
        return;
13076
0
    if (GetAxesCount() == 3)
13077
0
        return;
13078
0
    auto ctxt = d->getPROJContext();
13079
0
    auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13080
0
    if (!baseCRS)
13081
0
        return;
13082
0
    auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13083
0
    if (!baseCRSCS)
13084
0
    {
13085
0
        proj_destroy(baseCRS);
13086
0
        return;
13087
0
    }
13088
0
    if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13089
0
    {
13090
0
        proj_destroy(baseCRSCS);
13091
0
        proj_destroy(baseCRS);
13092
0
        return;
13093
0
    }
13094
0
    auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13095
0
    if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13096
0
    {
13097
0
        proj_destroy(baseCRSCS);
13098
0
        proj_destroy(baseCRS);
13099
0
        proj_destroy(projCS);
13100
0
        return;
13101
0
    }
13102
13103
0
    PJ_AXIS_DESCRIPTION axis[3];
13104
0
    for (int i = 0; i < 3; i++)
13105
0
    {
13106
0
        const char *name = nullptr;
13107
0
        const char *abbreviation = nullptr;
13108
0
        const char *direction = nullptr;
13109
0
        double unit_conv_factor = 0;
13110
0
        const char *unit_name = nullptr;
13111
0
        proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13112
0
                              &abbreviation, &direction, &unit_conv_factor,
13113
0
                              &unit_name, nullptr, nullptr);
13114
0
        axis[i].name = CPLStrdup(name);
13115
0
        axis[i].abbreviation = CPLStrdup(abbreviation);
13116
0
        axis[i].direction = CPLStrdup(direction);
13117
0
        axis[i].unit_name = CPLStrdup(unit_name);
13118
0
        axis[i].unit_conv_factor = unit_conv_factor;
13119
0
        axis[i].unit_type = PJ_UT_LINEAR;
13120
0
    }
13121
0
    proj_destroy(baseCRSCS);
13122
0
    proj_destroy(projCS);
13123
0
    auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13124
0
    for (int i = 0; i < 3; i++)
13125
0
    {
13126
0
        CPLFree(axis[i].name);
13127
0
        CPLFree(axis[i].abbreviation);
13128
0
        CPLFree(axis[i].direction);
13129
0
        CPLFree(axis[i].unit_name);
13130
0
    }
13131
0
    if (!cs)
13132
0
    {
13133
0
        proj_destroy(baseCRS);
13134
0
        return;
13135
0
    }
13136
0
    auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13137
0
    auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13138
0
                                         conversion, cs);
13139
0
    proj_destroy(baseCRS);
13140
0
    proj_destroy(conversion);
13141
0
    proj_destroy(cs);
13142
0
    d->setPjCRS(crs);
13143
0
}
13144
13145
/*! @endcond */
13146
13147
/************************************************************************/
13148
/*                             PromoteTo3D()                            */
13149
/************************************************************************/
13150
13151
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13152
 *
13153
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
13154
 * units.
13155
 *
13156
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13157
 * be used.
13158
 * @return OGRERR_NONE if no error occurred.
13159
 * @since GDAL 3.1 and PROJ 6.3
13160
 */
13161
OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13162
0
{
13163
0
    TAKE_OPTIONAL_LOCK();
13164
13165
0
    d->refreshProjObj();
13166
0
    if (!d->m_pj_crs)
13167
0
        return OGRERR_FAILURE;
13168
0
    auto newPj =
13169
0
        proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13170
0
    if (!newPj)
13171
0
        return OGRERR_FAILURE;
13172
0
    d->setPjCRS(newPj);
13173
0
    return OGRERR_NONE;
13174
0
}
13175
13176
/************************************************************************/
13177
/*                             OSRPromoteTo3D()                         */
13178
/************************************************************************/
13179
13180
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13181
 *
13182
 * See OGRSpatialReference::PromoteTo3D()
13183
 *
13184
 * @since GDAL 3.1 and PROJ 6.3
13185
 */
13186
OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13187
0
{
13188
0
    VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13189
13190
0
    return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13191
0
}
13192
13193
/************************************************************************/
13194
/*                             DemoteTo2D()                             */
13195
/************************************************************************/
13196
13197
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13198
 *
13199
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13200
 * be used.
13201
 * @return OGRERR_NONE if no error occurred.
13202
 * @since GDAL 3.2 and PROJ 6.3
13203
 */
13204
OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13205
0
{
13206
0
    TAKE_OPTIONAL_LOCK();
13207
13208
0
    d->refreshProjObj();
13209
0
    if (!d->m_pj_crs)
13210
0
        return OGRERR_FAILURE;
13211
0
    auto newPj =
13212
0
        proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13213
0
    if (!newPj)
13214
0
        return OGRERR_FAILURE;
13215
0
    d->setPjCRS(newPj);
13216
0
    return OGRERR_NONE;
13217
0
}
13218
13219
/************************************************************************/
13220
/*                             OSRDemoteTo2D()                          */
13221
/************************************************************************/
13222
13223
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13224
 *
13225
 * See OGRSpatialReference::DemoteTo2D()
13226
 *
13227
 * @since GDAL 3.2 and PROJ 6.3
13228
 */
13229
OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13230
0
{
13231
0
    VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13232
13233
0
    return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13234
0
}
13235
13236
/************************************************************************/
13237
/*                           GetEPSGGeogCS()                            */
13238
/************************************************************************/
13239
13240
/** Try to establish what the EPSG code for this coordinate systems
13241
 * GEOGCS might be.  Returns -1 if no reasonable guess can be made.
13242
 *
13243
 * @return EPSG code
13244
 */
13245
13246
int OGRSpatialReference::GetEPSGGeogCS() const
13247
13248
0
{
13249
0
    TAKE_OPTIONAL_LOCK();
13250
13251
    /* -------------------------------------------------------------------- */
13252
    /*      Check axis order.                                               */
13253
    /* -------------------------------------------------------------------- */
13254
0
    auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13255
0
    if (!poGeogCRS)
13256
0
        return -1;
13257
13258
0
    bool ret = false;
13259
0
    poGeogCRS->d->demoteFromBoundCRS();
13260
0
    auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13261
0
                                             poGeogCRS->d->m_pj_crs);
13262
0
    poGeogCRS->d->undoDemoteFromBoundCRS();
13263
0
    if (cs)
13264
0
    {
13265
0
        const char *pszDirection = nullptr;
13266
0
        if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13267
0
                                  &pszDirection, nullptr, nullptr, nullptr,
13268
0
                                  nullptr))
13269
0
        {
13270
0
            if (EQUAL(pszDirection, "north"))
13271
0
            {
13272
0
                ret = true;
13273
0
            }
13274
0
        }
13275
13276
0
        proj_destroy(cs);
13277
0
    }
13278
0
    if (!ret)
13279
0
        return -1;
13280
13281
    /* -------------------------------------------------------------------- */
13282
    /*      Do we already have it?                                          */
13283
    /* -------------------------------------------------------------------- */
13284
0
    const char *pszAuthName = GetAuthorityName("GEOGCS");
13285
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13286
0
        return atoi(GetAuthorityCode("GEOGCS"));
13287
13288
    /* -------------------------------------------------------------------- */
13289
    /*      Get the datum and geogcs names.                                 */
13290
    /* -------------------------------------------------------------------- */
13291
13292
0
    const char *pszGEOGCS = GetAttrValue("GEOGCS");
13293
0
    const char *pszDatum = GetAttrValue("DATUM");
13294
13295
    // We can only operate on coordinate systems with a geogcs.
13296
0
    OGRSpatialReference oSRSTmp;
13297
0
    if (pszGEOGCS == nullptr || pszDatum == nullptr)
13298
0
    {
13299
        // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13300
        // export to WKT1, so try to extract the geographic CRS through PROJ
13301
        // API with CopyGeogCSFrom() and get the nodes' values from it.
13302
0
        oSRSTmp.CopyGeogCSFrom(this);
13303
0
        pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13304
0
        pszDatum = oSRSTmp.GetAttrValue("DATUM");
13305
0
        if (pszGEOGCS == nullptr || pszDatum == nullptr)
13306
0
        {
13307
0
            return -1;
13308
0
        }
13309
0
    }
13310
13311
    // Lookup geographic CRS name
13312
0
    const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13313
0
    PJ_OBJ_LIST *list = proj_create_from_name(
13314
0
        d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13315
0
    if (list)
13316
0
    {
13317
0
        const auto listSize = proj_list_get_count(list);
13318
0
        if (listSize == 1)
13319
0
        {
13320
0
            auto crs = proj_list_get(d->getPROJContext(), list, 0);
13321
0
            if (crs)
13322
0
            {
13323
0
                pszAuthName = proj_get_id_auth_name(crs, 0);
13324
0
                const char *pszCode = proj_get_id_code(crs, 0);
13325
0
                if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13326
0
                {
13327
0
                    const int nCode = atoi(pszCode);
13328
0
                    proj_destroy(crs);
13329
0
                    proj_list_destroy(list);
13330
0
                    return nCode;
13331
0
                }
13332
0
                proj_destroy(crs);
13333
0
            }
13334
0
        }
13335
0
        proj_list_destroy(list);
13336
0
    }
13337
13338
    /* -------------------------------------------------------------------- */
13339
    /*      Is this a "well known" geographic coordinate system?            */
13340
    /* -------------------------------------------------------------------- */
13341
0
    const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13342
0
                      strstr(pszDatum, "WGS") ||
13343
0
                      strstr(pszGEOGCS, "World Geodetic System") ||
13344
0
                      strstr(pszGEOGCS, "World_Geodetic_System") ||
13345
0
                      strstr(pszDatum, "World Geodetic System") ||
13346
0
                      strstr(pszDatum, "World_Geodetic_System");
13347
13348
0
    const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13349
0
                      strstr(pszDatum, "NAD") ||
13350
0
                      strstr(pszGEOGCS, "North American") ||
13351
0
                      strstr(pszGEOGCS, "North_American") ||
13352
0
                      strstr(pszDatum, "North American") ||
13353
0
                      strstr(pszDatum, "North_American");
13354
13355
0
    if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13356
0
        return 4326;
13357
13358
0
    if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13359
0
        return 4322;
13360
13361
    // This is questionable as there are several 'flavors' of NAD83 that
13362
    // are not the same as 4269
13363
0
    if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13364
0
        return 4269;
13365
13366
0
    if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13367
0
        return 4267;
13368
13369
    /* -------------------------------------------------------------------- */
13370
    /*      If we know the datum, associate the most likely GCS with        */
13371
    /*      it.                                                             */
13372
    /* -------------------------------------------------------------------- */
13373
0
    const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13374
0
    pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13375
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13376
0
        GetPrimeMeridian() == 0.0)
13377
0
    {
13378
0
        const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13379
13380
0
        if (nDatum >= 6000 && nDatum <= 6999)
13381
0
            return nDatum - 2000;
13382
0
    }
13383
13384
0
    return -1;
13385
0
}
13386
13387
/************************************************************************/
13388
/*                          SetCoordinateEpoch()                        */
13389
/************************************************************************/
13390
13391
/** Set the coordinate epoch, as decimal year.
13392
 *
13393
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13394
 * change with time. To be unambiguous the coordinates must always be qualified
13395
 * with the epoch at which they are valid. The coordinate epoch is not
13396
 * necessarily the epoch at which the observation was collected.
13397
 *
13398
 * Pedantically the coordinate epoch of an observation belongs to the
13399
 * observation, and not to the CRS, however it is often more practical to
13400
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13401
 * CRS (see IsDynamic())
13402
 *
13403
 * This method is the same as the OSRSetCoordinateEpoch() function.
13404
 *
13405
 * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13406
 * @since OGR 3.4
13407
 */
13408
13409
void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13410
0
{
13411
0
    d->m_coordinateEpoch = dfCoordinateEpoch;
13412
0
}
13413
13414
/************************************************************************/
13415
/*                      OSRSetCoordinateEpoch()                         */
13416
/************************************************************************/
13417
13418
/** \brief Set the coordinate epoch, as decimal year.
13419
 *
13420
 * See OGRSpatialReference::SetCoordinateEpoch()
13421
 *
13422
 * @since OGR 3.4
13423
 */
13424
void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13425
0
{
13426
0
    VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13427
13428
0
    return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13429
0
        dfCoordinateEpoch);
13430
0
}
13431
13432
/************************************************************************/
13433
/*                          GetCoordinateEpoch()                        */
13434
/************************************************************************/
13435
13436
/** Return the coordinate epoch, as decimal year.
13437
 *
13438
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13439
 * change with time. To be unambiguous the coordinates must always be qualified
13440
 * with the epoch at which they are valid. The coordinate epoch is not
13441
 * necessarily the epoch at which the observation was collected.
13442
 *
13443
 * Pedantically the coordinate epoch of an observation belongs to the
13444
 * observation, and not to the CRS, however it is often more practical to
13445
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13446
 * CRS (see IsDynamic())
13447
 *
13448
 * This method is the same as the OSRGetCoordinateEpoch() function.
13449
 *
13450
 * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13451
 *                         if not set, or relevant.
13452
 * @since OGR 3.4
13453
 */
13454
13455
double OGRSpatialReference::GetCoordinateEpoch() const
13456
0
{
13457
0
    return d->m_coordinateEpoch;
13458
0
}
13459
13460
/************************************************************************/
13461
/*                      OSRGetCoordinateEpoch()                        */
13462
/************************************************************************/
13463
13464
/** \brief Get the coordinate epoch, as decimal year.
13465
 *
13466
 * See OGRSpatialReference::GetCoordinateEpoch()
13467
 *
13468
 * @since OGR 3.4
13469
 */
13470
double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13471
0
{
13472
0
    VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13473
13474
0
    return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13475
0
}