Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrspatialreference.cpp
Line
Count
Source
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 final : 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
 */
937
938
void OGRSpatialReference::DestroySpatialReference(OGRSpatialReference *poSRS)
939
0
{
940
0
    delete poSRS;
941
0
}
942
943
/************************************************************************/
944
/*                     OSRDestroySpatialReference()                     */
945
/************************************************************************/
946
947
/**
948
 * \brief OGRSpatialReference destructor.
949
 *
950
 * This function is the same as OGRSpatialReference::~OGRSpatialReference()
951
 * and OGRSpatialReference::DestroySpatialReference()
952
 *
953
 * @param hSRS the object to delete
954
 */
955
void CPL_STDCALL OSRDestroySpatialReference(OGRSpatialReferenceH hSRS)
956
957
0
{
958
0
    delete ToPointer(hSRS);
959
0
}
960
961
/************************************************************************/
962
/*                               Clear()                                */
963
/************************************************************************/
964
965
/**
966
 * \brief Wipe current definition.
967
 *
968
 * Returns OGRSpatialReference to a state with no definition, as it
969
 * exists when first created.  It does not affect reference counts.
970
 */
971
972
void OGRSpatialReference::Clear()
973
974
0
{
975
0
    d->clear();
976
0
}
977
978
/************************************************************************/
979
/*                             operator=()                              */
980
/************************************************************************/
981
982
/** Assignment operator.
983
 * @param oSource SRS to assign to *this
984
 * @return *this
985
 */
986
OGRSpatialReference &
987
OGRSpatialReference::operator=(const OGRSpatialReference &oSource)
988
989
0
{
990
0
    if (&oSource != this)
991
0
    {
992
0
        Clear();
993
#ifdef CPPCHECK
994
        // Otherwise cppcheck would protest that nRefCount isn't modified
995
        d->nRefCount = (d->nRefCount + 1) - 1;
996
#endif
997
998
0
        oSource.d->refreshProjObj();
999
0
        if (oSource.d->m_pj_crs)
1000
0
            d->setPjCRS(proj_clone(d->getPROJContext(), oSource.d->m_pj_crs));
1001
0
        if (oSource.d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
1002
0
            SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1003
0
        else if (oSource.d->m_axisMappingStrategy == OAMS_CUSTOM)
1004
0
            SetDataAxisToSRSAxisMapping(oSource.d->m_axisMapping);
1005
1006
0
        d->m_coordinateEpoch = oSource.d->m_coordinateEpoch;
1007
0
    }
1008
1009
0
    return *this;
1010
0
}
1011
1012
/************************************************************************/
1013
/*                             operator=()                              */
1014
/************************************************************************/
1015
1016
/** Move assignment operator.
1017
 * @param oSource SRS to assign to *this
1018
 * @return *this
1019
 */
1020
OGRSpatialReference &
1021
OGRSpatialReference::operator=(OGRSpatialReference &&oSource)
1022
1023
0
{
1024
0
    if (&oSource != this)
1025
0
    {
1026
0
        d = std::move(oSource.d);
1027
0
    }
1028
1029
0
    return *this;
1030
0
}
1031
1032
/************************************************************************/
1033
/*                      AssignAndSetThreadSafe()                        */
1034
/************************************************************************/
1035
1036
/** Assignment method, with thread-safety.
1037
 *
1038
 * Same as an assignment operator, but asking also that the *this instance
1039
 * becomes thread-safe.
1040
 *
1041
 * @param oSource SRS to assign to *this
1042
 * @return *this
1043
 * @since 3.10
1044
 */
1045
1046
OGRSpatialReference &
1047
OGRSpatialReference::AssignAndSetThreadSafe(const OGRSpatialReference &oSource)
1048
0
{
1049
0
    *this = oSource;
1050
0
    d->SetThreadSafe();
1051
0
    return *this;
1052
0
}
1053
1054
/************************************************************************/
1055
/*                             Reference()                              */
1056
/************************************************************************/
1057
1058
/**
1059
 * \brief Increments the reference count by one.
1060
 *
1061
 * The reference count is used keep track of the number of OGRGeometry objects
1062
 * referencing this SRS.
1063
 *
1064
 * The method does the same thing as the C function OSRReference().
1065
 *
1066
 * @return the updated reference count.
1067
 */
1068
1069
int OGRSpatialReference::Reference()
1070
1071
0
{
1072
0
    return CPLAtomicInc(&d->nRefCount);
1073
0
}
1074
1075
/************************************************************************/
1076
/*                            OSRReference()                            */
1077
/************************************************************************/
1078
1079
/**
1080
 * \brief Increments the reference count by one.
1081
 *
1082
 * This function is the same as OGRSpatialReference::Reference()
1083
 */
1084
int OSRReference(OGRSpatialReferenceH hSRS)
1085
1086
0
{
1087
0
    VALIDATE_POINTER1(hSRS, "OSRReference", 0);
1088
1089
0
    return ToPointer(hSRS)->Reference();
1090
0
}
1091
1092
/************************************************************************/
1093
/*                            Dereference()                             */
1094
/************************************************************************/
1095
1096
/**
1097
 * \brief Decrements the reference count by one.
1098
 *
1099
 * The method does the same thing as the C function OSRDereference().
1100
 *
1101
 * @return the updated reference count.
1102
 */
1103
1104
int OGRSpatialReference::Dereference()
1105
1106
0
{
1107
0
    if (d->nRefCount <= 0)
1108
0
        CPLDebug("OSR",
1109
0
                 "Dereference() called on an object with refcount %d,"
1110
0
                 "likely already destroyed!",
1111
0
                 d->nRefCount);
1112
0
    return CPLAtomicDec(&d->nRefCount);
1113
0
}
1114
1115
/************************************************************************/
1116
/*                           OSRDereference()                           */
1117
/************************************************************************/
1118
1119
/**
1120
 * \brief Decrements the reference count by one.
1121
 *
1122
 * This function is the same as OGRSpatialReference::Dereference()
1123
 */
1124
int OSRDereference(OGRSpatialReferenceH hSRS)
1125
1126
0
{
1127
0
    VALIDATE_POINTER1(hSRS, "OSRDereference", 0);
1128
1129
0
    return ToPointer(hSRS)->Dereference();
1130
0
}
1131
1132
/************************************************************************/
1133
/*                         GetReferenceCount()                          */
1134
/************************************************************************/
1135
1136
/**
1137
 * \brief Fetch current reference count.
1138
 *
1139
 * @return the current reference count.
1140
 */
1141
int OGRSpatialReference::GetReferenceCount() const
1142
0
{
1143
0
    return d->nRefCount;
1144
0
}
1145
1146
/************************************************************************/
1147
/*                              Release()                               */
1148
/************************************************************************/
1149
1150
/**
1151
 * \brief Decrements the reference count by one, and destroy if zero.
1152
 *
1153
 * The method does the same thing as the C function OSRRelease().
1154
 */
1155
1156
void OGRSpatialReference::Release()
1157
1158
0
{
1159
0
    if (Dereference() <= 0)
1160
0
        delete this;
1161
0
}
1162
1163
/************************************************************************/
1164
/*                             OSRRelease()                             */
1165
/************************************************************************/
1166
1167
/**
1168
 * \brief Decrements the reference count by one, and destroy if zero.
1169
 *
1170
 * This function is the same as OGRSpatialReference::Release()
1171
 */
1172
void OSRRelease(OGRSpatialReferenceH hSRS)
1173
1174
0
{
1175
0
    VALIDATE_POINTER0(hSRS, "OSRRelease");
1176
1177
0
    ToPointer(hSRS)->Release();
1178
0
}
1179
1180
OGR_SRSNode *OGRSpatialReference::GetRoot()
1181
0
{
1182
0
    TAKE_OPTIONAL_LOCK();
1183
1184
0
    if (!d->m_poRoot)
1185
0
    {
1186
0
        d->refreshRootFromProjObj();
1187
0
    }
1188
0
    return d->m_poRoot;
1189
0
}
1190
1191
const OGR_SRSNode *OGRSpatialReference::GetRoot() const
1192
0
{
1193
0
    TAKE_OPTIONAL_LOCK();
1194
1195
0
    if (!d->m_poRoot)
1196
0
    {
1197
0
        d->refreshRootFromProjObj();
1198
0
    }
1199
0
    return d->m_poRoot;
1200
0
}
1201
1202
/************************************************************************/
1203
/*                              SetRoot()                               */
1204
/************************************************************************/
1205
1206
/**
1207
 * \brief Set the root SRS node.
1208
 *
1209
 * If the object has an existing tree of OGR_SRSNodes, they are destroyed
1210
 * as part of assigning the new root.  Ownership of the passed OGR_SRSNode is
1211
 * is assumed by the OGRSpatialReference.
1212
 *
1213
 * @param poNewRoot object to assign as root.
1214
 */
1215
1216
void OGRSpatialReference::SetRoot(OGR_SRSNode *poNewRoot)
1217
1218
0
{
1219
0
    if (d->m_poRoot != poNewRoot)
1220
0
    {
1221
0
        delete d->m_poRoot;
1222
0
        d->setRoot(poNewRoot);
1223
0
    }
1224
0
}
1225
1226
/************************************************************************/
1227
/*                            GetAttrNode()                             */
1228
/************************************************************************/
1229
1230
/**
1231
 * \brief Find named node in tree.
1232
 *
1233
 * This method does a pre-order traversal of the node tree searching for
1234
 * a node with this exact value (case insensitive), and returns it.  Leaf
1235
 * nodes are not considered, under the assumption that they are just
1236
 * attribute value nodes.
1237
 *
1238
 * If a node appears more than once in the tree (such as UNIT for instance),
1239
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1240
 * more specific.
1241
 *
1242
 * @param pszNodePath the name of the node to search for.  May contain multiple
1243
 * components such as "GEOGCS|UNIT".
1244
 *
1245
 * @return a pointer to the node found, or NULL if none.
1246
 */
1247
1248
OGR_SRSNode *OGRSpatialReference::GetAttrNode(const char *pszNodePath)
1249
1250
0
{
1251
0
    if (strchr(pszNodePath, '|') == nullptr)
1252
0
    {
1253
        // Fast path
1254
0
        OGR_SRSNode *poNode = GetRoot();
1255
0
        if (poNode)
1256
0
            poNode = poNode->GetNode(pszNodePath);
1257
0
        return poNode;
1258
0
    }
1259
1260
0
    char **papszPathTokens =
1261
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
1262
1263
0
    if (CSLCount(papszPathTokens) < 1)
1264
0
    {
1265
0
        CSLDestroy(papszPathTokens);
1266
0
        return nullptr;
1267
0
    }
1268
1269
0
    OGR_SRSNode *poNode = GetRoot();
1270
0
    for (int i = 0; poNode != nullptr && papszPathTokens[i] != nullptr; i++)
1271
0
    {
1272
0
        poNode = poNode->GetNode(papszPathTokens[i]);
1273
0
    }
1274
1275
0
    CSLDestroy(papszPathTokens);
1276
1277
0
    return poNode;
1278
0
}
1279
1280
/**
1281
 * \brief Find named node in tree.
1282
 *
1283
 * This method does a pre-order traversal of the node tree searching for
1284
 * a node with this exact value (case insensitive), and returns it.  Leaf
1285
 * nodes are not considered, under the assumption that they are just
1286
 * attribute value nodes.
1287
 *
1288
 * If a node appears more than once in the tree (such as UNIT for instance),
1289
 * the first encountered will be returned.  Use GetNode() on a subtree to be
1290
 * more specific.
1291
 *
1292
 * @param pszNodePath the name of the node to search for.  May contain multiple
1293
 * components such as "GEOGCS|UNIT".
1294
 *
1295
 * @return a pointer to the node found, or NULL if none.
1296
 */
1297
1298
const OGR_SRSNode *
1299
OGRSpatialReference::GetAttrNode(const char *pszNodePath) const
1300
1301
0
{
1302
0
    OGR_SRSNode *poNode =
1303
0
        const_cast<OGRSpatialReference *>(this)->GetAttrNode(pszNodePath);
1304
1305
0
    return poNode;
1306
0
}
1307
1308
/************************************************************************/
1309
/*                            GetAttrValue()                            */
1310
/************************************************************************/
1311
1312
/**
1313
 * \brief Fetch indicated attribute of named node.
1314
 *
1315
 * This method uses GetAttrNode() to find the named node, and then extracts
1316
 * the value of the indicated child.  Thus a call to GetAttrValue("UNIT",1)
1317
 * would return the second child of the UNIT node, which is normally the
1318
 * length of the linear unit in meters.
1319
 *
1320
 * This method does the same thing as the C function OSRGetAttrValue().
1321
 *
1322
 * @param pszNodeName the tree node to look for (case insensitive).
1323
 * @param iAttr the child of the node to fetch (zero based).
1324
 *
1325
 * @return the requested value, or NULL if it fails for any reason.
1326
 */
1327
1328
const char *OGRSpatialReference::GetAttrValue(const char *pszNodeName,
1329
                                              int iAttr) const
1330
1331
0
{
1332
0
    const OGR_SRSNode *poNode = GetAttrNode(pszNodeName);
1333
0
    if (poNode == nullptr)
1334
0
    {
1335
0
        if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJECTION"))
1336
0
        {
1337
0
            return GetAttrValue("METHOD", iAttr);
1338
0
        }
1339
0
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS|PROJECTION"))
1340
0
        {
1341
0
            return GetAttrValue("PROJCRS|METHOD", iAttr);
1342
0
        }
1343
0
        else if (d->m_bNodesWKT2 && EQUAL(pszNodeName, "PROJCS"))
1344
0
        {
1345
0
            return GetAttrValue("PROJCRS", iAttr);
1346
0
        }
1347
0
        return nullptr;
1348
0
    }
1349
1350
0
    if (iAttr < 0 || iAttr >= poNode->GetChildCount())
1351
0
        return nullptr;
1352
1353
0
    return poNode->GetChild(iAttr)->GetValue();
1354
0
}
1355
1356
/************************************************************************/
1357
/*                          OSRGetAttrValue()                           */
1358
/************************************************************************/
1359
1360
/**
1361
 * \brief Fetch indicated attribute of named node.
1362
 *
1363
 * This function is the same as OGRSpatialReference::GetAttrValue()
1364
 */
1365
const char *CPL_STDCALL OSRGetAttrValue(OGRSpatialReferenceH hSRS,
1366
                                        const char *pszKey, int iChild)
1367
1368
0
{
1369
0
    VALIDATE_POINTER1(hSRS, "OSRGetAttrValue", nullptr);
1370
1371
0
    return ToPointer(hSRS)->GetAttrValue(pszKey, iChild);
1372
0
}
1373
1374
/************************************************************************/
1375
/*                             GetName()                                */
1376
/************************************************************************/
1377
1378
/**
1379
 * \brief Return the CRS name.
1380
 *
1381
 * The returned value is only short lived and should not be used after other
1382
 * calls to methods on this object.
1383
 *
1384
 * @since GDAL 3.0
1385
 */
1386
1387
const char *OGRSpatialReference::GetName() const
1388
0
{
1389
0
    TAKE_OPTIONAL_LOCK();
1390
1391
0
    d->refreshProjObj();
1392
0
    if (!d->m_pj_crs)
1393
0
        return nullptr;
1394
0
    const char *pszName = proj_get_name(d->m_pj_crs);
1395
#if PROJ_VERSION_NUMBER == PROJ_COMPUTE_VERSION(8, 2, 0)
1396
    if (d->m_pjType == PJ_TYPE_BOUND_CRS && EQUAL(pszName, "SOURCECRS"))
1397
    {
1398
        // Work around a bug of PROJ 8.2.0 (fixed in 8.2.1)
1399
        PJ *baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
1400
        if (baseCRS)
1401
        {
1402
            pszName = proj_get_name(baseCRS);
1403
            // pszName still remains valid after proj_destroy(), since
1404
            // d->m_pj_crs keeps a reference to the base CRS C++ object.
1405
            proj_destroy(baseCRS);
1406
        }
1407
    }
1408
#endif
1409
0
    return pszName;
1410
0
}
1411
1412
/************************************************************************/
1413
/*                           OSRGetName()                               */
1414
/************************************************************************/
1415
1416
/**
1417
 * \brief Return the CRS name.
1418
 *
1419
 * The returned value is only short lived and should not be used after other
1420
 * calls to methods on this object.
1421
 *
1422
 * @since GDAL 3.0
1423
 */
1424
const char *OSRGetName(OGRSpatialReferenceH hSRS)
1425
1426
0
{
1427
0
    VALIDATE_POINTER1(hSRS, "OSRGetName", nullptr);
1428
1429
0
    return ToPointer(hSRS)->GetName();
1430
0
}
1431
1432
/************************************************************************/
1433
/*                       GetCelestialBodyName()                         */
1434
/************************************************************************/
1435
1436
/**
1437
 * \brief Return the name of the celestial body of this CRS.
1438
 *
1439
 * e.g. "Earth" for an Earth CRS
1440
 *
1441
 * The returned value is only short lived and should not be used after other
1442
 * calls to methods on this object.
1443
 *
1444
 * @since GDAL 3.12 and PROJ 8.1
1445
 */
1446
1447
const char *OGRSpatialReference::GetCelestialBodyName() const
1448
0
{
1449
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
1450
1451
0
    TAKE_OPTIONAL_LOCK();
1452
1453
0
    d->refreshProjObj();
1454
0
    if (!d->m_pj_crs)
1455
0
        return nullptr;
1456
0
    d->demoteFromBoundCRS();
1457
0
    const char *name =
1458
0
        proj_get_celestial_body_name(d->getPROJContext(), d->m_pj_crs);
1459
0
    if (name)
1460
0
    {
1461
0
        d->m_celestialBodyName = name;
1462
0
    }
1463
0
    d->undoDemoteFromBoundCRS();
1464
0
    return d->m_celestialBodyName.c_str();
1465
#else
1466
    if (std::fabs(GetSemiMajor(nullptr) - SRS_WGS84_SEMIMAJOR) <=
1467
        0.05 * SRS_WGS84_SEMIMAJOR)
1468
        return "Earth";
1469
    const char *pszAuthName = GetAuthorityName(nullptr);
1470
    if (pszAuthName && EQUAL(pszAuthName, "EPSG"))
1471
        return "Earth";
1472
    return nullptr;
1473
#endif
1474
0
}
1475
1476
/************************************************************************/
1477
/*                       OSRGetCelestialBodyName()                      */
1478
/************************************************************************/
1479
1480
/**
1481
 * \brief Return the name of the celestial body of this CRS.
1482
 *
1483
 * e.g. "Earth" for an Earth CRS
1484
 *
1485
 * The returned value is only short lived and should not be used after other
1486
 * calls to methods on this object.
1487
 *
1488
 * @since GDAL 3.12 and PROJ 8.1
1489
 */
1490
1491
const char *OSRGetCelestialBodyName(OGRSpatialReferenceH hSRS)
1492
1493
0
{
1494
0
    VALIDATE_POINTER1(hSRS, "GetCelestialBodyName", nullptr);
1495
1496
0
    return ToPointer(hSRS)->GetCelestialBodyName();
1497
0
}
1498
1499
/************************************************************************/
1500
/*                               Clone()                                */
1501
/************************************************************************/
1502
1503
/**
1504
 * \brief Make a duplicate of this OGRSpatialReference.
1505
 *
1506
 * This method is the same as the C function OSRClone().
1507
 *
1508
 * @return a new SRS, which becomes the responsibility of the caller.
1509
 */
1510
1511
OGRSpatialReference *OGRSpatialReference::Clone() const
1512
1513
0
{
1514
0
    OGRSpatialReference *poNewRef = new OGRSpatialReference();
1515
1516
0
    TAKE_OPTIONAL_LOCK();
1517
1518
0
    d->refreshProjObj();
1519
0
    if (d->m_pj_crs != nullptr)
1520
0
        poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1521
0
    if (d->m_bHasCenterLong && d->m_poRoot)
1522
0
    {
1523
0
        poNewRef->d->setRoot(d->m_poRoot->Clone());
1524
0
    }
1525
0
    poNewRef->d->m_axisMapping = d->m_axisMapping;
1526
0
    poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1527
0
    poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1528
0
    return poNewRef;
1529
0
}
1530
1531
/************************************************************************/
1532
/*                              OSRClone()                              */
1533
/************************************************************************/
1534
1535
/**
1536
 * \brief Make a duplicate of this OGRSpatialReference.
1537
 *
1538
 * This function is the same as OGRSpatialReference::Clone()
1539
 */
1540
OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1541
1542
0
{
1543
0
    VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1544
1545
0
    return ToHandle(ToPointer(hSRS)->Clone());
1546
0
}
1547
1548
/************************************************************************/
1549
/*                            dumpReadable()                            */
1550
/************************************************************************/
1551
1552
/** Dump pretty wkt to stdout, mostly for debugging.
1553
 */
1554
void OGRSpatialReference::dumpReadable()
1555
1556
0
{
1557
0
    char *pszPrettyWkt = nullptr;
1558
1559
0
    const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1560
0
    exportToWkt(&pszPrettyWkt, apszOptions);
1561
0
    printf("%s\n", pszPrettyWkt); /*ok*/
1562
0
    CPLFree(pszPrettyWkt);
1563
0
}
1564
1565
/************************************************************************/
1566
/*                         exportToPrettyWkt()                          */
1567
/************************************************************************/
1568
1569
/**
1570
 * Convert this SRS into a nicely formatted WKT 1 string for display to a
1571
 * person.
1572
 *
1573
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1574
 * Issues</a> page for implementation details of WKT 1 in OGR.
1575
 *
1576
 * Note that the returned WKT string should be freed with
1577
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1578
 *
1579
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1580
 * option. Valid values are the one of the FORMAT option of
1581
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1582
 *
1583
 * This method is the same as the C function OSRExportToPrettyWkt().
1584
 *
1585
 * @param ppszResult the resulting string is returned in this pointer.
1586
 * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1587
 *   stripped off.
1588
 *
1589
 * @return OGRERR_NONE if successful.
1590
 */
1591
1592
OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1593
                                              int bSimplify) const
1594
1595
0
{
1596
0
    CPLStringList aosOptions;
1597
0
    aosOptions.SetNameValue("MULTILINE", "YES");
1598
0
    if (bSimplify)
1599
0
    {
1600
0
        aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1601
0
    }
1602
0
    return exportToWkt(ppszResult, aosOptions.List());
1603
0
}
1604
1605
/************************************************************************/
1606
/*                        OSRExportToPrettyWkt()                        */
1607
/************************************************************************/
1608
1609
/**
1610
 * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1611
 * person.
1612
 *
1613
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1614
 * option. Valid values are the one of the FORMAT option of
1615
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1616
 *
1617
 * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1618
 */
1619
1620
OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1621
                                        char **ppszReturn, int bSimplify)
1622
1623
0
{
1624
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1625
1626
0
    *ppszReturn = nullptr;
1627
1628
0
    return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1629
0
}
1630
1631
/************************************************************************/
1632
/*                            exportToWkt()                             */
1633
/************************************************************************/
1634
1635
/**
1636
 * \brief Convert this SRS into WKT 1 format.
1637
 *
1638
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1639
 * Issues</a> page for implementation details of WKT 1 in OGR.
1640
 *
1641
 * Note that the returned WKT string should be freed with
1642
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1643
 *
1644
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1645
 * option. Valid values are the one of the FORMAT option of
1646
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1647
 *
1648
 * This method is the same as the C function OSRExportToWkt().
1649
 *
1650
 * @param ppszResult the resulting string is returned in this pointer.
1651
 *
1652
 * @return OGRERR_NONE if successful.
1653
 */
1654
1655
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1656
1657
0
{
1658
0
    return exportToWkt(ppszResult, nullptr);
1659
0
}
1660
1661
/************************************************************************/
1662
/*                GDAL_proj_crs_create_bound_crs_to_WGS84()             */
1663
/************************************************************************/
1664
1665
static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1666
                                                   bool onlyIfEPSGCode,
1667
                                                   bool canModifyHorizPart)
1668
0
{
1669
0
    PJ *ret = nullptr;
1670
0
    if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1671
0
    {
1672
0
        auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1673
0
        auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1674
0
        if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1675
0
            vertCRS &&
1676
0
            (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1677
0
        {
1678
0
            auto boundHoriz =
1679
0
                canModifyHorizPart
1680
0
                    ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1681
0
                    : proj_clone(ctx, horizCRS);
1682
0
            auto boundVert =
1683
0
                proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1684
0
            if (boundHoriz && boundVert)
1685
0
            {
1686
0
                ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1687
0
                                               boundHoriz, boundVert);
1688
0
            }
1689
0
            proj_destroy(boundHoriz);
1690
0
            proj_destroy(boundVert);
1691
0
        }
1692
0
        proj_destroy(horizCRS);
1693
0
        proj_destroy(vertCRS);
1694
0
    }
1695
0
    else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1696
0
             (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1697
0
    {
1698
0
        ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1699
0
    }
1700
0
    return ret;
1701
0
}
1702
1703
/************************************************************************/
1704
/*                            exportToWkt()                             */
1705
/************************************************************************/
1706
1707
/**
1708
 * Convert this SRS into a WKT string.
1709
 *
1710
 * Note that the returned WKT string should be freed with
1711
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1712
 *
1713
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1714
 * Issues</a> page for implementation details of WKT 1 in OGR.
1715
 *
1716
 * @param ppszResult the resulting string is returned in this pointer.
1717
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1718
 * supported options are
1719
 * <ul>
1720
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1721
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1722
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1723
 *     node is returned.
1724
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1725
 *     node is returned.
1726
 *     WKT1 is an alias of WKT1_GDAL.
1727
 *     WKT2 will default to the latest revision implemented (currently
1728
 *     WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1729
 * </li>
1730
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1731
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1732
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1733
 * height (for example for use with LAS 1.4 WKT1).
1734
 * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1735
 * </ul>
1736
 *
1737
 * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1738
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1739
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1740
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1741
 * TOWGS84[] node may be added.
1742
 *
1743
 * @return OGRERR_NONE if successful.
1744
 * @since GDAL 3.0
1745
 */
1746
1747
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1748
                                        const char *const *papszOptions) const
1749
0
{
1750
    // In the past calling this method was thread-safe, even if we never
1751
    // guaranteed it. Now proj_as_wkt() will cache the result internally,
1752
    // so this is no longer thread-safe.
1753
0
    std::lock_guard oLock(d->m_mutex);
1754
1755
0
    d->refreshProjObj();
1756
0
    if (!d->m_pj_crs)
1757
0
    {
1758
0
        *ppszResult = CPLStrdup("");
1759
0
        return OGRERR_FAILURE;
1760
0
    }
1761
1762
0
    if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1763
0
    {
1764
0
        return d->m_poRoot->exportToWkt(ppszResult);
1765
0
    }
1766
1767
0
    auto ctxt = d->getPROJContext();
1768
0
    auto wktFormat = PJ_WKT1_GDAL;
1769
0
    const char *pszFormat =
1770
0
        CSLFetchNameValueDef(papszOptions, "FORMAT",
1771
0
                             CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1772
0
    if (EQUAL(pszFormat, "DEFAULT"))
1773
0
        pszFormat = "";
1774
1775
0
    if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1776
0
    {
1777
0
        wktFormat = PJ_WKT1_ESRI;
1778
0
    }
1779
0
    else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1780
0
             EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1781
0
    {
1782
0
        wktFormat = PJ_WKT1_GDAL;
1783
0
    }
1784
0
    else if (EQUAL(pszFormat, "WKT2_2015"))
1785
0
    {
1786
0
        wktFormat = PJ_WKT2_2015;
1787
0
    }
1788
0
    else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1789
0
             EQUAL(pszFormat, "WKT2_2019"))
1790
0
    {
1791
0
        wktFormat = PJ_WKT2_2018;
1792
0
    }
1793
0
    else if (pszFormat[0] == '\0')
1794
0
    {
1795
        // cppcheck-suppress knownConditionTrueFalse
1796
0
        if (IsDerivedGeographic())
1797
0
        {
1798
0
            wktFormat = PJ_WKT2_2018;
1799
0
        }
1800
0
        else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1801
0
                 GetAxesCount() == 3)
1802
0
        {
1803
0
            wktFormat = PJ_WKT2_2018;
1804
0
        }
1805
0
    }
1806
0
    else
1807
0
    {
1808
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1809
0
        *ppszResult = CPLStrdup("");
1810
0
        return OGRERR_FAILURE;
1811
0
    }
1812
1813
0
    CPLStringList aosOptions;
1814
0
    if (wktFormat != PJ_WKT1_ESRI)
1815
0
    {
1816
0
        aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1817
0
    }
1818
0
    aosOptions.SetNameValue(
1819
0
        "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1820
1821
0
    const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1822
0
        papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1823
0
    if (pszAllowEllpsHeightAsVertCS)
1824
0
    {
1825
0
        aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1826
0
                                pszAllowEllpsHeightAsVertCS);
1827
0
    }
1828
1829
0
    PJ *boundCRS = nullptr;
1830
0
    if (wktFormat == PJ_WKT1_GDAL &&
1831
0
        CPLTestBool(CSLFetchNameValueDef(
1832
0
            papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1833
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1834
0
    {
1835
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1836
0
            d->getPROJContext(), d->m_pj_crs, true, true);
1837
0
    }
1838
1839
0
    CPLErrorAccumulator oErrorAccumulator;
1840
0
    const char *pszWKT;
1841
0
    {
1842
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1843
0
        CPL_IGNORE_RET_VAL(oAccumulator);
1844
0
        pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1845
0
                             aosOptions.List());
1846
0
    }
1847
0
    for (const auto &oError : oErrorAccumulator.GetErrors())
1848
0
    {
1849
0
        if (pszFormat[0] == '\0' &&
1850
0
            (oError.msg.find("Unsupported conversion method") !=
1851
0
                 std::string::npos ||
1852
0
             oError.msg.find("can only be exported to WKT2") !=
1853
0
                 std::string::npos ||
1854
0
             oError.msg.find("can only be exported since WKT2:2019") !=
1855
0
                 std::string::npos))
1856
0
        {
1857
0
            CPLErrorReset();
1858
            // If we cannot export in the default mode (WKT1), retry with WKT2
1859
0
            pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1860
0
                                 PJ_WKT2_2018, aosOptions.List());
1861
0
            break;
1862
0
        }
1863
0
        CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1864
0
    }
1865
1866
0
    if (!pszWKT)
1867
0
    {
1868
0
        *ppszResult = CPLStrdup("");
1869
0
        proj_destroy(boundCRS);
1870
0
        return OGRERR_FAILURE;
1871
0
    }
1872
1873
0
    if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1874
0
    {
1875
0
        OGR_SRSNode oRoot;
1876
0
        oRoot.importFromWkt(&pszWKT);
1877
0
        oRoot.StripNodes("AXIS");
1878
0
        if (EQUAL(pszFormat, "SFSQL"))
1879
0
        {
1880
0
            oRoot.StripNodes("TOWGS84");
1881
0
        }
1882
0
        oRoot.StripNodes("AUTHORITY");
1883
0
        oRoot.StripNodes("EXTENSION");
1884
0
        OGRErr eErr;
1885
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1886
0
            eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1887
0
        else
1888
0
            eErr = oRoot.exportToWkt(ppszResult);
1889
0
        proj_destroy(boundCRS);
1890
0
        return eErr;
1891
0
    }
1892
1893
0
    *ppszResult = CPLStrdup(pszWKT);
1894
1895
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1896
    if (wktFormat == PJ_WKT2_2018)
1897
    {
1898
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1899
        // related to a wrong EPSG code assigned to UTM South conversions
1900
        char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1901
        if (pszPtr)
1902
        {
1903
            pszPtr += strlen("CONVERSION[\"UTM zone ");
1904
            const int nZone = atoi(pszPtr);
1905
            while (*pszPtr >= '0' && *pszPtr <= '9')
1906
                ++pszPtr;
1907
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1908
                pszPtr[1] == '"' && pszPtr[2] == ',')
1909
            {
1910
                pszPtr += 3;
1911
                int nLevel = 0;
1912
                bool bInString = false;
1913
                // Find the ID node corresponding to this CONVERSION node
1914
                while (*pszPtr)
1915
                {
1916
                    if (bInString)
1917
                    {
1918
                        if (*pszPtr == '"' && pszPtr[1] == '"')
1919
                        {
1920
                            ++pszPtr;
1921
                        }
1922
                        else if (*pszPtr == '"')
1923
                        {
1924
                            bInString = false;
1925
                        }
1926
                    }
1927
                    else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1928
                    {
1929
                        if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1930
                                                              17000 + nZone)))
1931
                        {
1932
                            CPLAssert(pszPtr[11] == '7');
1933
                            CPLAssert(pszPtr[12] == '0');
1934
                            pszPtr[11] = '6';
1935
                            pszPtr[12] = '1';
1936
                        }
1937
                        break;
1938
                    }
1939
                    else if (*pszPtr == '"')
1940
                    {
1941
                        bInString = true;
1942
                    }
1943
                    else if (*pszPtr == '[')
1944
                    {
1945
                        ++nLevel;
1946
                    }
1947
                    else if (*pszPtr == ']')
1948
                    {
1949
                        --nLevel;
1950
                    }
1951
1952
                    ++pszPtr;
1953
                }
1954
            }
1955
        }
1956
    }
1957
#endif
1958
1959
0
    proj_destroy(boundCRS);
1960
0
    return OGRERR_NONE;
1961
0
}
1962
1963
/************************************************************************/
1964
/*                            exportToWkt()                             */
1965
/************************************************************************/
1966
1967
/**
1968
 * Convert this SRS into a WKT string.
1969
 *
1970
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1971
 * Issues</a> page for implementation details of WKT 1 in OGR.
1972
 *
1973
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1974
 * supported options are
1975
 * <ul>
1976
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1977
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1978
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1979
 *     node is returned.
1980
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1981
 *     node is returned.
1982
 *     WKT1 is an alias of WKT1_GDAL.
1983
 *     WKT2 will default to the latest revision implemented (currently
1984
 *     WKT2_2019)
1985
 * </li>
1986
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1987
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1988
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1989
 * height (for example for use with LAS 1.4 WKT1).
1990
 * Requires PROJ 7.2.1.</li>
1991
 * </ul>
1992
 *
1993
 * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1994
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1995
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1996
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1997
 * TOWGS84[] node may be added.
1998
 *
1999
 * @return a non-empty string if successful.
2000
 * @since GDAL 3.9
2001
 */
2002
2003
std::string
2004
OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
2005
0
{
2006
0
    std::string osWKT;
2007
0
    char *pszWKT = nullptr;
2008
0
    if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
2009
0
        osWKT = pszWKT;
2010
0
    CPLFree(pszWKT);
2011
0
    return osWKT;
2012
0
}
2013
2014
/************************************************************************/
2015
/*                           OSRExportToWkt()                           */
2016
/************************************************************************/
2017
2018
/**
2019
 * \brief Convert this SRS into WKT 1 format.
2020
 *
2021
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2022
 * Issues</a> page for implementation details of WKT in OGR.
2023
 *
2024
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
2025
 * option. Valid values are the one of the FORMAT option of
2026
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
2027
 *
2028
 * This function is the same as OGRSpatialReference::exportToWkt().
2029
 */
2030
2031
OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
2032
2033
0
{
2034
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
2035
2036
0
    *ppszReturn = nullptr;
2037
2038
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn);
2039
0
}
2040
2041
/************************************************************************/
2042
/*                          OSRExportToWktEx()                          */
2043
/************************************************************************/
2044
2045
/**
2046
 * \brief Convert this SRS into WKT format.
2047
 *
2048
 * This function is the same as OGRSpatialReference::exportToWkt(char **
2049
 * ppszResult,const char* const* papszOptions ) const
2050
 *
2051
 * @since GDAL 3.0
2052
 */
2053
2054
OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
2055
                        const char *const *papszOptions)
2056
0
{
2057
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
2058
2059
0
    *ppszReturn = nullptr;
2060
2061
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
2062
0
}
2063
2064
/************************************************************************/
2065
/*                       exportToPROJJSON()                             */
2066
/************************************************************************/
2067
2068
/**
2069
 * Convert this SRS into a PROJJSON string.
2070
 *
2071
 * Note that the returned JSON string should be freed with
2072
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
2073
 *
2074
 * @param ppszResult the resulting string is returned in this pointer.
2075
 * @param papszOptions NULL terminated list of options, or NULL. Currently
2076
 * supported options are
2077
 * <ul>
2078
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
2079
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2080
 * on).</li>
2081
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2082
 * disable it.</li>
2083
 * </ul>
2084
 *
2085
 * @return OGRERR_NONE if successful.
2086
 * @since GDAL 3.1 and PROJ 6.2
2087
 */
2088
2089
OGRErr OGRSpatialReference::exportToPROJJSON(
2090
    char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2091
0
{
2092
0
    TAKE_OPTIONAL_LOCK();
2093
2094
0
    d->refreshProjObj();
2095
0
    if (!d->m_pj_crs)
2096
0
    {
2097
0
        *ppszResult = nullptr;
2098
0
        return OGRERR_FAILURE;
2099
0
    }
2100
2101
0
    const char *pszPROJJSON =
2102
0
        proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2103
2104
0
    if (!pszPROJJSON)
2105
0
    {
2106
0
        *ppszResult = CPLStrdup("");
2107
0
        return OGRERR_FAILURE;
2108
0
    }
2109
2110
0
    *ppszResult = CPLStrdup(pszPROJJSON);
2111
2112
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2113
    {
2114
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2115
        // related to a wrong EPSG code assigned to UTM South conversions
2116
        char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2117
        if (pszPtr)
2118
        {
2119
            pszPtr += strlen("\"name\": \"UTM zone ");
2120
            const int nZone = atoi(pszPtr);
2121
            while (*pszPtr >= '0' && *pszPtr <= '9')
2122
                ++pszPtr;
2123
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2124
            {
2125
                pszPtr += 2;
2126
                int nLevel = 0;
2127
                bool bInString = false;
2128
                // Find the id node corresponding to this conversion node
2129
                while (*pszPtr)
2130
                {
2131
                    if (bInString)
2132
                    {
2133
                        if (*pszPtr == '\\')
2134
                        {
2135
                            ++pszPtr;
2136
                        }
2137
                        else if (*pszPtr == '"')
2138
                        {
2139
                            bInString = false;
2140
                        }
2141
                    }
2142
                    else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2143
                    {
2144
                        const char *pszNextEndCurl = strchr(pszPtr, '}');
2145
                        const char *pszAuthEPSG =
2146
                            strstr(pszPtr, "\"authority\": \"EPSG\"");
2147
                        char *pszCode = strstr(
2148
                            pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2149
                        if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2150
                            pszNextEndCurl - pszAuthEPSG > 0 &&
2151
                            pszNextEndCurl - pszCode > 0)
2152
                        {
2153
                            CPLAssert(pszCode[9] == '7');
2154
                            CPLAssert(pszCode[10] == '0');
2155
                            pszCode[9] = '6';
2156
                            pszCode[10] = '1';
2157
                        }
2158
                        break;
2159
                    }
2160
                    else if (*pszPtr == '"')
2161
                    {
2162
                        bInString = true;
2163
                    }
2164
                    else if (*pszPtr == '{' || *pszPtr == '[')
2165
                    {
2166
                        ++nLevel;
2167
                    }
2168
                    else if (*pszPtr == '}' || *pszPtr == ']')
2169
                    {
2170
                        --nLevel;
2171
                    }
2172
2173
                    ++pszPtr;
2174
                }
2175
            }
2176
        }
2177
    }
2178
#endif
2179
2180
0
    return OGRERR_NONE;
2181
0
}
2182
2183
/************************************************************************/
2184
/*                          OSRExportToPROJJSON()                       */
2185
/************************************************************************/
2186
2187
/**
2188
 * \brief Convert this SRS into PROJJSON format.
2189
 *
2190
 * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2191
 *
2192
 * @since GDAL 3.1 and PROJ 6.2
2193
 */
2194
2195
OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2196
                           const char *const *papszOptions)
2197
0
{
2198
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2199
2200
0
    *ppszReturn = nullptr;
2201
2202
0
    return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2203
0
}
2204
2205
/************************************************************************/
2206
/*                           importFromWkt()                            */
2207
/************************************************************************/
2208
2209
/**
2210
 * \brief Import from WKT string.
2211
 *
2212
 * This method will wipe the existing SRS definition, and
2213
 * reassign it based on the contents of the passed WKT string.  Only as
2214
 * much of the input string as needed to construct this SRS is consumed from
2215
 * the input string, and the input string pointer
2216
 * is then updated to point to the remaining (unused) input.
2217
 *
2218
 * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2219
 * the CRS contained in it will be used to fill the OGRSpatialReference object,
2220
 * and the coordinate epoch potentially present used as the coordinate epoch
2221
 * property of the OGRSpatialReference object.
2222
 *
2223
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2224
 * Issues</a> page for implementation details of WKT in OGR.
2225
 *
2226
 * This method is the same as the C function OSRImportFromWkt().
2227
 *
2228
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2229
 * point to remaining unused input text.
2230
 *
2231
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2232
 * fails for any reason.
2233
 */
2234
2235
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2236
2237
0
{
2238
0
    return importFromWkt(ppszInput, nullptr);
2239
0
}
2240
2241
/************************************************************************/
2242
/*                           importFromWkt()                            */
2243
/************************************************************************/
2244
2245
/*! @cond Doxygen_Suppress */
2246
2247
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2248
                                          CSLConstList papszOptions)
2249
2250
0
{
2251
0
    return importFromWkt(&pszInput, papszOptions);
2252
0
}
2253
2254
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2255
                                          CSLConstList papszOptions)
2256
2257
0
{
2258
0
    TAKE_OPTIONAL_LOCK();
2259
2260
0
    if (!ppszInput || !*ppszInput)
2261
0
        return OGRERR_FAILURE;
2262
2263
0
    if (strlen(*ppszInput) > 100 * 1000 &&
2264
0
        CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2265
0
    {
2266
0
        CPLError(CE_Failure, CPLE_NotSupported,
2267
0
                 "Suspiciously large input for importFromWkt(). Rejecting it. "
2268
0
                 "You can remove this limitation by definition the "
2269
0
                 "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2270
0
        return OGRERR_FAILURE;
2271
0
    }
2272
2273
0
    Clear();
2274
2275
0
    bool canCache = false;
2276
0
    auto tlsCache = OSRGetProjTLSCache();
2277
0
    std::string osWkt;
2278
0
    if (**ppszInput)
2279
0
    {
2280
0
        osWkt = *ppszInput;
2281
0
        auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2282
0
        if (cachedObj)
2283
0
        {
2284
0
            d->setPjCRS(cachedObj);
2285
0
        }
2286
0
        else
2287
0
        {
2288
0
            CPLStringList aosOptions(papszOptions);
2289
0
            if (aosOptions.FetchNameValue("STRICT") == nullptr)
2290
0
                aosOptions.SetNameValue("STRICT", "NO");
2291
0
            PROJ_STRING_LIST warnings = nullptr;
2292
0
            PROJ_STRING_LIST errors = nullptr;
2293
0
            auto ctxt = d->getPROJContext();
2294
0
            auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2295
0
                                           &warnings, &errors);
2296
0
            d->setPjCRS(pj);
2297
2298
0
            for (auto iter = warnings; iter && *iter; ++iter)
2299
0
            {
2300
0
                d->m_wktImportWarnings.push_back(*iter);
2301
0
            }
2302
0
            for (auto iter = errors; iter && *iter; ++iter)
2303
0
            {
2304
0
                d->m_wktImportErrors.push_back(*iter);
2305
0
                if (!d->m_pj_crs)
2306
0
                {
2307
0
                    CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2308
0
                }
2309
0
            }
2310
0
            if (warnings == nullptr && errors == nullptr)
2311
0
            {
2312
0
                canCache = true;
2313
0
            }
2314
0
            proj_string_list_destroy(warnings);
2315
0
            proj_string_list_destroy(errors);
2316
0
        }
2317
0
    }
2318
0
    if (!d->m_pj_crs)
2319
0
        return OGRERR_CORRUPT_DATA;
2320
2321
    // Only accept CRS objects
2322
0
    if (!proj_is_crs(d->m_pj_crs))
2323
0
    {
2324
0
        Clear();
2325
0
        return OGRERR_CORRUPT_DATA;
2326
0
    }
2327
2328
0
    if (canCache)
2329
0
    {
2330
0
        tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2331
0
    }
2332
2333
0
    if (strstr(*ppszInput, "CENTER_LONG"))
2334
0
    {
2335
0
        auto poRoot = new OGR_SRSNode();
2336
0
        d->setRoot(poRoot);
2337
0
        const char *pszTmp = *ppszInput;
2338
0
        poRoot->importFromWkt(&pszTmp);
2339
0
        d->m_bHasCenterLong = true;
2340
0
    }
2341
2342
    // TODO? we don't really update correctly since we assume that the
2343
    // passed string is only WKT.
2344
0
    *ppszInput += strlen(*ppszInput);
2345
0
    return OGRERR_NONE;
2346
2347
#if no_longer_implemented_for_now
2348
    /* -------------------------------------------------------------------- */
2349
    /*      The following seems to try and detect and unconsumed            */
2350
    /*      VERTCS[] coordinate system definition (ESRI style) and to       */
2351
    /*      import and attach it to the existing root.  Likely we will      */
2352
    /*      need to extend this somewhat to bring it into an acceptable     */
2353
    /*      OGRSpatialReference organization at some point.                 */
2354
    /* -------------------------------------------------------------------- */
2355
    if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2356
    {
2357
        if (((*ppszInput)[0]) == ',')
2358
            (*ppszInput)++;
2359
        OGR_SRSNode *poNewChild = new OGR_SRSNode();
2360
        poRoot->AddChild(poNewChild);
2361
        return poNewChild->importFromWkt(ppszInput);
2362
    }
2363
#endif
2364
0
}
2365
2366
/*! @endcond */
2367
2368
/**
2369
 * \brief Import from WKT string.
2370
 *
2371
 * This method will wipe the existing SRS definition, and
2372
 * reassign it based on the contents of the passed WKT string.  Only as
2373
 * much of the input string as needed to construct this SRS is consumed from
2374
 * the input string, and the input string pointer
2375
 * is then updated to point to the remaining (unused) input.
2376
 *
2377
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2378
 * Issues</a> page for implementation details of WKT in OGR.
2379
 *
2380
 * This method is the same as the C function OSRImportFromWkt().
2381
 *
2382
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2383
 * point to remaining unused input text.
2384
 *
2385
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2386
 * fails for any reason.
2387
 * @deprecated Use importFromWkt(const char**) or importFromWkt(const
2388
 * char*)
2389
 */
2390
2391
OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2392
2393
0
{
2394
0
    return importFromWkt(const_cast<const char **>(ppszInput));
2395
0
}
2396
2397
/**
2398
 * \brief Import from WKT string.
2399
 *
2400
 * This method will wipe the existing SRS definition, and
2401
 * reassign it based on the contents of the passed WKT string.  Only as
2402
 * much of the input string as needed to construct this SRS is consumed from
2403
 * the input string, and the input string pointer
2404
 * is then updated to point to the remaining (unused) input.
2405
 *
2406
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2407
 * Issues</a> page for implementation details of WKT in OGR.
2408
 *
2409
 * @param pszInput Input WKT
2410
 *
2411
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2412
 * fails for any reason.
2413
 */
2414
2415
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2416
0
{
2417
0
    return importFromWkt(&pszInput);
2418
0
}
2419
2420
/************************************************************************/
2421
/*                              Validate()                              */
2422
/************************************************************************/
2423
2424
/**
2425
 * \brief Validate CRS imported with importFromWkt() or with modified with
2426
 * direct node manipulations. Otherwise the CRS should be always valid.
2427
 *
2428
 * This method attempts to verify that the spatial reference system is
2429
 * well formed, and consists of known tokens.  The validation is not
2430
 * comprehensive.
2431
 *
2432
 * This method is the same as the C function OSRValidate().
2433
 *
2434
 * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2435
 * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2436
 * but contains non-standard PROJECTION[] values.
2437
 */
2438
2439
OGRErr OGRSpatialReference::Validate() const
2440
2441
0
{
2442
0
    TAKE_OPTIONAL_LOCK();
2443
2444
0
    for (const auto &str : d->m_wktImportErrors)
2445
0
    {
2446
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2447
0
    }
2448
0
    for (const auto &str : d->m_wktImportWarnings)
2449
0
    {
2450
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2451
0
    }
2452
0
    if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2453
0
    {
2454
0
        return OGRERR_CORRUPT_DATA;
2455
0
    }
2456
0
    if (!d->m_wktImportWarnings.empty())
2457
0
    {
2458
0
        return OGRERR_UNSUPPORTED_SRS;
2459
0
    }
2460
0
    return OGRERR_NONE;
2461
0
}
2462
2463
/************************************************************************/
2464
/*                            OSRValidate()                             */
2465
/************************************************************************/
2466
/**
2467
 * \brief Validate SRS tokens.
2468
 *
2469
 * This function is the same as the C++ method OGRSpatialReference::Validate().
2470
 */
2471
OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2472
2473
0
{
2474
0
    VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2475
2476
0
    return OGRSpatialReference::FromHandle(hSRS)->Validate();
2477
0
}
2478
2479
/************************************************************************/
2480
/*                          OSRImportFromWkt()                          */
2481
/************************************************************************/
2482
2483
/**
2484
 * \brief Import from WKT string.
2485
 *
2486
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2487
 * Issues</a> page for implementation details of WKT in OGR.
2488
 *
2489
 * This function is the same as OGRSpatialReference::importFromWkt().
2490
 */
2491
2492
OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2493
2494
0
{
2495
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2496
2497
0
    return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2498
0
}
2499
2500
/************************************************************************/
2501
/*                              SetNode()                               */
2502
/************************************************************************/
2503
2504
/**
2505
 * \brief Set attribute value in spatial reference.
2506
 *
2507
 * Missing intermediate nodes in the path will be created if not already
2508
 * in existence.  If the attribute has no children one will be created and
2509
 * assigned the value otherwise the zeroth child will be assigned the value.
2510
 *
2511
 * This method does the same as the C function OSRSetAttrValue().
2512
 *
2513
 * @param pszNodePath full path to attribute to be set.  For instance
2514
 * "PROJCS|GEOGCS|UNIT".
2515
 *
2516
 * @param pszNewNodeValue value to be assigned to node, such as "meter".
2517
 * This may be NULL if you just want to force creation of the intermediate
2518
 * path.
2519
 *
2520
 * @return OGRERR_NONE on success.
2521
 */
2522
2523
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2524
                                    const char *pszNewNodeValue)
2525
2526
0
{
2527
0
    TAKE_OPTIONAL_LOCK();
2528
2529
0
    char **papszPathTokens =
2530
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2531
2532
0
    if (CSLCount(papszPathTokens) < 1)
2533
0
    {
2534
0
        CSLDestroy(papszPathTokens);
2535
0
        return OGRERR_FAILURE;
2536
0
    }
2537
2538
0
    if (GetRoot() == nullptr ||
2539
0
        !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2540
0
    {
2541
0
        if (EQUAL(papszPathTokens[0], "PROJCS") &&
2542
0
            CSLCount(papszPathTokens) == 1)
2543
0
        {
2544
0
            CSLDestroy(papszPathTokens);
2545
0
            return SetProjCS(pszNewNodeValue);
2546
0
        }
2547
0
        else
2548
0
        {
2549
0
            SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2550
0
        }
2551
0
    }
2552
2553
0
    OGR_SRSNode *poNode = GetRoot();
2554
0
    for (int i = 1; papszPathTokens[i] != nullptr; i++)
2555
0
    {
2556
0
        int j = 0;  // Used after for.
2557
2558
0
        for (; j < poNode->GetChildCount(); j++)
2559
0
        {
2560
0
            if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2561
0
            {
2562
0
                poNode = poNode->GetChild(j);
2563
0
                j = -1;
2564
0
                break;
2565
0
            }
2566
0
        }
2567
2568
0
        if (j != -1)
2569
0
        {
2570
0
            OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2571
0
            poNode->AddChild(poNewNode);
2572
0
            poNode = poNewNode;
2573
0
        }
2574
0
    }
2575
2576
0
    CSLDestroy(papszPathTokens);
2577
2578
0
    if (pszNewNodeValue != nullptr)
2579
0
    {
2580
0
        if (poNode->GetChildCount() > 0)
2581
0
            poNode->GetChild(0)->SetValue(pszNewNodeValue);
2582
0
        else
2583
0
            poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2584
0
    };
2585
0
    return OGRERR_NONE;
2586
0
}
2587
2588
/************************************************************************/
2589
/*                          OSRSetAttrValue()                           */
2590
/************************************************************************/
2591
2592
/**
2593
 * \brief Set attribute value in spatial reference.
2594
 *
2595
 * This function is the same as OGRSpatialReference::SetNode()
2596
 */
2597
OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2598
                                   const char *pszPath, const char *pszValue)
2599
2600
0
{
2601
0
    VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2602
2603
0
    return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2604
0
}
2605
2606
/************************************************************************/
2607
/*                              SetNode()                               */
2608
/************************************************************************/
2609
2610
/**
2611
 * \brief Set attribute value in spatial reference.
2612
 *
2613
 * Missing intermediate nodes in the path will be created if not already
2614
 * in existence.  If the attribute has no children one will be created and
2615
 * assigned the value otherwise the zeroth child will be assigned the value.
2616
 *
2617
 * This method does the same as the C function OSRSetAttrValue().
2618
 *
2619
 * @param pszNodePath full path to attribute to be set.  For instance
2620
 * "PROJCS|GEOGCS|UNIT".
2621
 *
2622
 * @param dfValue value to be assigned to node.
2623
 *
2624
 * @return OGRERR_NONE on success.
2625
 */
2626
2627
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2628
2629
0
{
2630
0
    char szValue[64] = {'\0'};
2631
2632
0
    if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2633
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2634
0
    else
2635
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2636
2637
0
    return SetNode(pszNodePath, szValue);
2638
0
}
2639
2640
/************************************************************************/
2641
/*                          SetAngularUnits()                           */
2642
/************************************************************************/
2643
2644
/**
2645
 * \brief Set the angular units for the geographic coordinate system.
2646
 *
2647
 * This method creates a UNIT subnode with the specified values as a
2648
 * child of the GEOGCS node.
2649
 *
2650
 * This method does the same as the C function OSRSetAngularUnits().
2651
 *
2652
 * @param pszUnitsName the units name to be used.  Some preferred units
2653
 * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2654
 *
2655
 * @param dfInRadians the value to multiple by an angle in the indicated
2656
 * units to transform to radians.  Some standard conversion factors can
2657
 * be found in ogr_srs_api.h.
2658
 *
2659
 * @return OGRERR_NONE on success.
2660
 */
2661
2662
OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2663
                                            double dfInRadians)
2664
2665
0
{
2666
0
    TAKE_OPTIONAL_LOCK();
2667
2668
0
    d->bNormInfoSet = FALSE;
2669
2670
0
    d->refreshProjObj();
2671
0
    if (!d->m_pj_crs)
2672
0
        return OGRERR_FAILURE;
2673
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2674
0
    if (!geodCRS)
2675
0
        return OGRERR_FAILURE;
2676
0
    proj_destroy(geodCRS);
2677
0
    d->demoteFromBoundCRS();
2678
0
    d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2679
0
                                               pszUnitsName, dfInRadians,
2680
0
                                               nullptr, nullptr));
2681
0
    d->undoDemoteFromBoundCRS();
2682
2683
0
    d->m_osAngularUnits = pszUnitsName;
2684
0
    d->m_dfAngularUnitToRadian = dfInRadians;
2685
2686
0
    return OGRERR_NONE;
2687
0
}
2688
2689
/************************************************************************/
2690
/*                         OSRSetAngularUnits()                         */
2691
/************************************************************************/
2692
2693
/**
2694
 * \brief Set the angular units for the geographic coordinate system.
2695
 *
2696
 * This function is the same as OGRSpatialReference::SetAngularUnits()
2697
 */
2698
OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2699
                          double dfInRadians)
2700
2701
0
{
2702
0
    VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2703
2704
0
    return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2705
0
}
2706
2707
/************************************************************************/
2708
/*                          GetAngularUnits()                           */
2709
/************************************************************************/
2710
2711
/**
2712
 * \brief Fetch angular geographic coordinate system units.
2713
 *
2714
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2715
 * will be assumed.  This method only checks directly under the GEOGCS node
2716
 * for units.
2717
 *
2718
 * This method does the same thing as the C function OSRGetAngularUnits().
2719
 *
2720
 * @param ppszName a pointer to be updated with the pointer to the units name.
2721
 * The returned value remains internal to the OGRSpatialReference and should
2722
 * not be freed, or modified.  It may be invalidated on the next
2723
 * OGRSpatialReference call.
2724
 *
2725
 * @return the value to multiply by angular distances to transform them to
2726
 * radians.
2727
 */
2728
2729
double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2730
2731
0
{
2732
0
    TAKE_OPTIONAL_LOCK();
2733
2734
0
    d->refreshProjObj();
2735
2736
0
    if (!d->m_osAngularUnits.empty())
2737
0
    {
2738
0
        if (ppszName != nullptr)
2739
0
            *ppszName = d->m_osAngularUnits.c_str();
2740
0
        return d->m_dfAngularUnitToRadian;
2741
0
    }
2742
2743
0
    do
2744
0
    {
2745
0
        if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2746
0
        {
2747
0
            break;
2748
0
        }
2749
2750
0
        auto geodCRS =
2751
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2752
0
        if (!geodCRS)
2753
0
        {
2754
0
            break;
2755
0
        }
2756
0
        auto coordSys =
2757
0
            proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2758
0
        proj_destroy(geodCRS);
2759
0
        if (!coordSys)
2760
0
        {
2761
0
            break;
2762
0
        }
2763
0
        if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2764
0
            PJ_CS_TYPE_ELLIPSOIDAL)
2765
0
        {
2766
0
            proj_destroy(coordSys);
2767
0
            break;
2768
0
        }
2769
2770
0
        double dfConvFactor = 0.0;
2771
0
        const char *pszUnitName = nullptr;
2772
0
        if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2773
0
                                   nullptr, nullptr, &dfConvFactor,
2774
0
                                   &pszUnitName, nullptr, nullptr))
2775
0
        {
2776
0
            proj_destroy(coordSys);
2777
0
            break;
2778
0
        }
2779
2780
0
        d->m_osAngularUnits = pszUnitName;
2781
2782
0
        proj_destroy(coordSys);
2783
0
        d->m_dfAngularUnitToRadian = dfConvFactor;
2784
0
    } while (false);
2785
2786
0
    if (d->m_osAngularUnits.empty())
2787
0
    {
2788
0
        d->m_osAngularUnits = "degree";
2789
0
        d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2790
0
    }
2791
2792
0
    if (ppszName != nullptr)
2793
0
        *ppszName = d->m_osAngularUnits.c_str();
2794
0
    return d->m_dfAngularUnitToRadian;
2795
0
}
2796
2797
/**
2798
 * \brief Fetch angular geographic coordinate system units.
2799
 *
2800
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2801
 * will be assumed.  This method only checks directly under the GEOGCS node
2802
 * for units.
2803
 *
2804
 * This method does the same thing as the C function OSRGetAngularUnits().
2805
 *
2806
 * @param ppszName a pointer to be updated with the pointer to the units name.
2807
 * The returned value remains internal to the OGRSpatialReference and should
2808
 * not be freed, or modified.  It may be invalidated on the next
2809
 * OGRSpatialReference call.
2810
 *
2811
 * @return the value to multiply by angular distances to transform them to
2812
 * radians.
2813
 * @deprecated Use GetAngularUnits(const char**) const.
2814
 */
2815
2816
double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2817
2818
0
{
2819
0
    return GetAngularUnits(const_cast<const char **>(ppszName));
2820
0
}
2821
2822
/************************************************************************/
2823
/*                         OSRGetAngularUnits()                         */
2824
/************************************************************************/
2825
2826
/**
2827
 * \brief Fetch angular geographic coordinate system units.
2828
 *
2829
 * This function is the same as OGRSpatialReference::GetAngularUnits()
2830
 */
2831
double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2832
2833
0
{
2834
0
    VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2835
2836
0
    return ToPointer(hSRS)->GetAngularUnits(
2837
0
        const_cast<const char **>(ppszName));
2838
0
}
2839
2840
/************************************************************************/
2841
/*                 SetLinearUnitsAndUpdateParameters()                  */
2842
/************************************************************************/
2843
2844
/**
2845
 * \brief Set the linear units for the projection.
2846
 *
2847
 * This method creates a UNIT subnode with the specified values as a
2848
 * child of the PROJCS or LOCAL_CS node.   It works the same as the
2849
 * SetLinearUnits() method, but it also updates all existing linear
2850
 * projection parameter values from the old units to the new units.
2851
 *
2852
 * @param pszName the units name to be used.  Some preferred units
2853
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2854
 * and SRS_UL_US_FOOT.
2855
 *
2856
 * @param dfInMeters the value to multiple by a length in the indicated
2857
 * units to transform to meters.  Some standard conversion factors can
2858
 * be found in ogr_srs_api.h.
2859
 *
2860
 * @param pszUnitAuthority Unit authority name. Or nullptr
2861
 *
2862
 * @param pszUnitCode Unit code. Or nullptr
2863
 *
2864
 * @return OGRERR_NONE on success.
2865
 */
2866
2867
OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2868
    const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2869
    const char *pszUnitCode)
2870
2871
0
{
2872
0
    TAKE_OPTIONAL_LOCK();
2873
2874
0
    if (dfInMeters <= 0.0)
2875
0
        return OGRERR_FAILURE;
2876
2877
0
    d->refreshProjObj();
2878
0
    if (!d->m_pj_crs)
2879
0
        return OGRERR_FAILURE;
2880
2881
0
    d->demoteFromBoundCRS();
2882
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2883
0
    {
2884
0
        d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2885
0
            d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2886
0
            pszUnitAuthority, pszUnitCode, true));
2887
0
    }
2888
0
    d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2889
0
                                              pszName, dfInMeters,
2890
0
                                              pszUnitAuthority, pszUnitCode));
2891
0
    d->undoDemoteFromBoundCRS();
2892
2893
0
    d->m_osLinearUnits = pszName;
2894
0
    d->dfToMeter = dfInMeters;
2895
2896
0
    return OGRERR_NONE;
2897
0
}
2898
2899
/************************************************************************/
2900
/*                OSRSetLinearUnitsAndUpdateParameters()                */
2901
/************************************************************************/
2902
2903
/**
2904
 * \brief Set the linear units for the projection.
2905
 *
2906
 * This function is the same as
2907
 *   OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2908
 */
2909
OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2910
                                            const char *pszUnits,
2911
                                            double dfInMeters)
2912
2913
0
{
2914
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2915
0
                      OGRERR_FAILURE);
2916
2917
0
    return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2918
0
                                                              dfInMeters);
2919
0
}
2920
2921
/************************************************************************/
2922
/*                           SetLinearUnits()                           */
2923
/************************************************************************/
2924
2925
/**
2926
 * \brief Set the linear units for the projection.
2927
 *
2928
 * This method creates a UNIT subnode with the specified values as a
2929
 * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2930
 * Geographic 3D CRS the vertical axis units will be set.
2931
 *
2932
 * This method does the same as the C function OSRSetLinearUnits().
2933
 *
2934
 * @param pszUnitsName the units name to be used.  Some preferred units
2935
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2936
 * and SRS_UL_US_FOOT.
2937
 *
2938
 * @param dfInMeters the value to multiple by a length in the indicated
2939
 * units to transform to meters.  Some standard conversion factors can
2940
 * be found in ogr_srs_api.h.
2941
 *
2942
 * @return OGRERR_NONE on success.
2943
 */
2944
2945
OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2946
                                           double dfInMeters)
2947
2948
0
{
2949
0
    return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2950
0
}
2951
2952
/************************************************************************/
2953
/*                         OSRSetLinearUnits()                          */
2954
/************************************************************************/
2955
2956
/**
2957
 * \brief Set the linear units for the projection.
2958
 *
2959
 * This function is the same as OGRSpatialReference::SetLinearUnits()
2960
 */
2961
OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2962
                         double dfInMeters)
2963
2964
0
{
2965
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2966
2967
0
    return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2968
0
}
2969
2970
/************************************************************************/
2971
/*                        SetTargetLinearUnits()                        */
2972
/************************************************************************/
2973
2974
/**
2975
 * \brief Set the linear units for the projection.
2976
 *
2977
 * This method creates a UNIT subnode with the specified values as a
2978
 * child of the target node.
2979
 *
2980
 * This method does the same as the C function OSRSetTargetLinearUnits().
2981
 *
2982
 * @param pszTargetKey the keyword to set the linear units for.
2983
 * i.e. "PROJCS" or "VERT_CS"
2984
 *
2985
 * @param pszUnitsName the units name to be used.  Some preferred units
2986
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2987
 * and SRS_UL_US_FOOT.
2988
 *
2989
 * @param dfInMeters the value to multiple by a length in the indicated
2990
 * units to transform to meters.  Some standard conversion factors can
2991
 * be found in ogr_srs_api.h.
2992
 *
2993
 * @param pszUnitAuthority Unit authority name. Or nullptr
2994
 *
2995
 * @param pszUnitCode Unit code. Or nullptr
2996
 *
2997
 * @return OGRERR_NONE on success.
2998
 *
2999
 */
3000
3001
OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
3002
                                                 const char *pszUnitsName,
3003
                                                 double dfInMeters,
3004
                                                 const char *pszUnitAuthority,
3005
                                                 const char *pszUnitCode)
3006
3007
0
{
3008
0
    TAKE_OPTIONAL_LOCK();
3009
3010
0
    if (dfInMeters <= 0.0)
3011
0
        return OGRERR_FAILURE;
3012
3013
0
    d->refreshProjObj();
3014
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3015
0
    if (pszTargetKey == nullptr)
3016
0
    {
3017
0
        if (!d->m_pj_crs)
3018
0
            return OGRERR_FAILURE;
3019
3020
0
        d->demoteFromBoundCRS();
3021
0
        if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3022
0
        {
3023
0
            d->setPjCRS(proj_crs_alter_parameters_linear_unit(
3024
0
                d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3025
0
                pszUnitAuthority, pszUnitCode, false));
3026
0
        }
3027
0
        d->setPjCRS(proj_crs_alter_cs_linear_unit(
3028
0
            d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
3029
0
            pszUnitAuthority, pszUnitCode));
3030
0
        d->undoDemoteFromBoundCRS();
3031
3032
0
        d->m_osLinearUnits = pszUnitsName;
3033
0
        d->dfToMeter = dfInMeters;
3034
3035
0
        return OGRERR_NONE;
3036
0
    }
3037
3038
0
    OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3039
3040
0
    if (poCS == nullptr)
3041
0
        return OGRERR_FAILURE;
3042
3043
0
    char szValue[128] = {'\0'};
3044
0
    if (dfInMeters < std::numeric_limits<int>::max() &&
3045
0
        dfInMeters > std::numeric_limits<int>::min() &&
3046
0
        dfInMeters == static_cast<int>(dfInMeters))
3047
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
3048
0
    else
3049
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
3050
3051
0
    OGR_SRSNode *poUnits = nullptr;
3052
0
    if (poCS->FindChild("UNIT") >= 0)
3053
0
    {
3054
0
        poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
3055
0
        if (poUnits->GetChildCount() < 2)
3056
0
            return OGRERR_FAILURE;
3057
0
        poUnits->GetChild(0)->SetValue(pszUnitsName);
3058
0
        poUnits->GetChild(1)->SetValue(szValue);
3059
0
        if (poUnits->FindChild("AUTHORITY") != -1)
3060
0
            poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
3061
0
    }
3062
0
    else
3063
0
    {
3064
0
        poUnits = new OGR_SRSNode("UNIT");
3065
0
        poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3066
0
        poUnits->AddChild(new OGR_SRSNode(szValue));
3067
3068
0
        poCS->AddChild(poUnits);
3069
0
    }
3070
3071
0
    return OGRERR_NONE;
3072
0
}
3073
3074
/************************************************************************/
3075
/*                         OSRSetLinearUnits()                          */
3076
/************************************************************************/
3077
3078
/**
3079
 * \brief Set the linear units for the target node.
3080
 *
3081
 * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3082
 *
3083
 */
3084
OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3085
                               const char *pszTargetKey, const char *pszUnits,
3086
                               double dfInMeters)
3087
3088
0
{
3089
0
    VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3090
3091
0
    return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3092
0
                                                 dfInMeters);
3093
0
}
3094
3095
/************************************************************************/
3096
/*                           GetLinearUnits()                           */
3097
/************************************************************************/
3098
3099
/**
3100
 * \brief Fetch linear projection units.
3101
 *
3102
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3103
 * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3104
 * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3105
 * axis units will be returned.
3106
 *
3107
 * This method does the same thing as the C function OSRGetLinearUnits()
3108
 *
3109
 * @param ppszName a pointer to be updated with the pointer to the units name.
3110
 * The returned value remains internal to the OGRSpatialReference and should
3111
 * not be freed, or modified.  It may be invalidated on the next
3112
 * OGRSpatialReference call.
3113
 *
3114
 * @return the value to multiply by linear distances to transform them to
3115
 * meters.
3116
 * @deprecated Use GetLinearUnits(const char**) const.
3117
 */
3118
3119
double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3120
3121
0
{
3122
0
    return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3123
0
}
3124
3125
/**
3126
 * \brief Fetch linear projection units.
3127
 *
3128
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3129
 * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3130
 * for units.
3131
 *
3132
 * This method does the same thing as the C function OSRGetLinearUnits()
3133
 *
3134
 * @param ppszName a pointer to be updated with the pointer to the units name.
3135
 * The returned value remains internal to the OGRSpatialReference and should
3136
 * not be freed, or modified.  It may be invalidated on the next
3137
 * OGRSpatialReference call.
3138
 *
3139
 * @return the value to multiply by linear distances to transform them to
3140
 * meters.
3141
 */
3142
3143
double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3144
3145
0
{
3146
0
    return GetTargetLinearUnits(nullptr, ppszName);
3147
0
}
3148
3149
/************************************************************************/
3150
/*                         OSRGetLinearUnits()                          */
3151
/************************************************************************/
3152
3153
/**
3154
 * \brief Fetch linear projection units.
3155
 *
3156
 * This function is the same as OGRSpatialReference::GetLinearUnits()
3157
 */
3158
double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3159
3160
0
{
3161
0
    VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3162
3163
0
    return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3164
0
}
3165
3166
/************************************************************************/
3167
/*                        GetTargetLinearUnits()                        */
3168
/************************************************************************/
3169
3170
/**
3171
 * \brief Fetch linear units for target.
3172
 *
3173
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3174
 *
3175
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3176
 *
3177
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3178
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3179
 * GEOCCS, GEOGCS and VERT_CS are looked up)
3180
 * @param ppszName a pointer to be updated with the pointer to the units name.
3181
 * The returned value remains internal to the OGRSpatialReference and should not
3182
 * be freed, or modified.  It may be invalidated on the next
3183
 * OGRSpatialReference call. ppszName can be set to NULL.
3184
 *
3185
 * @return the value to multiply by linear distances to transform them to
3186
 * meters.
3187
 *
3188
 * @deprecated Use GetTargetLinearUnits(const char*, const char**)
3189
 * const.
3190
 */
3191
3192
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3193
                                                 const char **ppszName) const
3194
3195
0
{
3196
0
    TAKE_OPTIONAL_LOCK();
3197
3198
0
    d->refreshProjObj();
3199
3200
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3201
0
    if (pszTargetKey == nullptr)
3202
0
    {
3203
        // Use cached result if available
3204
0
        if (!d->m_osLinearUnits.empty())
3205
0
        {
3206
0
            if (ppszName)
3207
0
                *ppszName = d->m_osLinearUnits.c_str();
3208
0
            return d->dfToMeter;
3209
0
        }
3210
3211
0
        while (true)
3212
0
        {
3213
0
            if (d->m_pj_crs == nullptr)
3214
0
            {
3215
0
                break;
3216
0
            }
3217
3218
0
            d->demoteFromBoundCRS();
3219
0
            PJ *coordSys = nullptr;
3220
0
            if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3221
0
            {
3222
0
                for (int iComponent = 0; iComponent < 2; iComponent++)
3223
0
                {
3224
0
                    auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3225
0
                                                       d->m_pj_crs, iComponent);
3226
0
                    if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3227
0
                    {
3228
0
                        auto temp =
3229
0
                            proj_get_source_crs(d->getPROJContext(), subCRS);
3230
0
                        proj_destroy(subCRS);
3231
0
                        subCRS = temp;
3232
0
                    }
3233
0
                    if (subCRS &&
3234
0
                        (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3235
0
                         proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3236
0
                         proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3237
0
                    {
3238
0
                        coordSys = proj_crs_get_coordinate_system(
3239
0
                            d->getPROJContext(), subCRS);
3240
0
                        proj_destroy(subCRS);
3241
0
                        break;
3242
0
                    }
3243
0
                    else if (subCRS)
3244
0
                    {
3245
0
                        proj_destroy(subCRS);
3246
0
                    }
3247
0
                }
3248
0
                if (coordSys == nullptr)
3249
0
                {
3250
0
                    d->undoDemoteFromBoundCRS();
3251
0
                    break;
3252
0
                }
3253
0
            }
3254
0
            else
3255
0
            {
3256
0
                coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3257
0
                                                          d->m_pj_crs);
3258
0
            }
3259
3260
0
            d->undoDemoteFromBoundCRS();
3261
0
            if (!coordSys)
3262
0
            {
3263
0
                break;
3264
0
            }
3265
0
            auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3266
3267
0
            if (csType != PJ_CS_TYPE_CARTESIAN &&
3268
0
                csType != PJ_CS_TYPE_VERTICAL &&
3269
0
                csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3270
0
                csType != PJ_CS_TYPE_SPHERICAL)
3271
0
            {
3272
0
                proj_destroy(coordSys);
3273
0
                break;
3274
0
            }
3275
3276
0
            int axis = 0;
3277
3278
0
            if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3279
0
                csType == PJ_CS_TYPE_SPHERICAL)
3280
0
            {
3281
0
                const int axisCount =
3282
0
                    proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3283
3284
0
                if (axisCount == 3)
3285
0
                {
3286
0
                    axis = 2;
3287
0
                }
3288
0
                else
3289
0
                {
3290
0
                    proj_destroy(coordSys);
3291
0
                    break;
3292
0
                }
3293
0
            }
3294
3295
0
            double dfConvFactor = 0.0;
3296
0
            const char *pszUnitName = nullptr;
3297
0
            if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3298
0
                                       nullptr, nullptr, nullptr, &dfConvFactor,
3299
0
                                       &pszUnitName, nullptr, nullptr))
3300
0
            {
3301
0
                proj_destroy(coordSys);
3302
0
                break;
3303
0
            }
3304
3305
0
            d->m_osLinearUnits = pszUnitName;
3306
0
            d->dfToMeter = dfConvFactor;
3307
0
            if (ppszName)
3308
0
                *ppszName = d->m_osLinearUnits.c_str();
3309
3310
0
            proj_destroy(coordSys);
3311
0
            return dfConvFactor;
3312
0
        }
3313
3314
0
        d->m_osLinearUnits = "unknown";
3315
0
        d->dfToMeter = 1.0;
3316
3317
0
        if (ppszName != nullptr)
3318
0
            *ppszName = d->m_osLinearUnits.c_str();
3319
0
        return 1.0;
3320
0
    }
3321
3322
0
    const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3323
3324
0
    if (ppszName != nullptr)
3325
0
        *ppszName = "unknown";
3326
3327
0
    if (poCS == nullptr)
3328
0
        return 1.0;
3329
3330
0
    for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3331
0
    {
3332
0
        const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3333
3334
0
        if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3335
0
        {
3336
0
            if (ppszName != nullptr)
3337
0
                *ppszName = poChild->GetChild(0)->GetValue();
3338
3339
0
            return CPLAtof(poChild->GetChild(1)->GetValue());
3340
0
        }
3341
0
    }
3342
3343
0
    return 1.0;
3344
0
}
3345
3346
/**
3347
 * \brief Fetch linear units for target.
3348
 *
3349
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3350
 *
3351
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3352
 *
3353
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3354
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3355
 * GEOCCS and VERT_CS are looked up)
3356
 * @param ppszName a pointer to be updated with the pointer to the units name.
3357
 * The returned value remains internal to the OGRSpatialReference and should not
3358
 * be freed, or modified.  It may be invalidated on the next
3359
 * OGRSpatialReference call. ppszName can be set to NULL.
3360
 *
3361
 * @return the value to multiply by linear distances to transform them to
3362
 * meters.
3363
 *
3364
 */
3365
3366
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3367
                                                 char **ppszName) const
3368
3369
0
{
3370
0
    return GetTargetLinearUnits(pszTargetKey,
3371
0
                                const_cast<const char **>(ppszName));
3372
0
}
3373
3374
/************************************************************************/
3375
/*                      OSRGetTargetLinearUnits()                       */
3376
/************************************************************************/
3377
3378
/**
3379
 * \brief Fetch linear projection units.
3380
 *
3381
 * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3382
 *
3383
 */
3384
double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3385
                               const char *pszTargetKey, char **ppszName)
3386
3387
0
{
3388
0
    VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3389
3390
0
    return ToPointer(hSRS)->GetTargetLinearUnits(
3391
0
        pszTargetKey, const_cast<const char **>(ppszName));
3392
0
}
3393
3394
/************************************************************************/
3395
/*                          GetPrimeMeridian()                          */
3396
/************************************************************************/
3397
3398
/**
3399
 * \brief Fetch prime meridian info.
3400
 *
3401
 * Returns the offset of the prime meridian from greenwich in degrees,
3402
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3403
 * in the coordinate system definition a value of "Greenwich" and an
3404
 * offset of 0.0 is assumed.
3405
 *
3406
 * If the prime meridian name is returned, the pointer is to an internal
3407
 * copy of the name. It should not be freed, altered or depended on after
3408
 * the next OGR call.
3409
 *
3410
 * This method is the same as the C function OSRGetPrimeMeridian().
3411
 *
3412
 * @param ppszName return location for prime meridian name.  If NULL, name
3413
 * is not returned.
3414
 *
3415
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3416
 * degrees.
3417
 * @deprecated Use GetPrimeMeridian(const char**) const.
3418
 */
3419
3420
double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3421
3422
0
{
3423
0
    TAKE_OPTIONAL_LOCK();
3424
3425
0
    d->refreshProjObj();
3426
3427
0
    if (!d->m_osPrimeMeridianName.empty())
3428
0
    {
3429
0
        if (ppszName != nullptr)
3430
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3431
0
        return d->dfFromGreenwich;
3432
0
    }
3433
3434
0
    while (true)
3435
0
    {
3436
0
        if (!d->m_pj_crs)
3437
0
            break;
3438
3439
0
        auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3440
0
        if (!pm)
3441
0
            break;
3442
3443
0
        d->m_osPrimeMeridianName = proj_get_name(pm);
3444
0
        if (ppszName)
3445
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3446
0
        double dfLongitude = 0.0;
3447
0
        double dfConvFactor = 0.0;
3448
0
        proj_prime_meridian_get_parameters(
3449
0
            d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3450
0
        proj_destroy(pm);
3451
0
        d->dfFromGreenwich =
3452
0
            dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3453
0
        return d->dfFromGreenwich;
3454
0
    }
3455
3456
0
    d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3457
0
    d->dfFromGreenwich = 0.0;
3458
0
    if (ppszName != nullptr)
3459
0
        *ppszName = d->m_osPrimeMeridianName.c_str();
3460
0
    return d->dfFromGreenwich;
3461
0
}
3462
3463
/**
3464
 * \brief Fetch prime meridian info.
3465
 *
3466
 * Returns the offset of the prime meridian from greenwich in degrees,
3467
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3468
 * in the coordinate system definition a value of "Greenwich" and an
3469
 * offset of 0.0 is assumed.
3470
 *
3471
 * If the prime meridian name is returned, the pointer is to an internal
3472
 * copy of the name. It should not be freed, altered or depended on after
3473
 * the next OGR call.
3474
 *
3475
 * This method is the same as the C function OSRGetPrimeMeridian().
3476
 *
3477
 * @param ppszName return location for prime meridian name.  If NULL, name
3478
 * is not returned.
3479
 *
3480
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3481
 * degrees.
3482
 */
3483
3484
double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3485
3486
0
{
3487
0
    return GetPrimeMeridian(const_cast<const char **>(ppszName));
3488
0
}
3489
3490
/************************************************************************/
3491
/*                        OSRGetPrimeMeridian()                         */
3492
/************************************************************************/
3493
3494
/**
3495
 * \brief Fetch prime meridian info.
3496
 *
3497
 * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3498
 */
3499
double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3500
3501
0
{
3502
0
    VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3503
3504
0
    return ToPointer(hSRS)->GetPrimeMeridian(
3505
0
        const_cast<const char **>(ppszName));
3506
0
}
3507
3508
/************************************************************************/
3509
/*                             SetGeogCS()                              */
3510
/************************************************************************/
3511
3512
/**
3513
 * \brief Set geographic coordinate system.
3514
 *
3515
 * This method is used to set the datum, ellipsoid, prime meridian and
3516
 * angular units for a geographic coordinate system.  It can be used on its
3517
 * own to establish a geographic spatial reference, or applied to a
3518
 * projected coordinate system to establish the underlying geographic
3519
 * coordinate system.
3520
 *
3521
 * This method does the same as the C function OSRSetGeogCS().
3522
 *
3523
 * @param pszGeogName user visible name for the geographic coordinate system
3524
 * (not to serve as a key).
3525
 *
3526
 * @param pszDatumName key name for this datum.  The OpenGIS specification
3527
 * lists some known values, and otherwise EPSG datum names with a standard
3528
 * transformation are considered legal keys.
3529
 *
3530
 * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3531
 *
3532
 * @param dfSemiMajor the semi major axis of the spheroid.
3533
 *
3534
 * @param dfInvFlattening the inverse flattening for the spheroid.
3535
 * This can be computed from the semi minor axis as
3536
 * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3537
 *
3538
 * @param pszPMName the name of the prime meridian (not to serve as a key)
3539
 * If this is NULL a default value of "Greenwich" will be used.
3540
 *
3541
 * @param dfPMOffset the longitude of Greenwich relative to this prime
3542
 * meridian. Always in Degrees
3543
 *
3544
 * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3545
 * standard names).  If NULL a value of "degrees" will be assumed.
3546
 *
3547
 * @param dfConvertToRadians value to multiply angular units by to transform
3548
 * them to radians.  A value of SRS_UA_DEGREE_CONV will be used if
3549
 * pszAngularUnits is NULL.
3550
 *
3551
 * @return OGRERR_NONE on success.
3552
 */
3553
3554
OGRErr OGRSpatialReference::SetGeogCS(
3555
    const char *pszGeogName, const char *pszDatumName,
3556
    const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3557
    const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3558
    double dfConvertToRadians)
3559
3560
0
{
3561
0
    TAKE_OPTIONAL_LOCK();
3562
3563
0
    d->bNormInfoSet = FALSE;
3564
0
    d->m_osAngularUnits.clear();
3565
0
    d->m_dfAngularUnitToRadian = 0.0;
3566
0
    d->m_osPrimeMeridianName.clear();
3567
0
    d->dfFromGreenwich = 0.0;
3568
3569
    /* -------------------------------------------------------------------- */
3570
    /*      For a geocentric coordinate system we want to set the datum     */
3571
    /*      and ellipsoid based on the GEOGCS.  Create the GEOGCS in a      */
3572
    /*      temporary srs and use the copy method which has special         */
3573
    /*      handling for GEOCCS.                                            */
3574
    /* -------------------------------------------------------------------- */
3575
0
    if (IsGeocentric())
3576
0
    {
3577
0
        OGRSpatialReference oGCS;
3578
3579
0
        oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3580
0
                       dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3581
0
                       dfConvertToRadians);
3582
0
        return CopyGeogCSFrom(&oGCS);
3583
0
    }
3584
3585
0
    auto cs = proj_create_ellipsoidal_2D_cs(
3586
0
        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3587
0
        dfConvertToRadians);
3588
    // Prime meridian expressed in Degree
3589
0
    auto obj = proj_create_geographic_crs(
3590
0
        d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3591
0
        dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3592
0
    proj_destroy(cs);
3593
3594
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3595
0
        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3596
0
    {
3597
0
        d->setPjCRS(obj);
3598
0
    }
3599
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3600
0
    {
3601
0
        d->setPjCRS(
3602
0
            proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3603
0
        proj_destroy(obj);
3604
0
    }
3605
0
    else
3606
0
    {
3607
0
        proj_destroy(obj);
3608
0
    }
3609
3610
0
    return OGRERR_NONE;
3611
0
}
3612
3613
/************************************************************************/
3614
/*                            OSRSetGeogCS()                            */
3615
/************************************************************************/
3616
3617
/**
3618
 * \brief Set geographic coordinate system.
3619
 *
3620
 * This function is the same as OGRSpatialReference::SetGeogCS()
3621
 */
3622
OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3623
                    const char *pszDatumName, const char *pszSpheroidName,
3624
                    double dfSemiMajor, double dfInvFlattening,
3625
                    const char *pszPMName, double dfPMOffset,
3626
                    const char *pszAngularUnits, double dfConvertToRadians)
3627
3628
0
{
3629
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3630
3631
0
    return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3632
0
                                      pszSpheroidName, dfSemiMajor,
3633
0
                                      dfInvFlattening, pszPMName, dfPMOffset,
3634
0
                                      pszAngularUnits, dfConvertToRadians);
3635
0
}
3636
3637
/************************************************************************/
3638
/*                         SetWellKnownGeogCS()                         */
3639
/************************************************************************/
3640
3641
/**
3642
 * \brief Set a GeogCS based on well known name.
3643
 *
3644
 * This may be called on an empty OGRSpatialReference to make a geographic
3645
 * coordinate system, or on something with an existing PROJCS node to
3646
 * set the underlying geographic coordinate system of a projected coordinate
3647
 * system.
3648
 *
3649
 * The following well known text values are currently supported,
3650
 * Except for "EPSG:n", the others are without dependency on EPSG data files:
3651
 * <ul>
3652
 * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3653
 * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3654
 * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3655
 * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3656
 * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3657
 * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3658
 * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3659
 * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3660
 * </ul>
3661
 *
3662
 * @param pszName name of well known geographic coordinate system.
3663
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3664
 * recognised, the target object is already initialized, or an EPSG value
3665
 * can't be successfully looked up.
3666
 */
3667
3668
OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3669
3670
0
{
3671
0
    TAKE_OPTIONAL_LOCK();
3672
3673
    /* -------------------------------------------------------------------- */
3674
    /*      Check for EPSG authority numbers.                               */
3675
    /* -------------------------------------------------------------------- */
3676
0
    if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3677
0
    {
3678
0
        OGRSpatialReference oSRS2;
3679
0
        const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3680
0
        if (eErr != OGRERR_NONE)
3681
0
            return eErr;
3682
3683
0
        if (!oSRS2.IsGeographic())
3684
0
            return OGRERR_FAILURE;
3685
3686
0
        return CopyGeogCSFrom(&oSRS2);
3687
0
    }
3688
3689
    /* -------------------------------------------------------------------- */
3690
    /*      Check for simple names.                                         */
3691
    /* -------------------------------------------------------------------- */
3692
0
    const char *pszWKT = nullptr;
3693
3694
0
    if (EQUAL(pszName, "WGS84"))
3695
0
    {
3696
0
        pszWKT = SRS_WKT_WGS84_LAT_LONG;
3697
0
    }
3698
0
    else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3699
0
    {
3700
0
        pszWKT =
3701
0
            "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3702
0
            "ELLIPSOID[\"WGS "
3703
0
            "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3704
0
            "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3705
0
            "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3706
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3707
0
            "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3708
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3709
0
            "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3710
0
            "ID[\"OGC\",\"CRS84\"]]";
3711
0
    }
3712
0
    else if (EQUAL(pszName, "WGS72"))
3713
0
        pszWKT =
3714
0
            "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3715
0
            "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3716
0
            "AUTHORITY[\"EPSG\",\"6322\"]],"
3717
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3718
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3719
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3720
0
            "AUTHORITY[\"EPSG\",\"4322\"]]";
3721
3722
0
    else if (EQUAL(pszName, "NAD27"))
3723
0
        pszWKT =
3724
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3725
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3726
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3727
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3728
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3729
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3730
0
            "AUTHORITY[\"EPSG\",\"4267\"]]";
3731
3732
0
    else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3733
0
        pszWKT =
3734
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3735
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3736
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3737
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3738
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3739
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3740
3741
0
    else if (EQUAL(pszName, "NAD83"))
3742
0
        pszWKT =
3743
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3744
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3745
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3746
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3747
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3748
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3749
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3750
0
            "\"EPSG\",\"4269\"]]";
3751
3752
0
    else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
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[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3761
3762
0
    else
3763
0
        return OGRERR_FAILURE;
3764
3765
    /* -------------------------------------------------------------------- */
3766
    /*      Import the WKT                                                  */
3767
    /* -------------------------------------------------------------------- */
3768
0
    OGRSpatialReference oSRS2;
3769
0
    const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3770
0
    if (eErr != OGRERR_NONE)
3771
0
        return eErr;
3772
3773
    /* -------------------------------------------------------------------- */
3774
    /*      Copy over.                                                      */
3775
    /* -------------------------------------------------------------------- */
3776
0
    return CopyGeogCSFrom(&oSRS2);
3777
0
}
3778
3779
/************************************************************************/
3780
/*                       OSRSetWellKnownGeogCS()                        */
3781
/************************************************************************/
3782
3783
/**
3784
 * \brief Set a GeogCS based on well known name.
3785
 *
3786
 * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3787
 */
3788
OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3789
3790
0
{
3791
0
    VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3792
3793
0
    return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3794
0
}
3795
3796
/************************************************************************/
3797
/*                           CopyGeogCSFrom()                           */
3798
/************************************************************************/
3799
3800
/**
3801
 * \brief Copy GEOGCS from another OGRSpatialReference.
3802
 *
3803
 * The GEOGCS information is copied into this OGRSpatialReference from another.
3804
 * If this object has a PROJCS root already, the GEOGCS is installed within
3805
 * it, otherwise it is installed as the root.
3806
 *
3807
 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3808
 *
3809
 * @return OGRERR_NONE on success or an error code.
3810
 */
3811
3812
OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3813
3814
0
{
3815
0
    TAKE_OPTIONAL_LOCK();
3816
3817
0
    d->bNormInfoSet = FALSE;
3818
0
    d->m_osAngularUnits.clear();
3819
0
    d->m_dfAngularUnitToRadian = 0.0;
3820
0
    d->m_osPrimeMeridianName.clear();
3821
0
    d->dfFromGreenwich = 0.0;
3822
3823
0
    d->refreshProjObj();
3824
0
    poSrcSRS->d->refreshProjObj();
3825
0
    if (!poSrcSRS->d->m_pj_crs)
3826
0
    {
3827
0
        return OGRERR_FAILURE;
3828
0
    }
3829
0
    auto geodCRS =
3830
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3831
0
    if (!geodCRS)
3832
0
    {
3833
0
        return OGRERR_FAILURE;
3834
0
    }
3835
3836
    /* -------------------------------------------------------------------- */
3837
    /*      Handle geocentric coordinate systems specially.  We just        */
3838
    /*      want to copy the DATUM.                                         */
3839
    /* -------------------------------------------------------------------- */
3840
0
    if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3841
0
    {
3842
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3843
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
3844
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3845
0
        if (datum == nullptr)
3846
0
        {
3847
0
            datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3848
0
        }
3849
0
#endif
3850
0
        if (datum == nullptr)
3851
0
        {
3852
0
            proj_destroy(geodCRS);
3853
0
            return OGRERR_FAILURE;
3854
0
        }
3855
3856
0
        const char *pszUnitName = nullptr;
3857
0
        double unitConvFactor = GetLinearUnits(&pszUnitName);
3858
3859
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
3860
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3861
0
            unitConvFactor);
3862
0
        proj_destroy(datum);
3863
3864
0
        d->setPjCRS(pj_crs);
3865
0
    }
3866
3867
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3868
0
    {
3869
0
        auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3870
0
                                                  d->m_pj_crs, geodCRS);
3871
0
        d->setPjCRS(pj_crs);
3872
0
    }
3873
3874
0
    else
3875
0
    {
3876
0
        d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3877
0
    }
3878
3879
    // Apply TOWGS84 of source CRS
3880
0
    if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3881
0
    {
3882
0
        auto target =
3883
0
            proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3884
0
        auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3885
0
                                              poSrcSRS->d->m_pj_crs);
3886
0
        d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3887
0
                                              target, co));
3888
0
        proj_destroy(target);
3889
0
        proj_destroy(co);
3890
0
    }
3891
3892
0
    proj_destroy(geodCRS);
3893
3894
0
    return OGRERR_NONE;
3895
0
}
3896
3897
/************************************************************************/
3898
/*                         OSRCopyGeogCSFrom()                          */
3899
/************************************************************************/
3900
3901
/**
3902
 * \brief Copy GEOGCS from another OGRSpatialReference.
3903
 *
3904
 * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3905
 */
3906
OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3907
                         const OGRSpatialReferenceH hSrcSRS)
3908
3909
0
{
3910
0
    VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3911
0
    VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3912
3913
0
    return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3914
0
}
3915
3916
/************************************************************************/
3917
/*                   SET_FROM_USER_INPUT_LIMITATIONS_get()              */
3918
/************************************************************************/
3919
3920
/** Limitations for OGRSpatialReference::SetFromUserInput().
3921
 *
3922
 * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3923
 */
3924
const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3925
    "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3926
3927
/**
3928
 * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3929
 */
3930
CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3931
0
{
3932
0
    return SET_FROM_USER_INPUT_LIMITATIONS;
3933
0
}
3934
3935
/************************************************************************/
3936
/*                      RemoveIDFromMemberOfEnsembles()                 */
3937
/************************************************************************/
3938
3939
// cppcheck-suppress constParameterReference
3940
static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3941
0
{
3942
    // Remove "id" from members of datum ensembles for compatibility with
3943
    // older PROJ versions
3944
    // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3945
    // and https://github.com/OSGeo/PROJ/pull/3221
3946
0
    if (obj.GetType() == CPLJSONObject::Type::Object)
3947
0
    {
3948
0
        for (auto &subObj : obj.GetChildren())
3949
0
        {
3950
0
            RemoveIDFromMemberOfEnsembles(subObj);
3951
0
        }
3952
0
    }
3953
0
    else if (obj.GetType() == CPLJSONObject::Type::Array &&
3954
0
             obj.GetName() == "members")
3955
0
    {
3956
0
        for (auto &subObj : obj.ToArray())
3957
0
        {
3958
0
            if (subObj.GetType() == CPLJSONObject::Type::Object)
3959
0
            {
3960
0
                subObj.Delete("id");
3961
0
            }
3962
0
        }
3963
0
    }
3964
0
}
3965
3966
/************************************************************************/
3967
/*                          SetFromUserInput()                          */
3968
/************************************************************************/
3969
3970
/**
3971
 * \brief Set spatial reference from various text formats.
3972
 *
3973
 * This method will examine the provided input, and try to deduce the
3974
 * format, and then use it to initialize the spatial reference system.  It
3975
 * may take the following forms:
3976
 *
3977
 * <ol>
3978
 * <li> Well Known Text definition - passed on to importFromWkt().
3979
 * <li> "EPSG:n" - number passed on to importFromEPSG().
3980
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3981
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3982
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3983
 * <li> PROJ.4 definitions - passed on to importFromProj4().
3984
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3985
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3986
 * WGS84 or WGS72.
3987
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3988
 * <li> PROJJSON (PROJ &gt;= 6.2)
3989
 * </ol>
3990
 *
3991
 * It is expected that this method will be extended in the future to support
3992
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3993
 * State Plane definitions.
3994
 *
3995
 * This method is intended to be flexible, but by its nature it is
3996
 * imprecise as it must guess information about the format intended.  When
3997
 * possible applications should call the specific method appropriate if the
3998
 * input is known to be in a particular format.
3999
 *
4000
 * This method does the same thing as the OSRSetFromUserInput() function.
4001
 *
4002
 * @param pszDefinition text definition to try to deduce SRS from.
4003
 *
4004
 * @return OGRERR_NONE on success, or an error code if the name isn't
4005
 * recognised, the definition is corrupt, or an EPSG value can't be
4006
 * successfully looked up.
4007
 */
4008
4009
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
4010
0
{
4011
0
    return SetFromUserInput(pszDefinition, nullptr);
4012
0
}
4013
4014
/**
4015
 * \brief Set spatial reference from various text formats.
4016
 *
4017
 * This method will examine the provided input, and try to deduce the
4018
 * format, and then use it to initialize the spatial reference system.  It
4019
 * may take the following forms:
4020
 *
4021
 * <ol>
4022
 * <li> Well Known Text definition - passed on to importFromWkt().
4023
 * <li> "EPSG:n" - number passed on to importFromEPSG().
4024
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
4025
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
4026
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
4027
 * <li> PROJ.4 definitions - passed on to importFromProj4().
4028
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
4029
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
4030
 * WGS84 or WGS72.
4031
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
4032
 * <li> PROJJSON (PROJ &gt;= 6.2)
4033
 * </ol>
4034
 *
4035
 * It is expected that this method will be extended in the future to support
4036
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
4037
 * State Plane definitions.
4038
 *
4039
 * This method is intended to be flexible, but by its nature it is
4040
 * imprecise as it must guess information about the format intended.  When
4041
 * possible applications should call the specific method appropriate if the
4042
 * input is known to be in a particular format.
4043
 *
4044
 * This method does the same thing as the OSRSetFromUserInput() and
4045
 * OSRSetFromUserInputEx() functions.
4046
 *
4047
 * @param pszDefinition text definition to try to deduce SRS from.
4048
 *
4049
 * @param papszOptions NULL terminated list of options, or NULL.
4050
 * <ol>
4051
 * <li> ALLOW_NETWORK_ACCESS=YES/NO.
4052
 *      Whether http:// or https:// access is allowed. Defaults to YES.
4053
 * <li> ALLOW_FILE_ACCESS=YES/NO.
4054
 *      Whether reading a file using the Virtual File System layer is allowed
4055
 *      (can also involve network access). Defaults to YES.
4056
 * </ol>
4057
 *
4058
 * @return OGRERR_NONE on success, or an error code if the name isn't
4059
 * recognised, the definition is corrupt, or an EPSG value can't be
4060
 * successfully looked up.
4061
 */
4062
4063
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4064
                                             CSLConstList papszOptions)
4065
0
{
4066
0
    TAKE_OPTIONAL_LOCK();
4067
4068
    // Skip leading white space
4069
0
    while (isspace(static_cast<unsigned char>(*pszDefinition)))
4070
0
        pszDefinition++;
4071
4072
0
    if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4073
0
    {
4074
0
        pszDefinition += 6;
4075
0
    }
4076
4077
    /* -------------------------------------------------------------------- */
4078
    /*      Is it a recognised syntax?                                      */
4079
    /* -------------------------------------------------------------------- */
4080
0
    const char *const wktKeywords[] = {
4081
        // WKT1
4082
0
        "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4083
        // WKT2"
4084
0
        "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4085
0
        "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4086
0
        "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4087
0
    for (const char *keyword : wktKeywords)
4088
0
    {
4089
0
        if (STARTS_WITH_CI(pszDefinition, keyword))
4090
0
        {
4091
0
            return importFromWkt(pszDefinition);
4092
0
        }
4093
0
    }
4094
4095
0
    const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4096
0
    if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4097
0
    {
4098
0
        OGRErr eStatus = OGRERR_NONE;
4099
4100
0
        if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4101
0
        {
4102
            // Use proj_create() as it allows things like EPSG:3157+4617
4103
            // that are not normally supported by the below code that
4104
            // builds manually a compound CRS
4105
0
            PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4106
0
            if (!pj)
4107
0
            {
4108
0
                return OGRERR_FAILURE;
4109
0
            }
4110
0
            Clear();
4111
0
            d->setPjCRS(pj);
4112
0
            return OGRERR_NONE;
4113
0
        }
4114
0
        else
4115
0
        {
4116
0
            eStatus =
4117
0
                importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4118
0
        }
4119
4120
0
        return eStatus;
4121
0
    }
4122
4123
0
    if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4124
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4125
0
        STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4126
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4127
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4128
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4129
0
        return importFromURN(pszDefinition);
4130
4131
0
    if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4132
0
        STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4133
0
        STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4134
0
        STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4135
0
        STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4136
0
        return importFromCRSURL(pszDefinition);
4137
4138
0
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4139
0
        return importFromWMSAUTO(pszDefinition);
4140
4141
    // WMS/WCS OGC codes like OGC:CRS84.
4142
0
    if (EQUAL(pszDefinition, "OGC:CRS84"))
4143
0
        return SetWellKnownGeogCS(pszDefinition + 4);
4144
4145
0
    if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4146
0
        return SetWellKnownGeogCS(pszDefinition);
4147
4148
0
    if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4149
0
    {
4150
0
        char *pszFile = CPLStrdup(pszDefinition + 5);
4151
0
        char *pszCode = strstr(pszFile, ",") + 1;
4152
4153
0
        pszCode[-1] = '\0';
4154
4155
0
        OGRErr err = importFromDict(pszFile, pszCode);
4156
0
        CPLFree(pszFile);
4157
4158
0
        return err;
4159
0
    }
4160
4161
0
    if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4162
0
        EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4163
0
    {
4164
0
        Clear();
4165
0
        return SetWellKnownGeogCS(pszDefinition);
4166
0
    }
4167
4168
    // PROJJSON
4169
0
    if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4170
0
        (strstr(pszDefinition, "GeodeticCRS") ||
4171
0
         strstr(pszDefinition, "GeographicCRS") ||
4172
0
         strstr(pszDefinition, "ProjectedCRS") ||
4173
0
         strstr(pszDefinition, "VerticalCRS") ||
4174
0
         strstr(pszDefinition, "BoundCRS") ||
4175
0
         strstr(pszDefinition, "CompoundCRS") ||
4176
0
         strstr(pszDefinition, "DerivedGeodeticCRS") ||
4177
0
         strstr(pszDefinition, "DerivedGeographicCRS") ||
4178
0
         strstr(pszDefinition, "DerivedProjectedCRS") ||
4179
0
         strstr(pszDefinition, "DerivedVerticalCRS") ||
4180
0
         strstr(pszDefinition, "EngineeringCRS") ||
4181
0
         strstr(pszDefinition, "DerivedEngineeringCRS") ||
4182
0
         strstr(pszDefinition, "ParametricCRS") ||
4183
0
         strstr(pszDefinition, "DerivedParametricCRS") ||
4184
0
         strstr(pszDefinition, "TemporalCRS") ||
4185
0
         strstr(pszDefinition, "DerivedTemporalCRS")))
4186
0
    {
4187
0
        PJ *pj;
4188
0
        if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4189
0
        {
4190
            // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4191
            // a unknown id.
4192
0
            CPLJSONDocument oCRSDoc;
4193
0
            if (!oCRSDoc.LoadMemory(pszDefinition))
4194
0
                return OGRERR_CORRUPT_DATA;
4195
0
            CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4196
0
            RemoveIDFromMemberOfEnsembles(oCRSRoot);
4197
0
            pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4198
0
        }
4199
0
        else
4200
0
        {
4201
0
            pj = proj_create(d->getPROJContext(), pszDefinition);
4202
0
        }
4203
0
        if (!pj)
4204
0
        {
4205
0
            return OGRERR_FAILURE;
4206
0
        }
4207
0
        Clear();
4208
0
        d->setPjCRS(pj);
4209
0
        return OGRERR_NONE;
4210
0
    }
4211
4212
0
    if (strstr(pszDefinition, "+proj") != nullptr ||
4213
0
        strstr(pszDefinition, "+init") != nullptr)
4214
0
        return importFromProj4(pszDefinition);
4215
4216
0
    if (STARTS_WITH_CI(pszDefinition, "http://") ||
4217
0
        STARTS_WITH_CI(pszDefinition, "https://"))
4218
0
    {
4219
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4220
0
                                             "ALLOW_NETWORK_ACCESS", "YES")))
4221
0
            return importFromUrl(pszDefinition);
4222
4223
0
        CPLError(CE_Failure, CPLE_AppDefined,
4224
0
                 "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4225
0
                 pszDefinition);
4226
0
        return OGRERR_FAILURE;
4227
0
    }
4228
4229
0
    if (EQUAL(pszDefinition, "osgb:BNG"))
4230
0
    {
4231
0
        return importFromEPSG(27700);
4232
0
    }
4233
4234
    // Used by German CityGML files
4235
0
    if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4236
0
    {
4237
        // "ETRS89 / UTM Zone 32N + DHHN92 height"
4238
0
        return SetFromUserInput("EPSG:25832+5783");
4239
0
    }
4240
0
    else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4241
0
    {
4242
        // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4243
0
        return SetFromUserInput("EPSG:25832+7837");
4244
0
    }
4245
4246
    // Used by  Japan's Fundamental Geospatial Data (FGD) GML
4247
0
    if (EQUAL(pszDefinition, "fguuid:jgd2001.bl"))
4248
0
        return importFromEPSG(4612);  // JGD2000 (slight difference in years)
4249
0
    else if (EQUAL(pszDefinition, "fguuid:jgd2011.bl"))
4250
0
        return importFromEPSG(6668);  // JGD2011
4251
0
    else if (EQUAL(pszDefinition, "fguuid:jgd2024.bl"))
4252
0
    {
4253
        // FIXME when EPSG attributes a CRS code
4254
0
        return importFromWkt(
4255
0
            "GEOGCRS[\"JGD2024\",\n"
4256
0
            "    DATUM[\"Japanese Geodetic Datum 2024\",\n"
4257
0
            "        ELLIPSOID[\"GRS 1980\",6378137,298.257222101,\n"
4258
0
            "            LENGTHUNIT[\"metre\",1]]],\n"
4259
0
            "    PRIMEM[\"Greenwich\",0,\n"
4260
0
            "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4261
0
            "    CS[ellipsoidal,2],\n"
4262
0
            "        AXIS[\"geodetic latitude (Lat)\",north,\n"
4263
0
            "            ORDER[1],\n"
4264
0
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4265
0
            "        AXIS[\"geodetic longitude (Lon)\",east,\n"
4266
0
            "            ORDER[2],\n"
4267
0
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4268
0
            "    USAGE[\n"
4269
0
            "        SCOPE[\"Horizontal component of 3D system.\"],\n"
4270
0
            "        AREA[\"Japan - onshore and offshore.\"],\n"
4271
0
            "        BBOX[17.09,122.38,46.05,157.65]]]");
4272
0
    }
4273
4274
    // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4275
0
    const char *pszDot = strrchr(pszDefinition, ':');
4276
0
    if (pszDot)
4277
0
    {
4278
0
        CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4279
0
        auto authorities =
4280
0
            proj_get_authorities_from_database(d->getPROJContext());
4281
0
        if (authorities)
4282
0
        {
4283
0
            std::set<std::string> aosCandidateAuthorities;
4284
0
            for (auto iter = authorities; *iter; ++iter)
4285
0
            {
4286
0
                if (*iter == osPrefix)
4287
0
                {
4288
0
                    aosCandidateAuthorities.clear();
4289
0
                    aosCandidateAuthorities.insert(*iter);
4290
0
                    break;
4291
0
                }
4292
                // Deal with "IAU_2015" as authority in the list and input
4293
                // "IAU:code"
4294
0
                else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4295
0
                             0 &&
4296
0
                         (*iter)[osPrefix.size()] == '_')
4297
0
                {
4298
0
                    aosCandidateAuthorities.insert(*iter);
4299
0
                }
4300
                // Deal with "IAU_2015" as authority in the list and input
4301
                // "IAU:2015:code"
4302
0
                else if (osPrefix.find(':') != std::string::npos &&
4303
0
                         osPrefix.size() == strlen(*iter) &&
4304
0
                         CPLString(osPrefix).replaceAll(':', '_') == *iter)
4305
0
                {
4306
0
                    aosCandidateAuthorities.clear();
4307
0
                    aosCandidateAuthorities.insert(*iter);
4308
0
                    break;
4309
0
                }
4310
0
            }
4311
4312
0
            proj_string_list_destroy(authorities);
4313
4314
0
            if (!aosCandidateAuthorities.empty())
4315
0
            {
4316
0
                auto obj = proj_create_from_database(
4317
0
                    d->getPROJContext(),
4318
0
                    aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4319
0
                    PJ_CATEGORY_CRS, false, nullptr);
4320
0
                if (!obj)
4321
0
                {
4322
0
                    return OGRERR_FAILURE;
4323
0
                }
4324
0
                Clear();
4325
0
                d->setPjCRS(obj);
4326
0
                return OGRERR_NONE;
4327
0
            }
4328
0
        }
4329
0
    }
4330
4331
    /* -------------------------------------------------------------------- */
4332
    /*      Try to open it as a file.                                       */
4333
    /* -------------------------------------------------------------------- */
4334
0
    if (!CPLTestBool(
4335
0
            CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4336
0
    {
4337
0
        VSIStatBufL sStat;
4338
0
        if (STARTS_WITH(pszDefinition, "/vsi") ||
4339
0
            VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4340
0
        {
4341
0
            CPLError(CE_Failure, CPLE_AppDefined,
4342
0
                     "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4343
0
                     pszDefinition);
4344
0
            return OGRERR_FAILURE;
4345
0
        }
4346
        // We used to silently return an error without a CE_Failure message
4347
        // Cf https://github.com/Toblerity/Fiona/issues/1063
4348
0
        return OGRERR_CORRUPT_DATA;
4349
0
    }
4350
4351
0
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4352
0
    VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4353
0
    if (fp == nullptr)
4354
0
        return OGRERR_CORRUPT_DATA;
4355
4356
0
    const size_t nBufMax = 100000;
4357
0
    char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4358
0
    const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4359
0
    VSIFCloseL(fp);
4360
4361
0
    if (nBytes == nBufMax - 1)
4362
0
    {
4363
0
        CPLDebug("OGR",
4364
0
                 "OGRSpatialReference::SetFromUserInput(%s), opened file "
4365
0
                 "but it is to large for our generous buffer.  Is it really "
4366
0
                 "just a WKT definition?",
4367
0
                 pszDefinition);
4368
0
        CPLFree(pszBuffer);
4369
0
        return OGRERR_FAILURE;
4370
0
    }
4371
4372
0
    pszBuffer[nBytes] = '\0';
4373
4374
0
    char *pszBufPtr = pszBuffer;
4375
0
    while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4376
0
        pszBufPtr++;
4377
4378
0
    OGRErr err = OGRERR_NONE;
4379
0
    if (pszBufPtr[0] == '<')
4380
0
        err = importFromXML(pszBufPtr);
4381
0
    else if ((strstr(pszBuffer, "+proj") != nullptr ||
4382
0
              strstr(pszBuffer, "+init") != nullptr) &&
4383
0
             (strstr(pszBuffer, "EXTENSION") == nullptr &&
4384
0
              strstr(pszBuffer, "extension") == nullptr))
4385
0
        err = importFromProj4(pszBufPtr);
4386
0
    else
4387
0
    {
4388
0
        if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4389
0
        {
4390
0
            pszBufPtr += 6;
4391
0
        }
4392
4393
        // coverity[tainted_data]
4394
0
        err = importFromWkt(pszBufPtr);
4395
0
    }
4396
4397
0
    CPLFree(pszBuffer);
4398
4399
0
    return err;
4400
0
}
4401
4402
/************************************************************************/
4403
/*                        OSRSetFromUserInput()                         */
4404
/************************************************************************/
4405
4406
/**
4407
 * \brief Set spatial reference from various text formats.
4408
 *
4409
 * This function is the same as OGRSpatialReference::SetFromUserInput()
4410
 *
4411
 * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4412
 */
4413
OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4414
                                       const char *pszDef)
4415
4416
0
{
4417
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4418
4419
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef);
4420
0
}
4421
4422
/************************************************************************/
4423
/*                       OSRSetFromUserInputEx()                        */
4424
/************************************************************************/
4425
4426
/**
4427
 * \brief Set spatial reference from various text formats.
4428
 *
4429
 * This function is the same as OGRSpatialReference::SetFromUserInput().
4430
 *
4431
 * @since GDAL 3.9
4432
 */
4433
OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4434
                             CSLConstList papszOptions)
4435
4436
0
{
4437
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4438
4439
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4440
0
}
4441
4442
/************************************************************************/
4443
/*                          ImportFromUrl()                             */
4444
/************************************************************************/
4445
4446
/**
4447
 * \brief Set spatial reference from a URL.
4448
 *
4449
 * This method will download the spatial reference at a given URL and
4450
 * feed it into SetFromUserInput for you.
4451
 *
4452
 * This method does the same thing as the OSRImportFromUrl() function.
4453
 *
4454
 * @param pszUrl text definition to try to deduce SRS from.
4455
 *
4456
 * @return OGRERR_NONE on success, or an error code with the curl
4457
 * error message if it is unable to download data.
4458
 */
4459
4460
OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4461
4462
0
{
4463
0
    TAKE_OPTIONAL_LOCK();
4464
4465
0
    if (!STARTS_WITH_CI(pszUrl, "http://") &&
4466
0
        !STARTS_WITH_CI(pszUrl, "https://"))
4467
0
    {
4468
0
        CPLError(CE_Failure, CPLE_AppDefined,
4469
0
                 "The given string is not recognized as a URL"
4470
0
                 "starting with 'http://' -- %s",
4471
0
                 pszUrl);
4472
0
        return OGRERR_FAILURE;
4473
0
    }
4474
4475
    /* -------------------------------------------------------------------- */
4476
    /*      Fetch the result.                                               */
4477
    /* -------------------------------------------------------------------- */
4478
0
    CPLErrorReset();
4479
4480
0
    std::string osUrl(pszUrl);
4481
    // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4482
    // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4483
    // to query WKT. To allow a static server to be used, rather append a
4484
    // "ogcwkt/" suffix.
4485
0
    for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4486
0
                                  "http://spatialreference.org/ref/"})
4487
0
    {
4488
0
        if (STARTS_WITH(pszUrl, pszPrefix))
4489
0
        {
4490
0
            const CPLStringList aosTokens(
4491
0
                CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4492
0
            if (aosTokens.size() == 2)
4493
0
            {
4494
0
                osUrl = "https://spatialreference.org/ref/";
4495
0
                osUrl += aosTokens[0];  // authority
4496
0
                osUrl += '/';
4497
0
                osUrl += aosTokens[1];  // code
4498
0
                osUrl += "/ogcwkt/";
4499
0
            }
4500
0
            break;
4501
0
        }
4502
0
    }
4503
4504
0
    const char *pszTimeout = "TIMEOUT=10";
4505
0
    char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4506
4507
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4508
4509
    /* -------------------------------------------------------------------- */
4510
    /*      Try to handle errors.                                           */
4511
    /* -------------------------------------------------------------------- */
4512
4513
0
    if (psResult == nullptr)
4514
0
        return OGRERR_FAILURE;
4515
0
    if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4516
0
        psResult->pabyData == nullptr)
4517
0
    {
4518
0
        if (CPLGetLastErrorNo() == 0)
4519
0
        {
4520
0
            CPLError(CE_Failure, CPLE_AppDefined,
4521
0
                     "No data was returned from the given URL");
4522
0
        }
4523
0
        CPLHTTPDestroyResult(psResult);
4524
0
        return OGRERR_FAILURE;
4525
0
    }
4526
4527
0
    if (psResult->nStatus != 0)
4528
0
    {
4529
0
        CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4530
0
                 psResult->nStatus, psResult->pszErrBuf);
4531
0
        CPLHTTPDestroyResult(psResult);
4532
0
        return OGRERR_FAILURE;
4533
0
    }
4534
4535
0
    const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4536
0
    if (STARTS_WITH_CI(pszData, "http://") ||
4537
0
        STARTS_WITH_CI(pszData, "https://"))
4538
0
    {
4539
0
        CPLError(CE_Failure, CPLE_AppDefined,
4540
0
                 "The data that was downloaded also starts with 'http://' "
4541
0
                 "and cannot be passed into SetFromUserInput.  Is this "
4542
0
                 "really a spatial reference definition? ");
4543
0
        CPLHTTPDestroyResult(psResult);
4544
0
        return OGRERR_FAILURE;
4545
0
    }
4546
0
    if (OGRERR_NONE != SetFromUserInput(pszData))
4547
0
    {
4548
0
        CPLHTTPDestroyResult(psResult);
4549
0
        return OGRERR_FAILURE;
4550
0
    }
4551
4552
0
    CPLHTTPDestroyResult(psResult);
4553
0
    return OGRERR_NONE;
4554
0
}
4555
4556
/************************************************************************/
4557
/*                        OSRimportFromUrl()                            */
4558
/************************************************************************/
4559
4560
/**
4561
 * \brief Set spatial reference from a URL.
4562
 *
4563
 * This function is the same as OGRSpatialReference::importFromUrl()
4564
 */
4565
OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4566
4567
0
{
4568
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4569
4570
0
    return ToPointer(hSRS)->importFromUrl(pszUrl);
4571
0
}
4572
4573
/************************************************************************/
4574
/*                         importFromURNPart()                          */
4575
/************************************************************************/
4576
OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4577
                                              const char *pszCode,
4578
                                              const char *pszURN)
4579
0
{
4580
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4581
0
    (void)this;
4582
0
    (void)pszAuthority;
4583
0
    (void)pszCode;
4584
0
    (void)pszURN;
4585
0
    return OGRERR_FAILURE;
4586
#else
4587
    /* -------------------------------------------------------------------- */
4588
    /*      Is this an EPSG code? Note that we import it with EPSG          */
4589
    /*      preferred axis ordering for geographic coordinate systems.      */
4590
    /* -------------------------------------------------------------------- */
4591
    if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4592
        return importFromEPSGA(atoi(pszCode));
4593
4594
    /* -------------------------------------------------------------------- */
4595
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4596
    /* -------------------------------------------------------------------- */
4597
    if (STARTS_WITH_CI(pszAuthority, "IAU"))
4598
        return importFromDict("IAU2000.wkt", pszCode);
4599
4600
    /* -------------------------------------------------------------------- */
4601
    /*      Is this an OGC code?                                            */
4602
    /* -------------------------------------------------------------------- */
4603
    if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4604
    {
4605
        CPLError(CE_Failure, CPLE_AppDefined,
4606
                 "URN %s has unrecognized authority.", pszURN);
4607
        return OGRERR_FAILURE;
4608
    }
4609
4610
    if (STARTS_WITH_CI(pszCode, "CRS84"))
4611
        return SetWellKnownGeogCS(pszCode);
4612
    else if (STARTS_WITH_CI(pszCode, "CRS83"))
4613
        return SetWellKnownGeogCS(pszCode);
4614
    else if (STARTS_WITH_CI(pszCode, "CRS27"))
4615
        return SetWellKnownGeogCS(pszCode);
4616
    else if (STARTS_WITH_CI(pszCode, "84"))  // urn:ogc:def:crs:OGC:2:84
4617
        return SetWellKnownGeogCS("CRS84");
4618
4619
    /* -------------------------------------------------------------------- */
4620
    /*      Handle auto codes.  We need to convert from format              */
4621
    /*      AUTO42001:99:8888 to format AUTO:42001,99,8888.                 */
4622
    /* -------------------------------------------------------------------- */
4623
    else if (STARTS_WITH_CI(pszCode, "AUTO"))
4624
    {
4625
        char szWMSAuto[100] = {'\0'};
4626
4627
        if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4628
            return OGRERR_FAILURE;
4629
4630
        snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4631
        for (int i = 5; szWMSAuto[i] != '\0'; i++)
4632
        {
4633
            if (szWMSAuto[i] == ':')
4634
                szWMSAuto[i] = ',';
4635
        }
4636
4637
        return importFromWMSAUTO(szWMSAuto);
4638
    }
4639
4640
    /* -------------------------------------------------------------------- */
4641
    /*      Not a recognise OGC item.                                       */
4642
    /* -------------------------------------------------------------------- */
4643
    CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4644
             pszURN);
4645
4646
    return OGRERR_FAILURE;
4647
#endif
4648
0
}
4649
4650
/************************************************************************/
4651
/*                           importFromURN()                            */
4652
/*                                                                      */
4653
/*      See OGC recommendation paper 06-023r1 or later for details.     */
4654
/************************************************************************/
4655
4656
/**
4657
 * \brief Initialize from OGC URN.
4658
 *
4659
 * Initializes this spatial reference from a coordinate system defined
4660
 * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4661
 * paper 06-023r1.  Currently EPSG and OGC authority values are supported,
4662
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4663
 *
4664
 * This method is also support through SetFromUserInput() which can
4665
 * normally be used for URNs.
4666
 *
4667
 * @param pszURN the urn string.
4668
 *
4669
 * @return OGRERR_NONE on success or an error code.
4670
 */
4671
4672
OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4673
4674
0
{
4675
0
    constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4676
0
    if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4677
0
        CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4678
0
            CPL_VALUE_INTEGER)
4679
0
    {
4680
0
        return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4681
0
    }
4682
4683
0
    TAKE_OPTIONAL_LOCK();
4684
4685
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4686
4687
    // PROJ 8.2.0 has support for IAU codes now.
4688
#if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4689
    /* -------------------------------------------------------------------- */
4690
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4691
    /* -------------------------------------------------------------------- */
4692
    const char *pszIAU = strstr(pszURN, "IAU");
4693
    if (pszIAU)
4694
    {
4695
        const char *pszCode = strchr(pszIAU, ':');
4696
        if (pszCode)
4697
        {
4698
            ++pszCode;
4699
            if (*pszCode == ':')
4700
                ++pszCode;
4701
            return importFromDict("IAU2000.wkt", pszCode);
4702
        }
4703
    }
4704
#endif
4705
4706
0
    if (strlen(pszURN) >= 1000)
4707
0
    {
4708
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4709
0
        return OGRERR_CORRUPT_DATA;
4710
0
    }
4711
0
    auto obj = proj_create(d->getPROJContext(), pszURN);
4712
0
    if (!obj)
4713
0
    {
4714
0
        return OGRERR_FAILURE;
4715
0
    }
4716
0
    Clear();
4717
0
    d->setPjCRS(obj);
4718
0
    return OGRERR_NONE;
4719
#else
4720
    const char *pszCur = nullptr;
4721
4722
    if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4723
        pszCur = pszURN + 16;
4724
    else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4725
        pszCur = pszURN + 20;
4726
    else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4727
        pszCur = pszURN + 18;
4728
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4729
        pszCur = pszURN + 16;
4730
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4731
        pszCur = pszURN + 20;
4732
    else
4733
    {
4734
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4735
                 pszURN);
4736
        return OGRERR_FAILURE;
4737
    }
4738
4739
    /* -------------------------------------------------------------------- */
4740
    /*      Clear any existing definition.                                  */
4741
    /* -------------------------------------------------------------------- */
4742
    Clear();
4743
4744
    /* -------------------------------------------------------------------- */
4745
    /*      Find code (ignoring version) out of string like:                */
4746
    /*                                                                      */
4747
    /*      authority:[version]:code                                        */
4748
    /* -------------------------------------------------------------------- */
4749
    const char *pszAuthority = pszCur;
4750
4751
    // skip authority
4752
    while (*pszCur != ':' && *pszCur)
4753
        pszCur++;
4754
    if (*pszCur == ':')
4755
        pszCur++;
4756
4757
    // skip version
4758
    const char *pszBeforeVersion = pszCur;
4759
    while (*pszCur != ':' && *pszCur)
4760
        pszCur++;
4761
    if (*pszCur == ':')
4762
        pszCur++;
4763
    else
4764
        // We come here in the case, the content to parse is authority:code
4765
        // (instead of authority::code) which is probably illegal according to
4766
        // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4767
        // for example in what is returned by GeoServer.
4768
        pszCur = pszBeforeVersion;
4769
4770
    const char *pszCode = pszCur;
4771
4772
    const char *pszComma = strchr(pszCur, ',');
4773
    if (pszComma == nullptr)
4774
        return importFromURNPart(pszAuthority, pszCode, pszURN);
4775
4776
    // There's a second part with the vertical SRS.
4777
    pszCur = pszComma + 1;
4778
    if (!STARTS_WITH(pszCur, "crs:"))
4779
    {
4780
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4781
                 pszURN);
4782
        return OGRERR_FAILURE;
4783
    }
4784
4785
    pszCur += 4;
4786
4787
    char *pszFirstCode = CPLStrdup(pszCode);
4788
    pszFirstCode[pszComma - pszCode] = '\0';
4789
    OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4790
    CPLFree(pszFirstCode);
4791
4792
    // Do we want to turn this into a compound definition
4793
    // with a vertical datum?
4794
    if (eStatus != OGRERR_NONE)
4795
        return eStatus;
4796
4797
    /* -------------------------------------------------------------------- */
4798
    /*      Find code (ignoring version) out of string like:                */
4799
    /*                                                                      */
4800
    /*      authority:[version]:code                                        */
4801
    /* -------------------------------------------------------------------- */
4802
    pszAuthority = pszCur;
4803
4804
    // skip authority
4805
    while (*pszCur != ':' && *pszCur)
4806
        pszCur++;
4807
    if (*pszCur == ':')
4808
        pszCur++;
4809
4810
    // skip version
4811
    pszBeforeVersion = pszCur;
4812
    while (*pszCur != ':' && *pszCur)
4813
        pszCur++;
4814
    if (*pszCur == ':')
4815
        pszCur++;
4816
    else
4817
        pszCur = pszBeforeVersion;
4818
4819
    pszCode = pszCur;
4820
4821
    OGRSpatialReference oVertSRS;
4822
    eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4823
    if (eStatus == OGRERR_NONE)
4824
    {
4825
        OGRSpatialReference oHorizSRS(*this);
4826
4827
        Clear();
4828
4829
        oHorizSRS.d->refreshProjObj();
4830
        oVertSRS.d->refreshProjObj();
4831
        if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4832
            return OGRERR_FAILURE;
4833
4834
        const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4835
        const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4836
4837
        CPLString osName = pszHorizName ? pszHorizName : "";
4838
        osName += " + ";
4839
        osName += pszVertName ? pszVertName : "";
4840
4841
        SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4842
    }
4843
4844
    return eStatus;
4845
#endif
4846
0
}
4847
4848
/************************************************************************/
4849
/*                           importFromCRSURL()                         */
4850
/*                                                                      */
4851
/*      See OGC Best Practice document 11-135 for details.              */
4852
/************************************************************************/
4853
4854
/**
4855
 * \brief Initialize from OGC URL.
4856
 *
4857
 * Initializes this spatial reference from a coordinate system defined
4858
 * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4859
 * paper 11-135.  Currently EPSG and OGC authority values are supported,
4860
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4861
 *
4862
 * This method is also supported through SetFromUserInput() which can
4863
 * normally be used for URLs.
4864
 *
4865
 * @param pszURL the URL string.
4866
 *
4867
 * @return OGRERR_NONE on success or an error code.
4868
 */
4869
4870
OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4871
4872
0
{
4873
0
    TAKE_OPTIONAL_LOCK();
4874
4875
#if !PROJ_AT_LEAST_VERSION(9, 1, 0)
4876
    if (strcmp(pszURL, "http://www.opengis.net/def/crs/OGC/0/CRS84h") == 0)
4877
    {
4878
        PJ *obj = proj_create(
4879
            d->getPROJContext(),
4880
            "GEOGCRS[\"WGS 84 longitude-latitude-height\",\n"
4881
            "    ENSEMBLE[\"World Geodetic System 1984 ensemble\",\n"
4882
            "        MEMBER[\"World Geodetic System 1984 (Transit)\"],\n"
4883
            "        MEMBER[\"World Geodetic System 1984 (G730)\"],\n"
4884
            "        MEMBER[\"World Geodetic System 1984 (G873)\"],\n"
4885
            "        MEMBER[\"World Geodetic System 1984 (G1150)\"],\n"
4886
            "        MEMBER[\"World Geodetic System 1984 (G1674)\"],\n"
4887
            "        MEMBER[\"World Geodetic System 1984 (G1762)\"],\n"
4888
            "        MEMBER[\"World Geodetic System 1984 (G2139)\"],\n"
4889
            "        MEMBER[\"World Geodetic System 1984 (G2296)\"],\n"
4890
            "        ELLIPSOID[\"WGS 84\",6378137,298.257223563,\n"
4891
            "            LENGTHUNIT[\"metre\",1]],\n"
4892
            "        ENSEMBLEACCURACY[2.0]],\n"
4893
            "    PRIMEM[\"Greenwich\",0,\n"
4894
            "        ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4895
            "    CS[ellipsoidal,3],\n"
4896
            "        AXIS[\"geodetic longitude (Lon)\",east,\n"
4897
            "            ORDER[1],\n"
4898
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4899
            "        AXIS[\"geodetic latitude (Lat)\",north,\n"
4900
            "            ORDER[2],\n"
4901
            "            ANGLEUNIT[\"degree\",0.0174532925199433]],\n"
4902
            "        AXIS[\"ellipsoidal height (h)\",up,\n"
4903
            "            ORDER[3],\n"
4904
            "            LENGTHUNIT[\"metre\",1]],\n"
4905
            "    USAGE[\n"
4906
            "        SCOPE[\"3D system frequently used in GIS, Web APIs and "
4907
            "Web applications\"],\n"
4908
            "        AREA[\"World.\"],\n"
4909
            "        BBOX[-90,-180,90,180]],\n"
4910
            "    ID[\"OGC\",\"CRS84h\"]]");
4911
        if (!obj)
4912
        {
4913
            return OGRERR_FAILURE;
4914
        }
4915
        Clear();
4916
        d->setPjCRS(obj);
4917
        return OGRERR_NONE;
4918
    }
4919
#endif
4920
4921
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4922
0
    if (strlen(pszURL) >= 10000)
4923
0
    {
4924
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4925
0
        return OGRERR_CORRUPT_DATA;
4926
0
    }
4927
4928
0
    PJ *obj;
4929
#if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4930
    if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4931
    {
4932
        obj = proj_create(
4933
            d->getPROJContext(),
4934
            CPLSPrintf("IAU:%s",
4935
                       pszURL +
4936
                           strlen("http://www.opengis.net/def/crs/IAU/0/")));
4937
    }
4938
    else
4939
#endif
4940
0
    {
4941
0
        obj = proj_create(d->getPROJContext(), pszURL);
4942
0
    }
4943
0
    if (!obj)
4944
0
    {
4945
0
        return OGRERR_FAILURE;
4946
0
    }
4947
0
    Clear();
4948
0
    d->setPjCRS(obj);
4949
0
    return OGRERR_NONE;
4950
#else
4951
    const char *pszCur = nullptr;
4952
4953
    if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4954
        pszCur = pszURL + 26;
4955
    else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4956
        pszCur = pszURL + 27;
4957
    else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4958
        pszCur = pszURL + 30;
4959
    else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4960
        pszCur = pszURL + 31;
4961
    else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4962
        pszCur = pszURL + 23;
4963
    else
4964
    {
4965
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4966
                 pszURL);
4967
        return OGRERR_FAILURE;
4968
    }
4969
4970
    if (*pszCur == '\0')
4971
    {
4972
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4973
        return OGRERR_FAILURE;
4974
    }
4975
4976
    /* -------------------------------------------------------------------- */
4977
    /*      Clear any existing definition.                                  */
4978
    /* -------------------------------------------------------------------- */
4979
    Clear();
4980
4981
    if (STARTS_WITH_CI(pszCur, "-compound?1="))
4982
    {
4983
        /* --------------------------------------------------------------------
4984
         */
4985
        /*      It's a compound CRS, of the form: */
4986
        /*                                                                      */
4987
        /*      http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4988
        /* --------------------------------------------------------------------
4989
         */
4990
        pszCur += 12;
4991
4992
        // Extract each component CRS URL.
4993
        int iComponentUrl = 2;
4994
4995
        CPLString osName = "";
4996
        Clear();
4997
4998
        while (iComponentUrl != -1)
4999
        {
5000
            char searchStr[15] = {};
5001
            snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
5002
5003
            const char *pszUrlEnd = strstr(pszCur, searchStr);
5004
5005
            // Figure out the next component URL.
5006
            char *pszComponentUrl = nullptr;
5007
5008
            if (pszUrlEnd)
5009
            {
5010
                size_t nLen = pszUrlEnd - pszCur;
5011
                pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
5012
                strncpy(pszComponentUrl, pszCur, nLen);
5013
                pszComponentUrl[nLen] = '\0';
5014
5015
                ++iComponentUrl;
5016
                pszCur += nLen + strlen(searchStr);
5017
            }
5018
            else
5019
            {
5020
                if (iComponentUrl == 2)
5021
                {
5022
                    CPLError(CE_Failure, CPLE_AppDefined,
5023
                             "Compound CRS URLs must have at least two "
5024
                             "component CRSs.");
5025
                    return OGRERR_FAILURE;
5026
                }
5027
                else
5028
                {
5029
                    pszComponentUrl = CPLStrdup(pszCur);
5030
                    // no more components
5031
                    iComponentUrl = -1;
5032
                }
5033
            }
5034
5035
            OGRSpatialReference oComponentSRS;
5036
            OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
5037
5038
            CPLFree(pszComponentUrl);
5039
            pszComponentUrl = nullptr;
5040
5041
            if (eStatus == OGRERR_NONE)
5042
            {
5043
                if (osName.length() != 0)
5044
                {
5045
                    osName += " + ";
5046
                }
5047
                osName += oComponentSRS.GetRoot()->GetValue();
5048
                SetNode("COMPD_CS", osName);
5049
                GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
5050
            }
5051
            else
5052
                return eStatus;
5053
        }
5054
5055
        return OGRERR_NONE;
5056
    }
5057
5058
    /* -------------------------------------------------------------------- */
5059
    /*      It's a normal CRS URL, of the form:                             */
5060
    /*                                                                      */
5061
    /*      http://opengis.net/def/crs/AUTHORITY/VERSION/CODE               */
5062
    /* -------------------------------------------------------------------- */
5063
    ++pszCur;
5064
    const char *pszAuthority = pszCur;
5065
5066
    // skip authority
5067
    while (*pszCur != '/' && *pszCur)
5068
        pszCur++;
5069
    if (*pszCur == '/')
5070
        pszCur++;
5071
5072
    // skip version
5073
    while (*pszCur != '/' && *pszCur)
5074
        pszCur++;
5075
    if (*pszCur == '/')
5076
        pszCur++;
5077
5078
    const char *pszCode = pszCur;
5079
5080
    return importFromURNPart(pszAuthority, pszCode, pszURL);
5081
#endif
5082
0
}
5083
5084
/************************************************************************/
5085
/*                         importFromWMSAUTO()                          */
5086
/************************************************************************/
5087
5088
/**
5089
 * \brief Initialize from WMSAUTO string.
5090
 *
5091
 * Note that the WMS 1.3 specification does not include the
5092
 * units code, while apparently earlier specs do.  We try to
5093
 * guess around this.
5094
 *
5095
 * @param pszDefinition the WMSAUTO string
5096
 *
5097
 * @return OGRERR_NONE on success or an error code.
5098
 */
5099
OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
5100
5101
0
{
5102
0
    TAKE_OPTIONAL_LOCK();
5103
5104
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
5105
0
    if (strlen(pszDefinition) >= 10000)
5106
0
    {
5107
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
5108
0
        return OGRERR_CORRUPT_DATA;
5109
0
    }
5110
5111
0
    auto obj = proj_create(d->getPROJContext(), pszDefinition);
5112
0
    if (!obj)
5113
0
    {
5114
0
        return OGRERR_FAILURE;
5115
0
    }
5116
0
    Clear();
5117
0
    d->setPjCRS(obj);
5118
0
    return OGRERR_NONE;
5119
#else
5120
    int nProjId, nUnitsId;
5121
    double dfRefLong, dfRefLat = 0.0;
5122
5123
    /* -------------------------------------------------------------------- */
5124
    /*      Tokenize                                                        */
5125
    /* -------------------------------------------------------------------- */
5126
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
5127
        pszDefinition += 5;
5128
5129
    char **papszTokens =
5130
        CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5131
5132
    if (CSLCount(papszTokens) == 4)
5133
    {
5134
        nProjId = atoi(papszTokens[0]);
5135
        nUnitsId = atoi(papszTokens[1]);
5136
        dfRefLong = CPLAtof(papszTokens[2]);
5137
        dfRefLat = CPLAtof(papszTokens[3]);
5138
    }
5139
    else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5140
    {
5141
        nProjId = atoi(papszTokens[0]);
5142
        nUnitsId = atoi(papszTokens[1]);
5143
        dfRefLong = CPLAtof(papszTokens[2]);
5144
        dfRefLat = 0.0;
5145
    }
5146
    else if (CSLCount(papszTokens) == 3)
5147
    {
5148
        nProjId = atoi(papszTokens[0]);
5149
        nUnitsId = 9001;
5150
        dfRefLong = CPLAtof(papszTokens[1]);
5151
        dfRefLat = CPLAtof(papszTokens[2]);
5152
    }
5153
    else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5154
    {
5155
        nProjId = atoi(papszTokens[0]);
5156
        nUnitsId = 9001;
5157
        dfRefLong = CPLAtof(papszTokens[1]);
5158
    }
5159
    else
5160
    {
5161
        CSLDestroy(papszTokens);
5162
        CPLError(CE_Failure, CPLE_AppDefined,
5163
                 "AUTO projection has wrong number of arguments, expected\n"
5164
                 "AUTO:proj_id,units_id,ref_long,ref_lat or"
5165
                 "AUTO:proj_id,ref_long,ref_lat");
5166
        return OGRERR_FAILURE;
5167
    }
5168
5169
    CSLDestroy(papszTokens);
5170
    papszTokens = nullptr;
5171
5172
    /* -------------------------------------------------------------------- */
5173
    /*      Build coordsys.                                                 */
5174
    /* -------------------------------------------------------------------- */
5175
    Clear();
5176
5177
    /* -------------------------------------------------------------------- */
5178
    /*      Set WGS84.                                                      */
5179
    /* -------------------------------------------------------------------- */
5180
    SetWellKnownGeogCS("WGS84");
5181
5182
    switch (nProjId)
5183
    {
5184
        case 42001:  // Auto UTM
5185
            SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5186
                   dfRefLat >= 0.0);
5187
            break;
5188
5189
        case 42002:  // Auto TM (strangely very UTM-like).
5190
            SetTM(0, dfRefLong, 0.9996, 500000.0,
5191
                  (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5192
            break;
5193
5194
        case 42003:  // Auto Orthographic.
5195
            SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5196
            break;
5197
5198
        case 42004:  // Auto Equirectangular
5199
            SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5200
            break;
5201
5202
        case 42005:
5203
            SetMollweide(dfRefLong, 0.0, 0.0);
5204
            break;
5205
5206
        default:
5207
            CPLError(CE_Failure, CPLE_AppDefined,
5208
                     "Unsupported projection id in importFromWMSAUTO(): %d",
5209
                     nProjId);
5210
            return OGRERR_FAILURE;
5211
    }
5212
5213
    /* -------------------------------------------------------------------- */
5214
    /*      Set units.                                                      */
5215
    /* -------------------------------------------------------------------- */
5216
5217
    switch (nUnitsId)
5218
    {
5219
        case 9001:
5220
            SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5221
            break;
5222
5223
        case 9002:
5224
            SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5225
            break;
5226
5227
        case 9003:
5228
            SetTargetLinearUnits(nullptr, "US survey foot",
5229
                                 CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5230
            break;
5231
5232
        default:
5233
            CPLError(CE_Failure, CPLE_AppDefined,
5234
                     "Unsupported units code (%d).", nUnitsId);
5235
            return OGRERR_FAILURE;
5236
            break;
5237
    }
5238
5239
    return OGRERR_NONE;
5240
#endif
5241
0
}
5242
5243
/************************************************************************/
5244
/*                            GetSemiMajor()                            */
5245
/************************************************************************/
5246
5247
/**
5248
 * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5249
 *
5250
 * This method does the same thing as the C function OSRGetSemiMajor().
5251
 *
5252
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5253
 * can be found.
5254
 *
5255
 * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5256
 */
5257
5258
double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5259
5260
0
{
5261
0
    TAKE_OPTIONAL_LOCK();
5262
5263
0
    if (pnErr != nullptr)
5264
0
        *pnErr = OGRERR_FAILURE;
5265
5266
0
    d->refreshProjObj();
5267
0
    if (!d->m_pj_crs)
5268
0
        return SRS_WGS84_SEMIMAJOR;
5269
5270
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5271
0
    if (!ellps)
5272
0
        return SRS_WGS84_SEMIMAJOR;
5273
5274
0
    double dfSemiMajor = 0.0;
5275
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5276
0
                                  nullptr, nullptr, nullptr);
5277
0
    proj_destroy(ellps);
5278
5279
0
    if (dfSemiMajor > 0)
5280
0
    {
5281
0
        if (pnErr != nullptr)
5282
0
            *pnErr = OGRERR_NONE;
5283
0
        return dfSemiMajor;
5284
0
    }
5285
5286
0
    return SRS_WGS84_SEMIMAJOR;
5287
0
}
5288
5289
/************************************************************************/
5290
/*                          OSRGetSemiMajor()                           */
5291
/************************************************************************/
5292
5293
/**
5294
 * \brief Get spheroid semi major axis.
5295
 *
5296
 * This function is the same as OGRSpatialReference::GetSemiMajor()
5297
 */
5298
double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5299
5300
0
{
5301
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5302
5303
0
    return ToPointer(hSRS)->GetSemiMajor(pnErr);
5304
0
}
5305
5306
/************************************************************************/
5307
/*                          GetInvFlattening()                          */
5308
/************************************************************************/
5309
5310
/**
5311
 * \brief Get spheroid inverse flattening.
5312
 *
5313
 * This method does the same thing as the C function OSRGetInvFlattening().
5314
 *
5315
 * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5316
 * can be found.
5317
 *
5318
 * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5319
 */
5320
5321
double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5322
5323
0
{
5324
0
    TAKE_OPTIONAL_LOCK();
5325
5326
0
    if (pnErr != nullptr)
5327
0
        *pnErr = OGRERR_FAILURE;
5328
5329
0
    d->refreshProjObj();
5330
0
    if (!d->m_pj_crs)
5331
0
        return SRS_WGS84_INVFLATTENING;
5332
5333
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5334
0
    if (!ellps)
5335
0
        return SRS_WGS84_INVFLATTENING;
5336
5337
0
    double dfInvFlattening = -1.0;
5338
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5339
0
                                  nullptr, &dfInvFlattening);
5340
0
    proj_destroy(ellps);
5341
5342
0
    if (dfInvFlattening >= 0.0)
5343
0
    {
5344
0
        if (pnErr != nullptr)
5345
0
            *pnErr = OGRERR_NONE;
5346
0
        return dfInvFlattening;
5347
0
    }
5348
5349
0
    return SRS_WGS84_INVFLATTENING;
5350
0
}
5351
5352
/************************************************************************/
5353
/*                        OSRGetInvFlattening()                         */
5354
/************************************************************************/
5355
5356
/**
5357
 * \brief Get spheroid inverse flattening.
5358
 *
5359
 * This function is the same as OGRSpatialReference::GetInvFlattening()
5360
 */
5361
double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5362
5363
0
{
5364
0
    VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5365
5366
0
    return ToPointer(hSRS)->GetInvFlattening(pnErr);
5367
0
}
5368
5369
/************************************************************************/
5370
/*                           GetEccentricity()                          */
5371
/************************************************************************/
5372
5373
/**
5374
 * \brief Get spheroid eccentricity
5375
 *
5376
 * @return eccentricity (or -1 in case of error)
5377
 */
5378
5379
double OGRSpatialReference::GetEccentricity() const
5380
5381
0
{
5382
0
    OGRErr eErr = OGRERR_NONE;
5383
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5384
0
    if (eErr != OGRERR_NONE)
5385
0
    {
5386
0
        return -1.0;
5387
0
    }
5388
0
    if (dfInvFlattening == 0.0)
5389
0
        return 0.0;
5390
0
    if (dfInvFlattening < 0.5)
5391
0
        return -1.0;
5392
0
    return sqrt(2.0 / dfInvFlattening -
5393
0
                1.0 / (dfInvFlattening * dfInvFlattening));
5394
0
}
5395
5396
/************************************************************************/
5397
/*                      GetSquaredEccentricity()                        */
5398
/************************************************************************/
5399
5400
/**
5401
 * \brief Get spheroid squared eccentricity
5402
 *
5403
 * @return squared eccentricity (or -1 in case of error)
5404
 */
5405
5406
double OGRSpatialReference::GetSquaredEccentricity() const
5407
5408
0
{
5409
0
    OGRErr eErr = OGRERR_NONE;
5410
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5411
0
    if (eErr != OGRERR_NONE)
5412
0
    {
5413
0
        return -1.0;
5414
0
    }
5415
0
    if (dfInvFlattening == 0.0)
5416
0
        return 0.0;
5417
0
    if (dfInvFlattening < 0.5)
5418
0
        return -1.0;
5419
0
    return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5420
0
}
5421
5422
/************************************************************************/
5423
/*                            GetSemiMinor()                            */
5424
/************************************************************************/
5425
5426
/**
5427
 * \brief Get spheroid semi minor axis.
5428
 *
5429
 * This method does the same thing as the C function OSRGetSemiMinor().
5430
 *
5431
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5432
 * can be found.
5433
 *
5434
 * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5435
 */
5436
5437
double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5438
5439
0
{
5440
0
    const double dfSemiMajor = GetSemiMajor(pnErr);
5441
0
    const double dfInvFlattening = GetInvFlattening(pnErr);
5442
5443
0
    return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5444
0
}
5445
5446
/************************************************************************/
5447
/*                          OSRGetSemiMinor()                           */
5448
/************************************************************************/
5449
5450
/**
5451
 * \brief Get spheroid semi minor axis.
5452
 *
5453
 * This function is the same as OGRSpatialReference::GetSemiMinor()
5454
 */
5455
double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5456
5457
0
{
5458
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5459
5460
0
    return ToPointer(hSRS)->GetSemiMinor(pnErr);
5461
0
}
5462
5463
/************************************************************************/
5464
/*                             SetLocalCS()                             */
5465
/************************************************************************/
5466
5467
/**
5468
 * \brief Set the user visible LOCAL_CS name.
5469
 *
5470
 * This method is the same as the C function OSRSetLocalCS().
5471
 *
5472
 * This method will ensure a LOCAL_CS node is created as the root,
5473
 * and set the provided name on it.  It must be used before SetLinearUnits().
5474
 *
5475
 * @param pszName the user visible name to assign.  Not used as a key.
5476
 *
5477
 * @return OGRERR_NONE on success.
5478
 */
5479
5480
OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5481
5482
0
{
5483
0
    TAKE_OPTIONAL_LOCK();
5484
5485
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5486
0
        d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5487
0
    {
5488
0
        d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5489
0
    }
5490
0
    else
5491
0
    {
5492
0
        CPLDebug("OGR",
5493
0
                 "OGRSpatialReference::SetLocalCS(%s) failed.  "
5494
0
                 "It appears an incompatible object already exists.",
5495
0
                 pszName);
5496
0
        return OGRERR_FAILURE;
5497
0
    }
5498
5499
0
    return OGRERR_NONE;
5500
0
}
5501
5502
/************************************************************************/
5503
/*                           OSRSetLocalCS()                            */
5504
/************************************************************************/
5505
5506
/**
5507
 * \brief Set the user visible LOCAL_CS name.
5508
 *
5509
 * This function is the same as OGRSpatialReference::SetLocalCS()
5510
 */
5511
OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5512
5513
0
{
5514
0
    VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5515
5516
0
    return ToPointer(hSRS)->SetLocalCS(pszName);
5517
0
}
5518
5519
/************************************************************************/
5520
/*                             SetGeocCS()                              */
5521
/************************************************************************/
5522
5523
/**
5524
 * \brief Set the user visible GEOCCS name.
5525
 *
5526
 * This method is the same as the C function OSRSetGeocCS().
5527
5528
 * This method will ensure a GEOCCS node is created as the root,
5529
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5530
 * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5531
 * the GEOGCS.
5532
 *
5533
 * @param pszName the user visible name to assign.  Not used as a key.
5534
 *
5535
 * @return OGRERR_NONE on success.
5536
 *
5537
 */
5538
5539
OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5540
5541
0
{
5542
0
    TAKE_OPTIONAL_LOCK();
5543
5544
0
    OGRErr eErr = OGRERR_NONE;
5545
0
    d->refreshProjObj();
5546
0
    d->demoteFromBoundCRS();
5547
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN)
5548
0
    {
5549
0
        d->setPjCRS(proj_create_geocentric_crs(
5550
0
            d->getPROJContext(), pszName, "World Geodetic System 1984",
5551
0
            "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5552
0
            SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5553
0
            "Metre", 1.0));
5554
0
    }
5555
0
    else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5556
0
    {
5557
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5558
0
    }
5559
0
    else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5560
0
             d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5561
0
    {
5562
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5563
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
5564
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5565
0
        if (datum == nullptr)
5566
0
        {
5567
0
            datum =
5568
0
                proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5569
0
        }
5570
0
#endif
5571
0
        if (datum == nullptr)
5572
0
        {
5573
0
            d->undoDemoteFromBoundCRS();
5574
0
            return OGRERR_FAILURE;
5575
0
        }
5576
5577
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
5578
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5579
0
            0.0);
5580
0
        d->setPjCRS(pj_crs);
5581
5582
0
        proj_destroy(datum);
5583
0
    }
5584
0
    else
5585
0
    {
5586
0
        CPLDebug("OGR",
5587
0
                 "OGRSpatialReference::SetGeocCS(%s) failed.  "
5588
0
                 "It appears an incompatible object already exists.",
5589
0
                 pszName);
5590
0
        eErr = OGRERR_FAILURE;
5591
0
    }
5592
0
    d->undoDemoteFromBoundCRS();
5593
5594
0
    return eErr;
5595
0
}
5596
5597
/************************************************************************/
5598
/*                            OSRSetGeocCS()                            */
5599
/************************************************************************/
5600
5601
/**
5602
 * \brief Set the user visible PROJCS name.
5603
 *
5604
 * This function is the same as OGRSpatialReference::SetGeocCS()
5605
 *
5606
 */
5607
OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5608
5609
0
{
5610
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5611
5612
0
    return ToPointer(hSRS)->SetGeocCS(pszName);
5613
0
}
5614
5615
/************************************************************************/
5616
/*                             SetVertCS()                              */
5617
/************************************************************************/
5618
5619
/**
5620
 * \brief Set the user visible VERT_CS name.
5621
 *
5622
 * This method is the same as the C function OSRSetVertCS().
5623
5624
 * This method will ensure a VERT_CS node is created if needed.  If the
5625
 * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5626
 * turned into a COMPD_CS.
5627
 *
5628
 * @param pszVertCSName the user visible name of the vertical coordinate
5629
 * system. Not used as a key.
5630
 *
5631
 * @param pszVertDatumName the user visible name of the vertical datum.  It
5632
 * is helpful if this matches the EPSG name.
5633
 *
5634
 * @param nVertDatumType the OGC vertical datum type. Ignored
5635
 *
5636
 * @return OGRERR_NONE on success.
5637
 *
5638
 */
5639
5640
OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5641
                                      const char *pszVertDatumName,
5642
                                      int nVertDatumType)
5643
5644
0
{
5645
0
    TAKE_OPTIONAL_LOCK();
5646
5647
0
    CPL_IGNORE_RET_VAL(nVertDatumType);
5648
5649
0
    d->refreshProjObj();
5650
5651
0
    auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5652
0
                                            pszVertDatumName, nullptr, 0.0);
5653
5654
    /* -------------------------------------------------------------------- */
5655
    /*      Handle the case where we want to make a compound coordinate     */
5656
    /*      system.                                                         */
5657
    /* -------------------------------------------------------------------- */
5658
0
    if (IsProjected() || IsGeographic())
5659
0
    {
5660
0
        auto compoundCRS = proj_create_compound_crs(
5661
0
            d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5662
0
        proj_destroy(vertCRS);
5663
0
        d->setPjCRS(compoundCRS);
5664
0
    }
5665
0
    else
5666
0
    {
5667
0
        d->setPjCRS(vertCRS);
5668
0
    }
5669
0
    return OGRERR_NONE;
5670
0
}
5671
5672
/************************************************************************/
5673
/*                            OSRSetVertCS()                            */
5674
/************************************************************************/
5675
5676
/**
5677
 * \brief Setup the vertical coordinate system.
5678
 *
5679
 * This function is the same as OGRSpatialReference::SetVertCS()
5680
 *
5681
 */
5682
OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5683
                    const char *pszVertDatumName, int nVertDatumType)
5684
5685
0
{
5686
0
    VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5687
5688
0
    return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5689
0
                                      nVertDatumType);
5690
0
}
5691
5692
/************************************************************************/
5693
/*                           SetCompoundCS()                            */
5694
/************************************************************************/
5695
5696
/**
5697
 * \brief Setup a compound coordinate system.
5698
 *
5699
 * This method is the same as the C function OSRSetCompoundCS().
5700
5701
 * This method is replace the current SRS with a COMPD_CS coordinate system
5702
 * consisting of the passed in horizontal and vertical coordinate systems.
5703
 *
5704
 * @param pszName the name of the compound coordinate system.
5705
 *
5706
 * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5707
 *
5708
 * @param poVertSRS the vertical SRS (VERT_CS).
5709
 *
5710
 * @return OGRERR_NONE on success.
5711
 */
5712
5713
OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5714
                                          const OGRSpatialReference *poHorizSRS,
5715
                                          const OGRSpatialReference *poVertSRS)
5716
5717
0
{
5718
0
    TAKE_OPTIONAL_LOCK();
5719
5720
    /* -------------------------------------------------------------------- */
5721
    /*      Verify these are legal horizontal and vertical coordinate       */
5722
    /*      systems.                                                        */
5723
    /* -------------------------------------------------------------------- */
5724
0
    if (!poVertSRS->IsVertical())
5725
0
    {
5726
0
        CPLError(CE_Failure, CPLE_AppDefined,
5727
0
                 "SetCompoundCS() fails, vertical component is not VERT_CS.");
5728
0
        return OGRERR_FAILURE;
5729
0
    }
5730
0
    if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5731
0
    {
5732
0
        CPLError(CE_Failure, CPLE_AppDefined,
5733
0
                 "SetCompoundCS() fails, horizontal component is not PROJCS or "
5734
0
                 "GEOGCS.");
5735
0
        return OGRERR_FAILURE;
5736
0
    }
5737
5738
    /* -------------------------------------------------------------------- */
5739
    /*      Replace with compound srs.                                      */
5740
    /* -------------------------------------------------------------------- */
5741
0
    Clear();
5742
5743
0
    auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5744
0
                                                poHorizSRS->d->m_pj_crs,
5745
0
                                                poVertSRS->d->m_pj_crs);
5746
0
    d->setPjCRS(compoundCRS);
5747
5748
0
    return OGRERR_NONE;
5749
0
}
5750
5751
/************************************************************************/
5752
/*                          OSRSetCompoundCS()                          */
5753
/************************************************************************/
5754
5755
/**
5756
 * \brief Setup a compound coordinate system.
5757
 *
5758
 * This function is the same as OGRSpatialReference::SetCompoundCS()
5759
 */
5760
OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5761
                        OGRSpatialReferenceH hHorizSRS,
5762
                        OGRSpatialReferenceH hVertSRS)
5763
5764
0
{
5765
0
    VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5766
0
    VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5767
0
    VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5768
5769
0
    return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5770
0
                                          ToPointer(hVertSRS));
5771
0
}
5772
5773
/************************************************************************/
5774
/*                             SetProjCS()                              */
5775
/************************************************************************/
5776
5777
/**
5778
 * \brief Set the user visible PROJCS name.
5779
 *
5780
 * This method is the same as the C function OSRSetProjCS().
5781
 *
5782
 * This method will ensure a PROJCS node is created as the root,
5783
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5784
 * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5785
 *
5786
 * @param pszName the user visible name to assign.  Not used as a key.
5787
 *
5788
 * @return OGRERR_NONE on success.
5789
 */
5790
5791
OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5792
5793
0
{
5794
0
    TAKE_OPTIONAL_LOCK();
5795
5796
0
    d->refreshProjObj();
5797
0
    d->demoteFromBoundCRS();
5798
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5799
0
    {
5800
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5801
0
    }
5802
0
    else
5803
0
    {
5804
0
        auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5805
0
                                                nullptr, nullptr, nullptr,
5806
0
                                                nullptr, nullptr, 0, nullptr);
5807
0
        auto cs = proj_create_cartesian_2D_cs(
5808
0
            d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5809
5810
0
        auto projCRS = proj_create_projected_crs(
5811
0
            d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5812
0
        proj_destroy(dummyConv);
5813
0
        proj_destroy(cs);
5814
5815
0
        d->setPjCRS(projCRS);
5816
0
    }
5817
0
    d->undoDemoteFromBoundCRS();
5818
0
    return OGRERR_NONE;
5819
0
}
5820
5821
/************************************************************************/
5822
/*                            OSRSetProjCS()                            */
5823
/************************************************************************/
5824
5825
/**
5826
 * \brief Set the user visible PROJCS name.
5827
 *
5828
 * This function is the same as OGRSpatialReference::SetProjCS()
5829
 */
5830
OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5831
5832
0
{
5833
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5834
5835
0
    return ToPointer(hSRS)->SetProjCS(pszName);
5836
0
}
5837
5838
/************************************************************************/
5839
/*                           SetProjection()                            */
5840
/************************************************************************/
5841
5842
/**
5843
 * \brief Set a projection name.
5844
 *
5845
 * This method is the same as the C function OSRSetProjection().
5846
 *
5847
 * @param pszProjection the projection name, which should be selected from
5848
 * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5849
 *
5850
 * @return OGRERR_NONE on success.
5851
 */
5852
5853
OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5854
5855
0
{
5856
0
    TAKE_OPTIONAL_LOCK();
5857
5858
0
    OGR_SRSNode *poGeogCS = nullptr;
5859
5860
0
    if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5861
0
    {
5862
0
        poGeogCS = d->m_poRoot;
5863
0
        d->m_poRoot = nullptr;
5864
0
    }
5865
5866
0
    if (!GetAttrNode("PROJCS"))
5867
0
    {
5868
0
        SetNode("PROJCS", "unnamed");
5869
0
    }
5870
5871
0
    const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5872
0
    if (eErr != OGRERR_NONE)
5873
0
        return eErr;
5874
5875
0
    if (poGeogCS != nullptr)
5876
0
        d->m_poRoot->InsertChild(poGeogCS, 1);
5877
5878
0
    return OGRERR_NONE;
5879
0
}
5880
5881
/************************************************************************/
5882
/*                            OSRSetProjection()                        */
5883
/************************************************************************/
5884
5885
/**
5886
 * \brief Set a projection name.
5887
 *
5888
 * This function is the same as OGRSpatialReference::SetProjection()
5889
 */
5890
OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5891
5892
0
{
5893
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5894
5895
0
    return ToPointer(hSRS)->SetProjection(pszProjection);
5896
0
}
5897
5898
/************************************************************************/
5899
/*                      GetWKT2ProjectionMethod()                       */
5900
/************************************************************************/
5901
5902
/**
5903
 * \brief Returns info on the projection method, based on WKT2 naming
5904
 * conventions.
5905
 *
5906
 * The returned strings are short lived and should be considered to be
5907
 * invalidated by any further call to the GDAL API.
5908
 *
5909
 * @param[out] ppszMethodName Pointer to a string that will receive the
5910
 * projection method name.
5911
 * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5912
 * receive the name of the authority that defines the projection method.
5913
 * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5914
 * an authority.
5915
 * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5916
 * receive the code that defines the projection method.
5917
 * *ppszMethodCode may be nullptr if the projection method is not linked to
5918
 * an authority.
5919
 *
5920
 * @return OGRERR_NONE on success.
5921
 */
5922
OGRErr
5923
OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5924
                                             const char **ppszMethodAuthName,
5925
                                             const char **ppszMethodCode) const
5926
0
{
5927
0
    TAKE_OPTIONAL_LOCK();
5928
5929
0
    auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5930
0
    if (!conv)
5931
0
        return OGRERR_FAILURE;
5932
0
    const char *pszTmpMethodName = "";
5933
0
    const char *pszTmpMethodAuthName = "";
5934
0
    const char *pszTmpMethodCode = "";
5935
0
    int ret = proj_coordoperation_get_method_info(
5936
0
        d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5937
0
        &pszTmpMethodCode);
5938
    // "Internalize" temporary strings returned by PROJ
5939
0
    CPLAssert(pszTmpMethodName);
5940
0
    if (ppszMethodName)
5941
0
        *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5942
0
    if (ppszMethodAuthName)
5943
0
        *ppszMethodAuthName = pszTmpMethodAuthName
5944
0
                                  ? CPLSPrintf("%s", pszTmpMethodAuthName)
5945
0
                                  : nullptr;
5946
0
    if (ppszMethodCode)
5947
0
        *ppszMethodCode =
5948
0
            pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5949
0
    proj_destroy(conv);
5950
0
    return ret ? OGRERR_NONE : OGRERR_FAILURE;
5951
0
}
5952
5953
/************************************************************************/
5954
/*                            SetProjParm()                             */
5955
/************************************************************************/
5956
5957
/**
5958
 * \brief Set a projection parameter value.
5959
 *
5960
 * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5961
 *
5962
 * This method is the same as the C function OSRSetProjParm().
5963
 *
5964
 * Please check https://gdal.org/proj_list pages for
5965
 * legal parameter names for specific projections.
5966
 *
5967
 *
5968
 * @param pszParamName the parameter name, which should be selected from
5969
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5970
 *
5971
 * @param dfValue value to assign.
5972
 *
5973
 * @return OGRERR_NONE on success.
5974
 */
5975
5976
OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5977
                                        double dfValue)
5978
5979
0
{
5980
0
    TAKE_OPTIONAL_LOCK();
5981
5982
0
    OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5983
5984
0
    if (poPROJCS == nullptr)
5985
0
        return OGRERR_FAILURE;
5986
5987
0
    char szValue[64] = {'\0'};
5988
0
    OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5989
5990
    /* -------------------------------------------------------------------- */
5991
    /*      Try to find existing parameter with this name.                  */
5992
    /* -------------------------------------------------------------------- */
5993
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5994
0
    {
5995
0
        OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5996
5997
0
        if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5998
0
            poParam->GetChildCount() == 2 &&
5999
0
            EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
6000
0
        {
6001
0
            poParam->GetChild(1)->SetValue(szValue);
6002
0
            return OGRERR_NONE;
6003
0
        }
6004
0
    }
6005
6006
    /* -------------------------------------------------------------------- */
6007
    /*      Otherwise create a new parameter and append.                    */
6008
    /* -------------------------------------------------------------------- */
6009
0
    OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
6010
0
    poParam->AddChild(new OGR_SRSNode(pszParamName));
6011
0
    poParam->AddChild(new OGR_SRSNode(szValue));
6012
6013
0
    poPROJCS->AddChild(poParam);
6014
6015
0
    return OGRERR_NONE;
6016
0
}
6017
6018
/************************************************************************/
6019
/*                           OSRSetProjParm()                           */
6020
/************************************************************************/
6021
6022
/**
6023
 * \brief Set a projection parameter value.
6024
 *
6025
 * This function is the same as OGRSpatialReference::SetProjParm()
6026
 */
6027
OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6028
                      double dfValue)
6029
6030
0
{
6031
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
6032
6033
0
    return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
6034
0
}
6035
6036
/************************************************************************/
6037
/*                            FindProjParm()                            */
6038
/************************************************************************/
6039
6040
/**
6041
 * \brief Return the child index of the named projection parameter on
6042
 * its parent PROJCS node.
6043
 *
6044
 * @param pszParameter projection parameter to look for
6045
 * @param poPROJCS projection CS node to look in. If NULL is passed,
6046
 *        the PROJCS node of the SpatialReference object will be searched.
6047
 *
6048
 * @return the child index of the named projection parameter. -1 on failure
6049
 */
6050
int OGRSpatialReference::FindProjParm(const char *pszParameter,
6051
                                      const OGR_SRSNode *poPROJCS) const
6052
6053
0
{
6054
0
    TAKE_OPTIONAL_LOCK();
6055
6056
0
    if (poPROJCS == nullptr)
6057
0
        poPROJCS = GetAttrNode("PROJCS");
6058
6059
0
    if (poPROJCS == nullptr)
6060
0
        return -1;
6061
6062
    /* -------------------------------------------------------------------- */
6063
    /*      Search for requested parameter.                                 */
6064
    /* -------------------------------------------------------------------- */
6065
0
    bool bIsWKT2 = false;
6066
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
6067
0
    {
6068
0
        const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6069
6070
0
        if (poParameter->GetChildCount() >= 2)
6071
0
        {
6072
0
            const char *pszValue = poParameter->GetValue();
6073
0
            if (EQUAL(pszValue, "PARAMETER") &&
6074
0
                EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
6075
0
                      pszParameter))
6076
0
            {
6077
0
                return iChild;
6078
0
            }
6079
0
            else if (EQUAL(pszValue, "METHOD"))
6080
0
            {
6081
0
                bIsWKT2 = true;
6082
0
            }
6083
0
        }
6084
0
    }
6085
6086
    /* -------------------------------------------------------------------- */
6087
    /*      Try similar names, for selected parameters.                     */
6088
    /* -------------------------------------------------------------------- */
6089
0
    if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
6090
0
    {
6091
0
        if (bIsWKT2)
6092
0
        {
6093
0
            int iChild = FindProjParm(
6094
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6095
0
            if (iChild == -1)
6096
0
                iChild = FindProjParm(
6097
0
                    EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
6098
0
            return iChild;
6099
0
        }
6100
0
        return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
6101
0
    }
6102
6103
0
    if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
6104
0
    {
6105
0
        if (bIsWKT2)
6106
0
        {
6107
0
            int iChild = FindProjParm(
6108
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
6109
0
            if (iChild == -1)
6110
0
                iChild = FindProjParm(
6111
0
                    EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
6112
0
            return iChild;
6113
0
        }
6114
0
        int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
6115
0
        if (iChild == -1)
6116
0
            iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
6117
0
        return iChild;
6118
0
    }
6119
6120
0
    return -1;
6121
0
}
6122
6123
/************************************************************************/
6124
/*                            GetProjParm()                             */
6125
/************************************************************************/
6126
6127
/**
6128
 * \brief Fetch a projection parameter value.
6129
 *
6130
 * NOTE: This code should be modified to translate non degree angles into
6131
 * degrees based on the GEOGCS unit.  This has not yet been done.
6132
 *
6133
 * This method is the same as the C function OSRGetProjParm().
6134
 *
6135
 * @param pszName the name of the parameter to fetch, from the set of
6136
 * SRS_PP codes in ogr_srs_api.h.
6137
 *
6138
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6139
 *
6140
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6141
 *
6142
 * @return value of parameter.
6143
 */
6144
6145
double OGRSpatialReference::GetProjParm(const char *pszName,
6146
                                        double dfDefaultValue,
6147
                                        OGRErr *pnErr) const
6148
6149
0
{
6150
0
    TAKE_OPTIONAL_LOCK();
6151
6152
0
    d->refreshProjObj();
6153
0
    GetRoot();  // force update of d->m_bNodesWKT2
6154
6155
0
    if (pnErr != nullptr)
6156
0
        *pnErr = OGRERR_NONE;
6157
6158
    /* -------------------------------------------------------------------- */
6159
    /*      Find the desired parameter.                                     */
6160
    /* -------------------------------------------------------------------- */
6161
0
    const OGR_SRSNode *poPROJCS =
6162
0
        GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6163
0
    if (poPROJCS == nullptr)
6164
0
    {
6165
0
        if (pnErr != nullptr)
6166
0
            *pnErr = OGRERR_FAILURE;
6167
0
        return dfDefaultValue;
6168
0
    }
6169
6170
0
    const int iChild = FindProjParm(pszName, poPROJCS);
6171
0
    if (iChild == -1)
6172
0
    {
6173
0
        if (IsProjected() && GetAxesCount() == 3)
6174
0
        {
6175
0
            OGRSpatialReference *poSRSTmp = Clone();
6176
0
            poSRSTmp->DemoteTo2D(nullptr);
6177
0
            const double dfRet =
6178
0
                poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6179
0
            delete poSRSTmp;
6180
0
            return dfRet;
6181
0
        }
6182
6183
0
        if (pnErr != nullptr)
6184
0
            *pnErr = OGRERR_FAILURE;
6185
0
        return dfDefaultValue;
6186
0
    }
6187
6188
0
    const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6189
0
    return CPLAtof(poParameter->GetChild(1)->GetValue());
6190
0
}
6191
6192
/************************************************************************/
6193
/*                           OSRGetProjParm()                           */
6194
/************************************************************************/
6195
6196
/**
6197
 * \brief Fetch a projection parameter value.
6198
 *
6199
 * This function is the same as OGRSpatialReference::GetProjParm()
6200
 */
6201
double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6202
                      double dfDefaultValue, OGRErr *pnErr)
6203
6204
0
{
6205
0
    VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6206
6207
0
    return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6208
0
}
6209
6210
/************************************************************************/
6211
/*                          GetNormProjParm()                           */
6212
/************************************************************************/
6213
6214
/**
6215
 * \brief Fetch a normalized projection parameter value.
6216
 *
6217
 * This method is the same as GetProjParm() except that the value of
6218
 * the parameter is "normalized" into degrees or meters depending on
6219
 * whether it is linear or angular.
6220
 *
6221
 * This method is the same as the C function OSRGetNormProjParm().
6222
 *
6223
 * @param pszName the name of the parameter to fetch, from the set of
6224
 * SRS_PP codes in ogr_srs_api.h.
6225
 *
6226
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6227
 *
6228
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6229
 *
6230
 * @return value of parameter.
6231
 */
6232
6233
double OGRSpatialReference::GetNormProjParm(const char *pszName,
6234
                                            double dfDefaultValue,
6235
                                            OGRErr *pnErr) const
6236
6237
0
{
6238
0
    TAKE_OPTIONAL_LOCK();
6239
6240
0
    GetNormInfo();
6241
6242
0
    OGRErr nError = OGRERR_NONE;
6243
0
    double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6244
0
    if (pnErr != nullptr)
6245
0
        *pnErr = nError;
6246
6247
    // If we got the default just return it unadjusted.
6248
0
    if (nError != OGRERR_NONE)
6249
0
        return dfRawResult;
6250
6251
0
    if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6252
0
        dfRawResult *= d->dfToDegrees;
6253
6254
0
    if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6255
0
        return dfRawResult * d->dfToMeter;
6256
6257
0
    return dfRawResult;
6258
0
}
6259
6260
/************************************************************************/
6261
/*                         OSRGetNormProjParm()                         */
6262
/************************************************************************/
6263
6264
/**
6265
 * \brief This function is the same as OGRSpatialReference::
6266
 *
6267
 * This function is the same as OGRSpatialReference::GetNormProjParm()
6268
 */
6269
double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6270
                          double dfDefaultValue, OGRErr *pnErr)
6271
6272
0
{
6273
0
    VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6274
6275
0
    return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6276
0
}
6277
6278
/************************************************************************/
6279
/*                          SetNormProjParm()                           */
6280
/************************************************************************/
6281
6282
/**
6283
 * \brief Set a projection parameter with a normalized value.
6284
 *
6285
 * This method is the same as SetProjParm() except that the value of
6286
 * the parameter passed in is assumed to be in "normalized" form (decimal
6287
 * degrees for angular values, meters for linear values.  The values are
6288
 * converted in a form suitable for the GEOGCS and linear units in effect.
6289
 *
6290
 * This method is the same as the C function OSRSetNormProjParm().
6291
 *
6292
 * @param pszName the parameter name, which should be selected from
6293
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6294
 *
6295
 * @param dfValue value to assign.
6296
 *
6297
 * @return OGRERR_NONE on success.
6298
 */
6299
6300
OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6301
6302
0
{
6303
0
    TAKE_OPTIONAL_LOCK();
6304
6305
0
    GetNormInfo();
6306
6307
0
    if (d->dfToDegrees != 0.0 &&
6308
0
        (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6309
0
        IsAngularParameter(pszName))
6310
0
    {
6311
0
        dfValue /= d->dfToDegrees;
6312
0
    }
6313
0
    else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6314
0
             IsLinearParameter(pszName))
6315
0
        dfValue /= d->dfToMeter;
6316
6317
0
    return SetProjParm(pszName, dfValue);
6318
0
}
6319
6320
/************************************************************************/
6321
/*                         OSRSetNormProjParm()                         */
6322
/************************************************************************/
6323
6324
/**
6325
 * \brief Set a projection parameter with a normalized value.
6326
 *
6327
 * This function is the same as OGRSpatialReference::SetNormProjParm()
6328
 */
6329
OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6330
                          double dfValue)
6331
6332
0
{
6333
0
    VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6334
6335
0
    return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6336
0
}
6337
6338
/************************************************************************/
6339
/*                               SetTM()                                */
6340
/************************************************************************/
6341
6342
OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6343
                                  double dfScale, double dfFalseEasting,
6344
                                  double dfFalseNorthing)
6345
6346
0
{
6347
0
    TAKE_OPTIONAL_LOCK();
6348
6349
0
    return d->replaceConversionAndUnref(
6350
0
        proj_create_conversion_transverse_mercator(
6351
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6352
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6353
0
}
6354
6355
/************************************************************************/
6356
/*                              OSRSetTM()                              */
6357
/************************************************************************/
6358
6359
OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6360
                double dfCenterLong, double dfScale, double dfFalseEasting,
6361
                double dfFalseNorthing)
6362
6363
0
{
6364
0
    VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6365
6366
0
    return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6367
0
                                  dfFalseEasting, dfFalseNorthing);
6368
0
}
6369
6370
/************************************************************************/
6371
/*                            SetTMVariant()                            */
6372
/************************************************************************/
6373
6374
OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6375
                                         double dfCenterLat,
6376
                                         double dfCenterLong, double dfScale,
6377
                                         double dfFalseEasting,
6378
                                         double dfFalseNorthing)
6379
6380
0
{
6381
0
    TAKE_OPTIONAL_LOCK();
6382
6383
0
    SetProjection(pszVariantName);
6384
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6385
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6386
0
    SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6387
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6388
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6389
6390
0
    return OGRERR_NONE;
6391
0
}
6392
6393
/************************************************************************/
6394
/*                          OSRSetTMVariant()                           */
6395
/************************************************************************/
6396
6397
OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6398
                       double dfCenterLat, double dfCenterLong, double dfScale,
6399
                       double dfFalseEasting, double dfFalseNorthing)
6400
6401
0
{
6402
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6403
6404
0
    return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6405
0
                                         dfCenterLong, dfScale, dfFalseEasting,
6406
0
                                         dfFalseNorthing);
6407
0
}
6408
6409
/************************************************************************/
6410
/*                              SetTMSO()                               */
6411
/************************************************************************/
6412
6413
OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6414
                                    double dfScale, double dfFalseEasting,
6415
                                    double dfFalseNorthing)
6416
6417
0
{
6418
0
    TAKE_OPTIONAL_LOCK();
6419
6420
0
    auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6421
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6422
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6423
6424
0
    const char *pszName = nullptr;
6425
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6426
0
    CPLString osName = pszName ? pszName : "";
6427
6428
0
    d->refreshProjObj();
6429
6430
0
    d->demoteFromBoundCRS();
6431
6432
0
    auto cs = proj_create_cartesian_2D_cs(
6433
0
        d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6434
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6435
0
    auto projCRS =
6436
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6437
0
                                  d->getGeodBaseCRS(), conv, cs);
6438
0
    proj_destroy(conv);
6439
0
    proj_destroy(cs);
6440
6441
0
    d->setPjCRS(projCRS);
6442
6443
0
    d->undoDemoteFromBoundCRS();
6444
6445
0
    return OGRERR_NONE;
6446
0
}
6447
6448
/************************************************************************/
6449
/*                             OSRSetTMSO()                             */
6450
/************************************************************************/
6451
6452
OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6453
                  double dfCenterLong, double dfScale, double dfFalseEasting,
6454
                  double dfFalseNorthing)
6455
6456
0
{
6457
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6458
6459
0
    return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6460
0
                                    dfFalseEasting, dfFalseNorthing);
6461
0
}
6462
6463
/************************************************************************/
6464
/*                              SetTPED()                               */
6465
/************************************************************************/
6466
6467
OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6468
                                    double dfLat2, double dfLong2,
6469
                                    double dfFalseEasting,
6470
                                    double dfFalseNorthing)
6471
6472
0
{
6473
0
    TAKE_OPTIONAL_LOCK();
6474
6475
0
    return d->replaceConversionAndUnref(
6476
0
        proj_create_conversion_two_point_equidistant(
6477
0
            d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6478
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6479
0
}
6480
6481
/************************************************************************/
6482
/*                             OSRSetTPED()                             */
6483
/************************************************************************/
6484
6485
OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6486
                  double dfLat2, double dfLong2, double dfFalseEasting,
6487
                  double dfFalseNorthing)
6488
6489
0
{
6490
0
    VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6491
6492
0
    return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6493
0
                                    dfFalseEasting, dfFalseNorthing);
6494
0
}
6495
6496
/************************************************************************/
6497
/*                               SetTMG()                               */
6498
/************************************************************************/
6499
6500
OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6501
                                   double dfFalseEasting,
6502
                                   double dfFalseNorthing)
6503
6504
0
{
6505
0
    TAKE_OPTIONAL_LOCK();
6506
6507
0
    return d->replaceConversionAndUnref(
6508
0
        proj_create_conversion_tunisia_mapping_grid(
6509
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6510
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6511
0
}
6512
6513
/************************************************************************/
6514
/*                             OSRSetTMG()                              */
6515
/************************************************************************/
6516
6517
OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6518
                 double dfCenterLong, double dfFalseEasting,
6519
                 double dfFalseNorthing)
6520
6521
0
{
6522
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6523
6524
0
    return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6525
0
                                   dfFalseNorthing);
6526
0
}
6527
6528
/************************************************************************/
6529
/*                              SetACEA()                               */
6530
/************************************************************************/
6531
6532
OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6533
                                    double dfCenterLat, double dfCenterLong,
6534
                                    double dfFalseEasting,
6535
                                    double dfFalseNorthing)
6536
6537
0
{
6538
0
    TAKE_OPTIONAL_LOCK();
6539
6540
    // Note different order of parameters. The one in PROJ is conformant with
6541
    // EPSG
6542
0
    return d->replaceConversionAndUnref(
6543
0
        proj_create_conversion_albers_equal_area(
6544
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6545
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6546
0
}
6547
6548
/************************************************************************/
6549
/*                             OSRSetACEA()                             */
6550
/************************************************************************/
6551
6552
OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6553
                  double dfCenterLat, double dfCenterLong,
6554
                  double dfFalseEasting, double dfFalseNorthing)
6555
6556
0
{
6557
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6558
6559
0
    return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6560
0
                                    dfFalseEasting, dfFalseNorthing);
6561
0
}
6562
6563
/************************************************************************/
6564
/*                               SetAE()                                */
6565
/************************************************************************/
6566
6567
OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6568
                                  double dfFalseEasting, double dfFalseNorthing)
6569
6570
0
{
6571
0
    TAKE_OPTIONAL_LOCK();
6572
6573
0
    return d->replaceConversionAndUnref(
6574
0
        proj_create_conversion_azimuthal_equidistant(
6575
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6576
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6577
0
}
6578
6579
/************************************************************************/
6580
/*                              OSRSetAE()                              */
6581
/************************************************************************/
6582
6583
OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6584
                double dfCenterLong, double dfFalseEasting,
6585
                double dfFalseNorthing)
6586
6587
0
{
6588
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6589
6590
0
    return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6591
0
                                  dfFalseNorthing);
6592
0
}
6593
6594
/************************************************************************/
6595
/*                              SetBonne()                              */
6596
/************************************************************************/
6597
6598
OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6599
                                     double dfFalseEasting,
6600
                                     double dfFalseNorthing)
6601
6602
0
{
6603
0
    TAKE_OPTIONAL_LOCK();
6604
6605
0
    return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6606
0
        d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6607
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6608
0
}
6609
6610
/************************************************************************/
6611
/*                            OSRSetBonne()                             */
6612
/************************************************************************/
6613
6614
OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6615
                   double dfCentralMeridian, double dfFalseEasting,
6616
                   double dfFalseNorthing)
6617
6618
0
{
6619
0
    VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6620
6621
0
    return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6622
0
                                     dfFalseNorthing);
6623
0
}
6624
6625
/************************************************************************/
6626
/*                               SetCEA()                               */
6627
/************************************************************************/
6628
6629
OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6630
                                   double dfFalseEasting,
6631
                                   double dfFalseNorthing)
6632
6633
0
{
6634
0
    TAKE_OPTIONAL_LOCK();
6635
6636
0
    return d->replaceConversionAndUnref(
6637
0
        proj_create_conversion_lambert_cylindrical_equal_area(
6638
0
            d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6639
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6640
0
}
6641
6642
/************************************************************************/
6643
/*                             OSRSetCEA()                              */
6644
/************************************************************************/
6645
6646
OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6647
                 double dfCentralMeridian, double dfFalseEasting,
6648
                 double dfFalseNorthing)
6649
6650
0
{
6651
0
    VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6652
6653
0
    return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6654
0
                                   dfFalseNorthing);
6655
0
}
6656
6657
/************************************************************************/
6658
/*                               SetCS()                                */
6659
/************************************************************************/
6660
6661
OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6662
                                  double dfFalseEasting, double dfFalseNorthing)
6663
6664
0
{
6665
0
    TAKE_OPTIONAL_LOCK();
6666
6667
0
    return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6668
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6669
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6670
0
}
6671
6672
/************************************************************************/
6673
/*                              OSRSetCS()                              */
6674
/************************************************************************/
6675
6676
OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6677
                double dfCenterLong, double dfFalseEasting,
6678
                double dfFalseNorthing)
6679
6680
0
{
6681
0
    VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6682
6683
0
    return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6684
0
                                  dfFalseNorthing);
6685
0
}
6686
6687
/************************************************************************/
6688
/*                               SetEC()                                */
6689
/************************************************************************/
6690
6691
OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6692
                                  double dfCenterLat, double dfCenterLong,
6693
                                  double dfFalseEasting, double dfFalseNorthing)
6694
6695
0
{
6696
0
    TAKE_OPTIONAL_LOCK();
6697
6698
    // Note: different order of arguments
6699
0
    return d->replaceConversionAndUnref(
6700
0
        proj_create_conversion_equidistant_conic(
6701
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6702
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6703
0
}
6704
6705
/************************************************************************/
6706
/*                              OSRSetEC()                              */
6707
/************************************************************************/
6708
6709
OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6710
                double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6711
                double dfFalseNorthing)
6712
6713
0
{
6714
0
    VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6715
6716
0
    return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6717
0
                                  dfFalseEasting, dfFalseNorthing);
6718
0
}
6719
6720
/************************************************************************/
6721
/*                             SetEckert()                              */
6722
/************************************************************************/
6723
6724
OGRErr OGRSpatialReference::SetEckert(int nVariation,  // 1-6.
6725
                                      double dfCentralMeridian,
6726
                                      double dfFalseEasting,
6727
                                      double dfFalseNorthing)
6728
6729
0
{
6730
0
    TAKE_OPTIONAL_LOCK();
6731
6732
0
    PJ *conv;
6733
0
    if (nVariation == 1)
6734
0
    {
6735
0
        conv = proj_create_conversion_eckert_i(
6736
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6737
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6738
0
    }
6739
0
    else if (nVariation == 2)
6740
0
    {
6741
0
        conv = proj_create_conversion_eckert_ii(
6742
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6743
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6744
0
    }
6745
0
    else if (nVariation == 3)
6746
0
    {
6747
0
        conv = proj_create_conversion_eckert_iii(
6748
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6749
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6750
0
    }
6751
0
    else if (nVariation == 4)
6752
0
    {
6753
0
        conv = proj_create_conversion_eckert_iv(
6754
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6755
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6756
0
    }
6757
0
    else if (nVariation == 5)
6758
0
    {
6759
0
        conv = proj_create_conversion_eckert_v(
6760
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6761
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6762
0
    }
6763
0
    else if (nVariation == 6)
6764
0
    {
6765
0
        conv = proj_create_conversion_eckert_vi(
6766
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6767
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6768
0
    }
6769
0
    else
6770
0
    {
6771
0
        CPLError(CE_Failure, CPLE_AppDefined,
6772
0
                 "Unsupported Eckert variation (%d).", nVariation);
6773
0
        return OGRERR_UNSUPPORTED_SRS;
6774
0
    }
6775
6776
0
    return d->replaceConversionAndUnref(conv);
6777
0
}
6778
6779
/************************************************************************/
6780
/*                            OSRSetEckert()                            */
6781
/************************************************************************/
6782
6783
OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6784
                    double dfCentralMeridian, double dfFalseEasting,
6785
                    double dfFalseNorthing)
6786
6787
0
{
6788
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6789
6790
0
    return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6791
0
                                      dfFalseEasting, dfFalseNorthing);
6792
0
}
6793
6794
/************************************************************************/
6795
/*                            SetEckertIV()                             */
6796
/*                                                                      */
6797
/*      Deprecated                                                      */
6798
/************************************************************************/
6799
6800
OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6801
                                        double dfFalseEasting,
6802
                                        double dfFalseNorthing)
6803
6804
0
{
6805
0
    return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6806
0
}
6807
6808
/************************************************************************/
6809
/*                           OSRSetEckertIV()                           */
6810
/************************************************************************/
6811
6812
OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6813
                      double dfFalseEasting, double dfFalseNorthing)
6814
6815
0
{
6816
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6817
6818
0
    return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6819
0
                                        dfFalseNorthing);
6820
0
}
6821
6822
/************************************************************************/
6823
/*                            SetEckertVI()                             */
6824
/*                                                                      */
6825
/*      Deprecated                                                      */
6826
/************************************************************************/
6827
6828
OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6829
                                        double dfFalseEasting,
6830
                                        double dfFalseNorthing)
6831
6832
0
{
6833
0
    return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6834
0
}
6835
6836
/************************************************************************/
6837
/*                           OSRSetEckertVI()                           */
6838
/************************************************************************/
6839
6840
OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6841
                      double dfFalseEasting, double dfFalseNorthing)
6842
6843
0
{
6844
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6845
6846
0
    return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6847
0
                                        dfFalseNorthing);
6848
0
}
6849
6850
/************************************************************************/
6851
/*                         SetEquirectangular()                         */
6852
/************************************************************************/
6853
6854
OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6855
                                               double dfCenterLong,
6856
                                               double dfFalseEasting,
6857
                                               double dfFalseNorthing)
6858
6859
0
{
6860
0
    TAKE_OPTIONAL_LOCK();
6861
6862
0
    if (dfCenterLat == 0.0)
6863
0
    {
6864
0
        return d->replaceConversionAndUnref(
6865
0
            proj_create_conversion_equidistant_cylindrical(
6866
0
                d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6867
0
                dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6868
0
    }
6869
6870
    // Non-standard extension with non-zero latitude of origin
6871
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6872
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6873
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6874
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6875
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6876
6877
0
    return OGRERR_NONE;
6878
0
}
6879
6880
/************************************************************************/
6881
/*                       OSRSetEquirectangular()                        */
6882
/************************************************************************/
6883
6884
OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6885
                             double dfCenterLong, double dfFalseEasting,
6886
                             double dfFalseNorthing)
6887
6888
0
{
6889
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6890
6891
0
    return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6892
0
                                               dfFalseEasting, dfFalseNorthing);
6893
0
}
6894
6895
/************************************************************************/
6896
/*                         SetEquirectangular2()                        */
6897
/* Generalized form                                                     */
6898
/************************************************************************/
6899
6900
OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6901
                                                double dfCenterLong,
6902
                                                double dfStdParallel1,
6903
                                                double dfFalseEasting,
6904
                                                double dfFalseNorthing)
6905
6906
0
{
6907
0
    TAKE_OPTIONAL_LOCK();
6908
6909
0
    if (dfCenterLat == 0.0)
6910
0
    {
6911
0
        return d->replaceConversionAndUnref(
6912
0
            proj_create_conversion_equidistant_cylindrical(
6913
0
                d->getPROJContext(), dfStdParallel1, dfCenterLong,
6914
0
                dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6915
0
    }
6916
6917
    // Non-standard extension with non-zero latitude of origin
6918
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6919
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6920
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6921
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6922
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6923
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6924
6925
0
    return OGRERR_NONE;
6926
0
}
6927
6928
/************************************************************************/
6929
/*                       OSRSetEquirectangular2()                       */
6930
/************************************************************************/
6931
6932
OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6933
                              double dfCenterLong, double dfStdParallel1,
6934
                              double dfFalseEasting, double dfFalseNorthing)
6935
6936
0
{
6937
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6938
6939
0
    return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6940
0
                                                dfStdParallel1, dfFalseEasting,
6941
0
                                                dfFalseNorthing);
6942
0
}
6943
6944
/************************************************************************/
6945
/*                               SetGS()                                */
6946
/************************************************************************/
6947
6948
OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6949
                                  double dfFalseEasting, double dfFalseNorthing)
6950
6951
0
{
6952
0
    return d->replaceConversionAndUnref(proj_create_conversion_gall(
6953
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6954
0
        nullptr, 0.0, nullptr, 0.0));
6955
0
}
6956
6957
/************************************************************************/
6958
/*                              OSRSetGS()                              */
6959
/************************************************************************/
6960
6961
OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6962
                double dfFalseEasting, double dfFalseNorthing)
6963
6964
0
{
6965
0
    VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6966
6967
0
    return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6968
0
                                  dfFalseNorthing);
6969
0
}
6970
6971
/************************************************************************/
6972
/*                               SetGH()                                */
6973
/************************************************************************/
6974
6975
OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6976
                                  double dfFalseEasting, double dfFalseNorthing)
6977
6978
0
{
6979
0
    TAKE_OPTIONAL_LOCK();
6980
6981
0
    return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6982
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6983
0
        nullptr, 0.0, nullptr, 0.0));
6984
0
}
6985
6986
/************************************************************************/
6987
/*                              OSRSetGH()                              */
6988
/************************************************************************/
6989
6990
OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6991
                double dfFalseEasting, double dfFalseNorthing)
6992
6993
0
{
6994
0
    VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6995
6996
0
    return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6997
0
                                  dfFalseNorthing);
6998
0
}
6999
7000
/************************************************************************/
7001
/*                              SetIGH()                                */
7002
/************************************************************************/
7003
7004
OGRErr OGRSpatialReference::SetIGH()
7005
7006
0
{
7007
0
    TAKE_OPTIONAL_LOCK();
7008
7009
0
    return d->replaceConversionAndUnref(
7010
0
        proj_create_conversion_interrupted_goode_homolosine(
7011
0
            d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
7012
0
}
7013
7014
/************************************************************************/
7015
/*                              OSRSetIGH()                             */
7016
/************************************************************************/
7017
7018
OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
7019
7020
0
{
7021
0
    VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
7022
7023
0
    return ToPointer(hSRS)->SetIGH();
7024
0
}
7025
7026
/************************************************************************/
7027
/*                              SetGEOS()                               */
7028
/************************************************************************/
7029
7030
OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
7031
                                    double dfSatelliteHeight,
7032
                                    double dfFalseEasting,
7033
                                    double dfFalseNorthing)
7034
7035
0
{
7036
0
    TAKE_OPTIONAL_LOCK();
7037
7038
0
    return d->replaceConversionAndUnref(
7039
0
        proj_create_conversion_geostationary_satellite_sweep_y(
7040
0
            d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
7041
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7042
0
}
7043
7044
/************************************************************************/
7045
/*                              OSRSetGEOS()                             */
7046
/************************************************************************/
7047
7048
OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7049
                  double dfSatelliteHeight, double dfFalseEasting,
7050
                  double dfFalseNorthing)
7051
7052
0
{
7053
0
    VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
7054
7055
0
    return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
7056
0
                                    dfFalseEasting, dfFalseNorthing);
7057
0
}
7058
7059
/************************************************************************/
7060
/*                       SetGaussSchreiberTMercator()                   */
7061
/************************************************************************/
7062
7063
OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
7064
                                                       double dfCenterLong,
7065
                                                       double dfScale,
7066
                                                       double dfFalseEasting,
7067
                                                       double dfFalseNorthing)
7068
7069
0
{
7070
0
    TAKE_OPTIONAL_LOCK();
7071
7072
0
    return d->replaceConversionAndUnref(
7073
0
        proj_create_conversion_gauss_schreiber_transverse_mercator(
7074
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7075
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7076
0
}
7077
7078
/************************************************************************/
7079
/*                     OSRSetGaussSchreiberTMercator()                  */
7080
/************************************************************************/
7081
7082
OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
7083
                                     double dfCenterLat, double dfCenterLong,
7084
                                     double dfScale, double dfFalseEasting,
7085
                                     double dfFalseNorthing)
7086
7087
0
{
7088
0
    VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
7089
7090
0
    return ToPointer(hSRS)->SetGaussSchreiberTMercator(
7091
0
        dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
7092
0
}
7093
7094
/************************************************************************/
7095
/*                            SetGnomonic()                             */
7096
/************************************************************************/
7097
7098
OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
7099
                                        double dfFalseEasting,
7100
                                        double dfFalseNorthing)
7101
7102
0
{
7103
0
    TAKE_OPTIONAL_LOCK();
7104
7105
0
    return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
7106
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7107
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7108
0
}
7109
7110
/************************************************************************/
7111
/*                           OSRSetGnomonic()                           */
7112
/************************************************************************/
7113
7114
OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7115
                      double dfCenterLong, double dfFalseEasting,
7116
                      double dfFalseNorthing)
7117
7118
0
{
7119
0
    VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
7120
7121
0
    return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
7122
0
                                        dfFalseEasting, dfFalseNorthing);
7123
0
}
7124
7125
/************************************************************************/
7126
/*                              SetHOMAC()                              */
7127
/************************************************************************/
7128
7129
/**
7130
 * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7131
 * azimuth angle.
7132
 *
7133
 * This projection corresponds to EPSG projection method 9815, also
7134
 * sometimes known as hotine oblique mercator (variant B).
7135
 *
7136
 * This method does the same thing as the C function OSRSetHOMAC().
7137
 *
7138
 * @param dfCenterLat Latitude of the projection origin.
7139
 * @param dfCenterLong Longitude of the projection origin.
7140
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7141
 * centerline.
7142
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7143
 * @param dfScale Scale factor applies to the projection origin.
7144
 * @param dfFalseEasting False easting.
7145
 * @param dfFalseNorthing False northing.
7146
 *
7147
 * @return OGRERR_NONE on success.
7148
 */
7149
7150
OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7151
                                     double dfAzimuth, double dfRectToSkew,
7152
                                     double dfScale, double dfFalseEasting,
7153
                                     double dfFalseNorthing)
7154
7155
0
{
7156
0
    TAKE_OPTIONAL_LOCK();
7157
7158
0
    return d->replaceConversionAndUnref(
7159
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7160
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7161
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7162
0
            0.0, nullptr, 0.0));
7163
0
}
7164
7165
/************************************************************************/
7166
/*                            OSRSetHOMAC()                             */
7167
/************************************************************************/
7168
7169
/**
7170
 * \brief Set an Oblique Mercator projection using azimuth angle.
7171
 *
7172
 * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7173
 */
7174
OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7175
                   double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7176
                   double dfScale, double dfFalseEasting,
7177
                   double dfFalseNorthing)
7178
7179
0
{
7180
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7181
7182
0
    return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7183
0
                                     dfRectToSkew, dfScale, dfFalseEasting,
7184
0
                                     dfFalseNorthing);
7185
0
}
7186
7187
/************************************************************************/
7188
/*                               SetHOM()                               */
7189
/************************************************************************/
7190
7191
/**
7192
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7193
 *
7194
 * This projection corresponds to EPSG projection method 9812, also
7195
 * sometimes known as hotine oblique mercator (variant A)..
7196
 *
7197
 * This method does the same thing as the C function OSRSetHOM().
7198
 *
7199
 * @param dfCenterLat Latitude of the projection origin.
7200
 * @param dfCenterLong Longitude of the projection origin.
7201
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7202
 * centerline.
7203
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7204
 * @param dfScale Scale factor applies to the projection origin.
7205
 * @param dfFalseEasting False easting.
7206
 * @param dfFalseNorthing False northing.
7207
 *
7208
 * @return OGRERR_NONE on success.
7209
 */
7210
7211
OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7212
                                   double dfAzimuth, double dfRectToSkew,
7213
                                   double dfScale, double dfFalseEasting,
7214
                                   double dfFalseNorthing)
7215
7216
0
{
7217
0
    TAKE_OPTIONAL_LOCK();
7218
7219
0
    return d->replaceConversionAndUnref(
7220
0
        proj_create_conversion_hotine_oblique_mercator_variant_a(
7221
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7222
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7223
0
            0.0, nullptr, 0.0));
7224
0
}
7225
7226
/************************************************************************/
7227
/*                             OSRSetHOM()                              */
7228
/************************************************************************/
7229
/**
7230
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7231
 *
7232
 * This is the same as the C++ method OGRSpatialReference::SetHOM()
7233
 */
7234
OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7235
                 double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7236
                 double dfScale, double dfFalseEasting, double dfFalseNorthing)
7237
7238
0
{
7239
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7240
7241
0
    return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7242
0
                                   dfRectToSkew, dfScale, dfFalseEasting,
7243
0
                                   dfFalseNorthing);
7244
0
}
7245
7246
/************************************************************************/
7247
/*                             SetHOM2PNO()                             */
7248
/************************************************************************/
7249
7250
/**
7251
 * \brief Set a Hotine Oblique Mercator projection using two points on
7252
 * projection centerline.
7253
 *
7254
 * This method does the same thing as the C function OSRSetHOM2PNO().
7255
 *
7256
 * @param dfCenterLat Latitude of the projection origin.
7257
 * @param dfLat1 Latitude of the first point on center line.
7258
 * @param dfLong1 Longitude of the first point on center line.
7259
 * @param dfLat2 Latitude of the second point on center line.
7260
 * @param dfLong2 Longitude of the second point on center line.
7261
 * @param dfScale Scale factor applies to the projection origin.
7262
 * @param dfFalseEasting False easting.
7263
 * @param dfFalseNorthing False northing.
7264
 *
7265
 * @return OGRERR_NONE on success.
7266
 */
7267
7268
OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7269
                                       double dfLong1, double dfLat2,
7270
                                       double dfLong2, double dfScale,
7271
                                       double dfFalseEasting,
7272
                                       double dfFalseNorthing)
7273
7274
0
{
7275
0
    TAKE_OPTIONAL_LOCK();
7276
7277
0
    return d->replaceConversionAndUnref(
7278
0
        proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7279
0
            d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7280
0
            dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7281
0
            0.0));
7282
0
}
7283
7284
/************************************************************************/
7285
/*                           OSRSetHOM2PNO()                            */
7286
/************************************************************************/
7287
/**
7288
 * \brief Set a Hotine Oblique Mercator projection using two points on
7289
 *  projection centerline.
7290
 *
7291
 * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7292
 */
7293
OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7294
                     double dfLat1, double dfLong1, double dfLat2,
7295
                     double dfLong2, double dfScale, double dfFalseEasting,
7296
                     double dfFalseNorthing)
7297
7298
0
{
7299
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7300
7301
0
    return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7302
0
                                       dfLong2, dfScale, dfFalseEasting,
7303
0
                                       dfFalseNorthing);
7304
0
}
7305
7306
/************************************************************************/
7307
/*                               SetLOM()                               */
7308
/************************************************************************/
7309
7310
/**
7311
 * \brief Set a Laborde Oblique Mercator projection.
7312
 *
7313
 * @param dfCenterLat Latitude of the projection origin.
7314
 * @param dfCenterLong Longitude of the projection origin.
7315
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7316
 * centerline.
7317
 * @param dfScale Scale factor on the initiali line
7318
 * @param dfFalseEasting False easting.
7319
 * @param dfFalseNorthing False northing.
7320
 *
7321
 * @return OGRERR_NONE on success.
7322
 */
7323
7324
OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7325
                                   double dfAzimuth, double dfScale,
7326
                                   double dfFalseEasting,
7327
                                   double dfFalseNorthing)
7328
7329
0
{
7330
0
    TAKE_OPTIONAL_LOCK();
7331
7332
0
    return d->replaceConversionAndUnref(
7333
0
        proj_create_conversion_laborde_oblique_mercator(
7334
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7335
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7336
0
}
7337
7338
/************************************************************************/
7339
/*                            SetIWMPolyconic()                         */
7340
/************************************************************************/
7341
7342
OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7343
                                            double dfCenterLong,
7344
                                            double dfFalseEasting,
7345
                                            double dfFalseNorthing)
7346
7347
0
{
7348
0
    TAKE_OPTIONAL_LOCK();
7349
7350
0
    return d->replaceConversionAndUnref(
7351
0
        proj_create_conversion_international_map_world_polyconic(
7352
0
            d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7353
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7354
0
}
7355
7356
/************************************************************************/
7357
/*                          OSRSetIWMPolyconic()                        */
7358
/************************************************************************/
7359
7360
OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7361
                          double dfLat2, double dfCenterLong,
7362
                          double dfFalseEasting, double dfFalseNorthing)
7363
7364
0
{
7365
0
    VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7366
7367
0
    return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7368
0
                                            dfFalseEasting, dfFalseNorthing);
7369
0
}
7370
7371
/************************************************************************/
7372
/*                             SetKrovak()                              */
7373
/************************************************************************/
7374
7375
/** Krovak east-north projection.
7376
 *
7377
 * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7378
 * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7379
 */
7380
OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7381
                                      double dfAzimuth,
7382
                                      double dfPseudoStdParallel1,
7383
                                      double dfScale, double dfFalseEasting,
7384
                                      double dfFalseNorthing)
7385
7386
0
{
7387
0
    TAKE_OPTIONAL_LOCK();
7388
7389
0
    return d->replaceConversionAndUnref(
7390
0
        proj_create_conversion_krovak_north_oriented(
7391
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7392
0
            dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7393
0
            nullptr, 0.0, nullptr, 0.0));
7394
0
}
7395
7396
/************************************************************************/
7397
/*                            OSRSetKrovak()                            */
7398
/************************************************************************/
7399
7400
OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7401
                    double dfCenterLong, double dfAzimuth,
7402
                    double dfPseudoStdParallel1, double dfScale,
7403
                    double dfFalseEasting, double dfFalseNorthing)
7404
7405
0
{
7406
0
    VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7407
7408
0
    return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7409
0
                                      dfPseudoStdParallel1, dfScale,
7410
0
                                      dfFalseEasting, dfFalseNorthing);
7411
0
}
7412
7413
/************************************************************************/
7414
/*                              SetLAEA()                               */
7415
/************************************************************************/
7416
7417
OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7418
                                    double dfFalseEasting,
7419
                                    double dfFalseNorthing)
7420
7421
0
{
7422
0
    TAKE_OPTIONAL_LOCK();
7423
7424
0
    auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7425
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7426
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7427
7428
0
    const char *pszName = nullptr;
7429
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7430
0
    CPLString osName = pszName ? pszName : "";
7431
7432
0
    d->refreshProjObj();
7433
7434
0
    d->demoteFromBoundCRS();
7435
7436
0
    auto cs = proj_create_cartesian_2D_cs(
7437
0
        d->getPROJContext(),
7438
0
        std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7439
0
            ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7440
0
        : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7441
0
            ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7442
0
            : PJ_CART2D_EASTING_NORTHING,
7443
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7444
0
    auto projCRS =
7445
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7446
0
                                  d->getGeodBaseCRS(), conv, cs);
7447
0
    proj_destroy(conv);
7448
0
    proj_destroy(cs);
7449
7450
0
    d->setPjCRS(projCRS);
7451
7452
0
    d->undoDemoteFromBoundCRS();
7453
7454
0
    return OGRERR_NONE;
7455
0
}
7456
7457
/************************************************************************/
7458
/*                             OSRSetLAEA()                             */
7459
/************************************************************************/
7460
7461
OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7462
                  double dfCenterLong, double dfFalseEasting,
7463
                  double dfFalseNorthing)
7464
7465
0
{
7466
0
    VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7467
7468
0
    return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7469
0
                                    dfFalseNorthing);
7470
0
}
7471
7472
/************************************************************************/
7473
/*                               SetLCC()                               */
7474
/************************************************************************/
7475
7476
OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7477
                                   double dfCenterLat, double dfCenterLong,
7478
                                   double dfFalseEasting,
7479
                                   double dfFalseNorthing)
7480
7481
0
{
7482
0
    TAKE_OPTIONAL_LOCK();
7483
7484
0
    return d->replaceConversionAndUnref(
7485
0
        proj_create_conversion_lambert_conic_conformal_2sp(
7486
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7487
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7488
0
}
7489
7490
/************************************************************************/
7491
/*                             OSRSetLCC()                              */
7492
/************************************************************************/
7493
7494
OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7495
                 double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7496
                 double dfFalseNorthing)
7497
7498
0
{
7499
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7500
7501
0
    return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7502
0
                                   dfFalseEasting, dfFalseNorthing);
7503
0
}
7504
7505
/************************************************************************/
7506
/*                             SetLCC1SP()                              */
7507
/************************************************************************/
7508
7509
OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7510
                                      double dfScale, double dfFalseEasting,
7511
                                      double dfFalseNorthing)
7512
7513
0
{
7514
0
    TAKE_OPTIONAL_LOCK();
7515
7516
0
    return d->replaceConversionAndUnref(
7517
0
        proj_create_conversion_lambert_conic_conformal_1sp(
7518
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7519
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7520
0
}
7521
7522
/************************************************************************/
7523
/*                            OSRSetLCC1SP()                            */
7524
/************************************************************************/
7525
7526
OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7527
                    double dfCenterLong, double dfScale, double dfFalseEasting,
7528
                    double dfFalseNorthing)
7529
7530
0
{
7531
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7532
7533
0
    return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7534
0
                                      dfFalseEasting, dfFalseNorthing);
7535
0
}
7536
7537
/************************************************************************/
7538
/*                              SetLCCB()                               */
7539
/************************************************************************/
7540
7541
OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7542
                                    double dfCenterLat, double dfCenterLong,
7543
                                    double dfFalseEasting,
7544
                                    double dfFalseNorthing)
7545
7546
0
{
7547
0
    TAKE_OPTIONAL_LOCK();
7548
7549
0
    return d->replaceConversionAndUnref(
7550
0
        proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7551
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7552
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7553
0
}
7554
7555
/************************************************************************/
7556
/*                             OSRSetLCCB()                             */
7557
/************************************************************************/
7558
7559
OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7560
                  double dfCenterLat, double dfCenterLong,
7561
                  double dfFalseEasting, double dfFalseNorthing)
7562
7563
0
{
7564
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7565
7566
0
    return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7567
0
                                    dfFalseEasting, dfFalseNorthing);
7568
0
}
7569
7570
/************************************************************************/
7571
/*                               SetMC()                                */
7572
/************************************************************************/
7573
7574
OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7575
                                  double dfFalseEasting, double dfFalseNorthing)
7576
7577
0
{
7578
0
    TAKE_OPTIONAL_LOCK();
7579
7580
0
    (void)dfCenterLat;  // ignored
7581
7582
0
    return d->replaceConversionAndUnref(
7583
0
        proj_create_conversion_miller_cylindrical(
7584
0
            d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7585
0
            nullptr, 0, nullptr, 0));
7586
0
}
7587
7588
/************************************************************************/
7589
/*                              OSRSetMC()                              */
7590
/************************************************************************/
7591
7592
OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7593
                double dfCenterLong, double dfFalseEasting,
7594
                double dfFalseNorthing)
7595
7596
0
{
7597
0
    VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7598
7599
0
    return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7600
0
                                  dfFalseNorthing);
7601
0
}
7602
7603
/************************************************************************/
7604
/*                            SetMercator()                             */
7605
/************************************************************************/
7606
7607
OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7608
                                        double dfScale, double dfFalseEasting,
7609
                                        double dfFalseNorthing)
7610
7611
0
{
7612
0
    TAKE_OPTIONAL_LOCK();
7613
7614
0
    if (dfCenterLat != 0.0 && dfScale == 1.0)
7615
0
    {
7616
        // Not sure this is correct, but this is how it has been used
7617
        // historically
7618
0
        return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7619
0
                              dfFalseNorthing);
7620
0
    }
7621
0
    return d->replaceConversionAndUnref(
7622
0
        proj_create_conversion_mercator_variant_a(
7623
0
            d->getPROJContext(),
7624
0
            dfCenterLat,  // should be zero
7625
0
            dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7626
0
            nullptr, 0));
7627
0
}
7628
7629
/************************************************************************/
7630
/*                           OSRSetMercator()                           */
7631
/************************************************************************/
7632
7633
OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7634
                      double dfCenterLong, double dfScale,
7635
                      double dfFalseEasting, double dfFalseNorthing)
7636
7637
0
{
7638
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7639
7640
0
    return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7641
0
                                        dfFalseEasting, dfFalseNorthing);
7642
0
}
7643
7644
/************************************************************************/
7645
/*                           SetMercator2SP()                           */
7646
/************************************************************************/
7647
7648
OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7649
                                           double dfCenterLong,
7650
                                           double dfFalseEasting,
7651
                                           double dfFalseNorthing)
7652
7653
0
{
7654
0
    if (dfCenterLat == 0.0)
7655
0
    {
7656
0
        return d->replaceConversionAndUnref(
7657
0
            proj_create_conversion_mercator_variant_b(
7658
0
                d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7659
0
                dfFalseNorthing, nullptr, 0, nullptr, 0));
7660
0
    }
7661
7662
0
    TAKE_OPTIONAL_LOCK();
7663
7664
0
    SetProjection(SRS_PT_MERCATOR_2SP);
7665
7666
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7667
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7668
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7669
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7670
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7671
7672
0
    return OGRERR_NONE;
7673
0
}
7674
7675
/************************************************************************/
7676
/*                         OSRSetMercator2SP()                          */
7677
/************************************************************************/
7678
7679
OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7680
                         double dfCenterLat, double dfCenterLong,
7681
                         double dfFalseEasting, double dfFalseNorthing)
7682
7683
0
{
7684
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7685
7686
0
    return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7687
0
                                           dfFalseEasting, dfFalseNorthing);
7688
0
}
7689
7690
/************************************************************************/
7691
/*                            SetMollweide()                            */
7692
/************************************************************************/
7693
7694
OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7695
                                         double dfFalseEasting,
7696
                                         double dfFalseNorthing)
7697
7698
0
{
7699
0
    TAKE_OPTIONAL_LOCK();
7700
7701
0
    return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7702
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7703
0
        nullptr, 0, nullptr, 0));
7704
0
}
7705
7706
/************************************************************************/
7707
/*                          OSRSetMollweide()                           */
7708
/************************************************************************/
7709
7710
OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7711
                       double dfFalseEasting, double dfFalseNorthing)
7712
7713
0
{
7714
0
    VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7715
7716
0
    return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7717
0
                                         dfFalseNorthing);
7718
0
}
7719
7720
/************************************************************************/
7721
/*                              SetNZMG()                               */
7722
/************************************************************************/
7723
7724
OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7725
                                    double dfFalseEasting,
7726
                                    double dfFalseNorthing)
7727
7728
0
{
7729
0
    TAKE_OPTIONAL_LOCK();
7730
7731
0
    return d->replaceConversionAndUnref(
7732
0
        proj_create_conversion_new_zealand_mapping_grid(
7733
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7734
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7735
0
}
7736
7737
/************************************************************************/
7738
/*                             OSRSetNZMG()                             */
7739
/************************************************************************/
7740
7741
OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7742
                  double dfCenterLong, double dfFalseEasting,
7743
                  double dfFalseNorthing)
7744
7745
0
{
7746
0
    VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7747
7748
0
    return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7749
0
                                    dfFalseNorthing);
7750
0
}
7751
7752
/************************************************************************/
7753
/*                               SetOS()                                */
7754
/************************************************************************/
7755
7756
OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7757
                                  double dfScale, double dfFalseEasting,
7758
                                  double dfFalseNorthing)
7759
7760
0
{
7761
0
    TAKE_OPTIONAL_LOCK();
7762
7763
0
    return d->replaceConversionAndUnref(
7764
0
        proj_create_conversion_oblique_stereographic(
7765
0
            d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7766
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7767
0
}
7768
7769
/************************************************************************/
7770
/*                              OSRSetOS()                              */
7771
/************************************************************************/
7772
7773
OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7774
                double dfCMeridian, double dfScale, double dfFalseEasting,
7775
                double dfFalseNorthing)
7776
7777
0
{
7778
0
    VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7779
7780
0
    return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7781
0
                                  dfFalseEasting, dfFalseNorthing);
7782
0
}
7783
7784
/************************************************************************/
7785
/*                          SetOrthographic()                           */
7786
/************************************************************************/
7787
7788
OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7789
                                            double dfCenterLong,
7790
                                            double dfFalseEasting,
7791
                                            double dfFalseNorthing)
7792
7793
0
{
7794
0
    TAKE_OPTIONAL_LOCK();
7795
7796
0
    return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7797
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7798
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7799
0
}
7800
7801
/************************************************************************/
7802
/*                         OSRSetOrthographic()                         */
7803
/************************************************************************/
7804
7805
OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7806
                          double dfCenterLong, double dfFalseEasting,
7807
                          double dfFalseNorthing)
7808
7809
0
{
7810
0
    VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7811
7812
0
    return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7813
0
                                            dfFalseEasting, dfFalseNorthing);
7814
0
}
7815
7816
/************************************************************************/
7817
/*                            SetPolyconic()                            */
7818
/************************************************************************/
7819
7820
OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7821
                                         double dfCenterLong,
7822
                                         double dfFalseEasting,
7823
                                         double dfFalseNorthing)
7824
7825
0
{
7826
0
    TAKE_OPTIONAL_LOCK();
7827
7828
    // note: it seems that by some definitions this should include a
7829
    //       scale_factor parameter.
7830
0
    return d->replaceConversionAndUnref(
7831
0
        proj_create_conversion_american_polyconic(
7832
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7833
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7834
0
}
7835
7836
/************************************************************************/
7837
/*                          OSRSetPolyconic()                           */
7838
/************************************************************************/
7839
7840
OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7841
                       double dfCenterLong, double dfFalseEasting,
7842
                       double dfFalseNorthing)
7843
7844
0
{
7845
0
    VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7846
7847
0
    return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7848
0
                                         dfFalseEasting, dfFalseNorthing);
7849
0
}
7850
7851
/************************************************************************/
7852
/*                               SetPS()                                */
7853
/************************************************************************/
7854
7855
/** Sets a Polar Stereographic projection.
7856
 *
7857
 * Two variants are possible:
7858
 * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7859
 *   interpreted as the latitude of origin, combined with the scale factor
7860
 * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7861
 *   is interpreted as the latitude of true scale. In that situation, dfScale
7862
 *   must be set to 1 (it is ignored in the projection parameters)
7863
 */
7864
OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7865
                                  double dfScale, double dfFalseEasting,
7866
                                  double dfFalseNorthing)
7867
7868
0
{
7869
0
    TAKE_OPTIONAL_LOCK();
7870
7871
0
    PJ *conv;
7872
0
    if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7873
0
    {
7874
0
        conv = proj_create_conversion_polar_stereographic_variant_b(
7875
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7876
0
            dfFalseNorthing, nullptr, 0, nullptr, 0);
7877
0
    }
7878
0
    else
7879
0
    {
7880
0
        conv = proj_create_conversion_polar_stereographic_variant_a(
7881
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7882
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7883
0
    }
7884
7885
0
    const char *pszName = nullptr;
7886
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7887
0
    CPLString osName = pszName ? pszName : "";
7888
7889
0
    d->refreshProjObj();
7890
7891
0
    d->demoteFromBoundCRS();
7892
7893
0
    auto cs = proj_create_cartesian_2D_cs(
7894
0
        d->getPROJContext(),
7895
0
        dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7896
0
                        : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7897
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7898
0
    auto projCRS =
7899
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7900
0
                                  d->getGeodBaseCRS(), conv, cs);
7901
0
    proj_destroy(conv);
7902
0
    proj_destroy(cs);
7903
7904
0
    d->setPjCRS(projCRS);
7905
7906
0
    d->undoDemoteFromBoundCRS();
7907
7908
0
    return OGRERR_NONE;
7909
0
}
7910
7911
/************************************************************************/
7912
/*                              OSRSetPS()                              */
7913
/************************************************************************/
7914
7915
OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7916
                double dfCenterLong, double dfScale, double dfFalseEasting,
7917
                double dfFalseNorthing)
7918
7919
0
{
7920
0
    VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7921
7922
0
    return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7923
0
                                  dfFalseEasting, dfFalseNorthing);
7924
0
}
7925
7926
/************************************************************************/
7927
/*                            SetRobinson()                             */
7928
/************************************************************************/
7929
7930
OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7931
                                        double dfFalseEasting,
7932
                                        double dfFalseNorthing)
7933
7934
0
{
7935
0
    TAKE_OPTIONAL_LOCK();
7936
7937
0
    return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7938
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7939
0
        nullptr, 0, nullptr, 0));
7940
0
}
7941
7942
/************************************************************************/
7943
/*                           OSRSetRobinson()                           */
7944
/************************************************************************/
7945
7946
OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7947
                      double dfFalseEasting, double dfFalseNorthing)
7948
7949
0
{
7950
0
    VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7951
7952
0
    return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7953
0
                                        dfFalseNorthing);
7954
0
}
7955
7956
/************************************************************************/
7957
/*                           SetSinusoidal()                            */
7958
/************************************************************************/
7959
7960
OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7961
                                          double dfFalseEasting,
7962
                                          double dfFalseNorthing)
7963
7964
0
{
7965
0
    TAKE_OPTIONAL_LOCK();
7966
7967
0
    return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7968
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7969
0
        nullptr, 0, nullptr, 0));
7970
0
}
7971
7972
/************************************************************************/
7973
/*                          OSRSetSinusoidal()                          */
7974
/************************************************************************/
7975
7976
OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7977
                        double dfFalseEasting, double dfFalseNorthing)
7978
7979
0
{
7980
0
    VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7981
7982
0
    return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7983
0
                                          dfFalseNorthing);
7984
0
}
7985
7986
/************************************************************************/
7987
/*                          SetStereographic()                          */
7988
/************************************************************************/
7989
7990
OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7991
                                             double dfCMeridian, double dfScale,
7992
                                             double dfFalseEasting,
7993
                                             double dfFalseNorthing)
7994
7995
0
{
7996
0
    TAKE_OPTIONAL_LOCK();
7997
7998
0
    return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7999
0
        d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
8000
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
8001
0
}
8002
8003
/************************************************************************/
8004
/*                        OSRSetStereographic()                         */
8005
/************************************************************************/
8006
8007
OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
8008
                           double dfCMeridian, double dfScale,
8009
                           double dfFalseEasting, double dfFalseNorthing)
8010
8011
0
{
8012
0
    VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
8013
8014
0
    return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
8015
0
                                             dfFalseEasting, dfFalseNorthing);
8016
0
}
8017
8018
/************************************************************************/
8019
/*                               SetSOC()                               */
8020
/*                                                                      */
8021
/*      NOTE: This definition isn't really used in practice any more    */
8022
/*      and should be considered deprecated.  It seems that swiss       */
8023
/*      oblique mercator is now define as Hotine_Oblique_Mercator       */
8024
/*      with an azimuth of 90 and a rectified_grid_angle of 90.  See    */
8025
/*      EPSG:2056 and Bug 423.                                          */
8026
/************************************************************************/
8027
8028
OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
8029
                                   double dfCentralMeridian,
8030
                                   double dfFalseEasting,
8031
                                   double dfFalseNorthing)
8032
8033
0
{
8034
0
    TAKE_OPTIONAL_LOCK();
8035
8036
0
    return d->replaceConversionAndUnref(
8037
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
8038
0
            d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
8039
0
            90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
8040
0
            0.0));
8041
#if 0
8042
    SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
8043
    SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
8044
    SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
8045
    SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
8046
    SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
8047
8048
    return OGRERR_NONE;
8049
#endif
8050
0
}
8051
8052
/************************************************************************/
8053
/*                             OSRSetSOC()                              */
8054
/************************************************************************/
8055
8056
OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
8057
                 double dfCentralMeridian, double dfFalseEasting,
8058
                 double dfFalseNorthing)
8059
8060
0
{
8061
0
    VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
8062
8063
0
    return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
8064
0
                                   dfFalseEasting, dfFalseNorthing);
8065
0
}
8066
8067
/************************************************************************/
8068
/*                               SetVDG()                               */
8069
/************************************************************************/
8070
8071
OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
8072
                                   double dfFalseNorthing)
8073
8074
0
{
8075
0
    TAKE_OPTIONAL_LOCK();
8076
8077
0
    return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
8078
0
        d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
8079
0
        nullptr, 0, nullptr, 0));
8080
0
}
8081
8082
/************************************************************************/
8083
/*                             OSRSetVDG()                              */
8084
/************************************************************************/
8085
8086
OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
8087
                 double dfFalseEasting, double dfFalseNorthing)
8088
8089
0
{
8090
0
    VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
8091
8092
0
    return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
8093
0
                                   dfFalseNorthing);
8094
0
}
8095
8096
/************************************************************************/
8097
/*                               SetUTM()                               */
8098
/************************************************************************/
8099
8100
/**
8101
 * \brief Set UTM projection definition.
8102
 *
8103
 * This will generate a projection definition with the full set of
8104
 * transverse mercator projection parameters for the given UTM zone.
8105
 * If no PROJCS[] description is set yet, one will be set to look
8106
 * like "UTM Zone %d, {Northern, Southern} Hemisphere".
8107
 *
8108
 * This method is the same as the C function OSRSetUTM().
8109
 *
8110
 * @param nZone UTM zone.
8111
 *
8112
 * @param bNorth TRUE for northern hemisphere, or FALSE for southern
8113
 * hemisphere.
8114
 *
8115
 * @return OGRERR_NONE on success.
8116
 */
8117
8118
OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
8119
8120
0
{
8121
0
    TAKE_OPTIONAL_LOCK();
8122
8123
0
    if (nZone < 0 || nZone > 60)
8124
0
    {
8125
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8126
0
        return OGRERR_FAILURE;
8127
0
    }
8128
8129
0
    return d->replaceConversionAndUnref(
8130
0
        proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8131
0
}
8132
8133
/************************************************************************/
8134
/*                             OSRSetUTM()                              */
8135
/************************************************************************/
8136
8137
/**
8138
 * \brief Set UTM projection definition.
8139
 *
8140
 * This is the same as the C++ method OGRSpatialReference::SetUTM()
8141
 */
8142
OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8143
8144
0
{
8145
0
    VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8146
8147
0
    return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8148
0
}
8149
8150
/************************************************************************/
8151
/*                             GetUTMZone()                             */
8152
/*                                                                      */
8153
/*      Returns zero if it isn't UTM.                                   */
8154
/************************************************************************/
8155
8156
/**
8157
 * \brief Get utm zone information.
8158
 *
8159
 * This is the same as the C function OSRGetUTMZone().
8160
 *
8161
 * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8162
 * zone which is negative in the southern hemisphere instead of having the
8163
 * pbNorth flag used in the C and C++ interface.
8164
 *
8165
 * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8166
 * FALSE if southern.
8167
 *
8168
 * @return UTM zone number or zero if this isn't a UTM definition.
8169
 */
8170
8171
int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8172
8173
0
{
8174
0
    TAKE_OPTIONAL_LOCK();
8175
8176
0
    if (IsProjected() && GetAxesCount() == 3)
8177
0
    {
8178
0
        OGRSpatialReference *poSRSTmp = Clone();
8179
0
        poSRSTmp->DemoteTo2D(nullptr);
8180
0
        const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8181
0
        delete poSRSTmp;
8182
0
        return nZone;
8183
0
    }
8184
8185
0
    const char *pszProjection = GetAttrValue("PROJECTION");
8186
8187
0
    if (pszProjection == nullptr ||
8188
0
        !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8189
0
        return 0;
8190
8191
0
    if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8192
0
        return 0;
8193
8194
0
    if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8195
0
        return 0;
8196
8197
0
    if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8198
0
        return 0;
8199
8200
0
    const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8201
8202
0
    if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8203
0
        return 0;
8204
8205
0
    if (pbNorth != nullptr)
8206
0
        *pbNorth = (dfFalseNorthing == 0);
8207
8208
0
    const double dfCentralMeridian =
8209
0
        GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8210
0
    const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8211
8212
0
    if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8213
0
        std::isnan(dfZone) ||
8214
0
        std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8215
0
        return 0;
8216
8217
0
    return static_cast<int>(dfZone);
8218
0
}
8219
8220
/************************************************************************/
8221
/*                           OSRGetUTMZone()                            */
8222
/************************************************************************/
8223
8224
/**
8225
 * \brief Get utm zone information.
8226
 *
8227
 * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8228
 */
8229
int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8230
8231
0
{
8232
0
    VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8233
8234
0
    return ToPointer(hSRS)->GetUTMZone(pbNorth);
8235
0
}
8236
8237
/************************************************************************/
8238
/*                             SetWagner()                              */
8239
/************************************************************************/
8240
8241
OGRErr OGRSpatialReference::SetWagner(int nVariation,  // 1--7.
8242
                                      double dfCenterLat, double dfFalseEasting,
8243
                                      double dfFalseNorthing)
8244
8245
0
{
8246
0
    TAKE_OPTIONAL_LOCK();
8247
8248
0
    PJ *conv;
8249
0
    if (nVariation == 1)
8250
0
    {
8251
0
        conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8252
0
                                               dfFalseEasting, dfFalseNorthing,
8253
0
                                               nullptr, 0.0, nullptr, 0.0);
8254
0
    }
8255
0
    else if (nVariation == 2)
8256
0
    {
8257
0
        conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8258
0
                                                dfFalseEasting, dfFalseNorthing,
8259
0
                                                nullptr, 0.0, nullptr, 0.0);
8260
0
    }
8261
0
    else if (nVariation == 3)
8262
0
    {
8263
0
        conv = proj_create_conversion_wagner_iii(
8264
0
            d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8265
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8266
0
    }
8267
0
    else if (nVariation == 4)
8268
0
    {
8269
0
        conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8270
0
                                                dfFalseEasting, dfFalseNorthing,
8271
0
                                                nullptr, 0.0, nullptr, 0.0);
8272
0
    }
8273
0
    else if (nVariation == 5)
8274
0
    {
8275
0
        conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8276
0
                                               dfFalseEasting, dfFalseNorthing,
8277
0
                                               nullptr, 0.0, nullptr, 0.0);
8278
0
    }
8279
0
    else if (nVariation == 6)
8280
0
    {
8281
0
        conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8282
0
                                                dfFalseEasting, dfFalseNorthing,
8283
0
                                                nullptr, 0.0, nullptr, 0.0);
8284
0
    }
8285
0
    else if (nVariation == 7)
8286
0
    {
8287
0
        conv = proj_create_conversion_wagner_vii(
8288
0
            d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8289
0
            0.0, nullptr, 0.0);
8290
0
    }
8291
0
    else
8292
0
    {
8293
0
        CPLError(CE_Failure, CPLE_AppDefined,
8294
0
                 "Unsupported Wagner variation (%d).", nVariation);
8295
0
        return OGRERR_UNSUPPORTED_SRS;
8296
0
    }
8297
8298
0
    return d->replaceConversionAndUnref(conv);
8299
0
}
8300
8301
/************************************************************************/
8302
/*                            OSRSetWagner()                            */
8303
/************************************************************************/
8304
8305
OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8306
                    double dfCenterLat, double dfFalseEasting,
8307
                    double dfFalseNorthing)
8308
8309
0
{
8310
0
    VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8311
8312
0
    return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8313
0
                                      dfFalseNorthing);
8314
0
}
8315
8316
/************************************************************************/
8317
/*                            SetQSC()                     */
8318
/************************************************************************/
8319
8320
OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8321
0
{
8322
0
    TAKE_OPTIONAL_LOCK();
8323
8324
0
    return d->replaceConversionAndUnref(
8325
0
        proj_create_conversion_quadrilateralized_spherical_cube(
8326
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8327
0
            0, nullptr, 0));
8328
0
}
8329
8330
/************************************************************************/
8331
/*                           OSRSetQSC()                   */
8332
/************************************************************************/
8333
8334
OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8335
                 double dfCenterLong)
8336
8337
0
{
8338
0
    VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8339
8340
0
    return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8341
0
}
8342
8343
/************************************************************************/
8344
/*                            SetSCH()                     */
8345
/************************************************************************/
8346
8347
OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8348
                                   double dfPegHeading, double dfPegHgt)
8349
8350
0
{
8351
0
    TAKE_OPTIONAL_LOCK();
8352
8353
0
    return d->replaceConversionAndUnref(
8354
0
        proj_create_conversion_spherical_cross_track_height(
8355
0
            d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8356
0
            nullptr, 0, nullptr, 0));
8357
0
}
8358
8359
/************************************************************************/
8360
/*                           OSRSetSCH()                   */
8361
/************************************************************************/
8362
8363
OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8364
                 double dfPegHeading, double dfPegHgt)
8365
8366
0
{
8367
0
    VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8368
8369
0
    return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8370
0
}
8371
8372
/************************************************************************/
8373
/*                         SetVerticalPerspective()                     */
8374
/************************************************************************/
8375
8376
OGRErr OGRSpatialReference::SetVerticalPerspective(
8377
    double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8378
    double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8379
0
{
8380
0
    TAKE_OPTIONAL_LOCK();
8381
8382
0
    return d->replaceConversionAndUnref(
8383
0
        proj_create_conversion_vertical_perspective(
8384
0
            d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8385
0
            dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8386
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
8387
0
}
8388
8389
/************************************************************************/
8390
/*                       OSRSetVerticalPerspective()                    */
8391
/************************************************************************/
8392
8393
OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8394
                                 double dfTopoOriginLat, double dfTopoOriginLon,
8395
                                 double dfTopoOriginHeight,
8396
                                 double dfViewPointHeight,
8397
                                 double dfFalseEasting, double dfFalseNorthing)
8398
8399
0
{
8400
0
    VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8401
8402
0
    return ToPointer(hSRS)->SetVerticalPerspective(
8403
0
        dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8404
0
        dfFalseEasting, dfFalseNorthing);
8405
0
}
8406
8407
/************************************************************************/
8408
/*             SetDerivedGeogCRSWithPoleRotationGRIBConvention()        */
8409
/************************************************************************/
8410
8411
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8412
    const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8413
    double dfAxisRotation)
8414
0
{
8415
0
    TAKE_OPTIONAL_LOCK();
8416
8417
0
    d->refreshProjObj();
8418
0
    if (!d->m_pj_crs)
8419
0
        return OGRERR_FAILURE;
8420
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8421
0
        return OGRERR_FAILURE;
8422
0
    auto ctxt = d->getPROJContext();
8423
0
    auto conv = proj_create_conversion_pole_rotation_grib_convention(
8424
0
        ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, 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
0
}
8432
8433
/************************************************************************/
8434
/*         SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention()        */
8435
/************************************************************************/
8436
8437
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8438
    const char *pszCRSName, double dfGridNorthPoleLat,
8439
    double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8440
0
{
8441
0
    TAKE_OPTIONAL_LOCK();
8442
8443
0
#if PROJ_VERSION_MAJOR > 8 ||                                                  \
8444
0
    (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8445
0
    d->refreshProjObj();
8446
0
    if (!d->m_pj_crs)
8447
0
        return OGRERR_FAILURE;
8448
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8449
0
        return OGRERR_FAILURE;
8450
0
    auto ctxt = d->getPROJContext();
8451
0
    auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8452
0
        ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8453
0
        nullptr, 0);
8454
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8455
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8456
0
                                                   d->m_pj_crs, conv, cs));
8457
0
    proj_destroy(conv);
8458
0
    proj_destroy(cs);
8459
0
    return OGRERR_NONE;
8460
#else
8461
    (void)pszCRSName;
8462
    SetProjection("Rotated_pole");
8463
    SetExtension(
8464
        "PROJCS", "PROJ4",
8465
        CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8466
                   "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8467
                   "+to_meter=0.0174532925199433 "
8468
                   "+wktext",
8469
                   180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8470
                   dfGridNorthPoleLat, GetSemiMajor(nullptr),
8471
                   GetSemiMinor(nullptr)));
8472
    return OGRERR_NONE;
8473
#endif
8474
0
}
8475
8476
/************************************************************************/
8477
/*                            SetAuthority()                            */
8478
/************************************************************************/
8479
8480
/**
8481
 * \brief Set the authority for a node.
8482
 *
8483
 * This method is the same as the C function OSRSetAuthority().
8484
 *
8485
 * @param pszTargetKey the partial or complete path to the node to
8486
 * set an authority on.  i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8487
 *
8488
 * @param pszAuthority authority name, such as "EPSG".
8489
 *
8490
 * @param nCode code for value with this authority.
8491
 *
8492
 * @return OGRERR_NONE on success.
8493
 */
8494
8495
OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8496
                                         const char *pszAuthority, int nCode)
8497
8498
0
{
8499
0
    TAKE_OPTIONAL_LOCK();
8500
8501
0
    d->refreshProjObj();
8502
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8503
8504
0
    if (pszTargetKey == nullptr)
8505
0
    {
8506
0
        if (!d->m_pj_crs)
8507
0
            return OGRERR_FAILURE;
8508
0
        CPLString osCode;
8509
0
        osCode.Printf("%d", nCode);
8510
0
        d->demoteFromBoundCRS();
8511
0
        d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8512
0
                                  pszAuthority, osCode.c_str()));
8513
0
        d->undoDemoteFromBoundCRS();
8514
0
        return OGRERR_NONE;
8515
0
    }
8516
8517
0
    d->demoteFromBoundCRS();
8518
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8519
0
    {
8520
0
        CPLString osCode;
8521
0
        osCode.Printf("%d", nCode);
8522
0
        auto newGeogCRS =
8523
0
            proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8524
0
                          pszAuthority, osCode.c_str());
8525
8526
0
        auto conv =
8527
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8528
8529
0
        auto projCRS = proj_create_projected_crs(
8530
0
            d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8531
0
            d->getProjCRSCoordSys());
8532
8533
        // Preserve existing id on the PROJCRS
8534
0
        const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8535
0
        const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8536
0
        if (pszProjCRSAuthName && pszProjCRSCode)
8537
0
        {
8538
0
            auto projCRSWithId =
8539
0
                proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8540
0
                              pszProjCRSCode);
8541
0
            proj_destroy(projCRS);
8542
0
            projCRS = projCRSWithId;
8543
0
        }
8544
8545
0
        proj_destroy(newGeogCRS);
8546
0
        proj_destroy(conv);
8547
8548
0
        d->setPjCRS(projCRS);
8549
0
        d->undoDemoteFromBoundCRS();
8550
0
        return OGRERR_NONE;
8551
0
    }
8552
0
    d->undoDemoteFromBoundCRS();
8553
8554
    /* -------------------------------------------------------------------- */
8555
    /*      Find the node below which the authority should be put.          */
8556
    /* -------------------------------------------------------------------- */
8557
0
    OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8558
8559
0
    if (poNode == nullptr)
8560
0
        return OGRERR_FAILURE;
8561
8562
    /* -------------------------------------------------------------------- */
8563
    /*      If there is an existing AUTHORITY child blow it away before     */
8564
    /*      trying to set a new one.                                        */
8565
    /* -------------------------------------------------------------------- */
8566
0
    int iOldChild = poNode->FindChild("AUTHORITY");
8567
0
    if (iOldChild != -1)
8568
0
        poNode->DestroyChild(iOldChild);
8569
8570
    /* -------------------------------------------------------------------- */
8571
    /*      Create a new authority node.                                    */
8572
    /* -------------------------------------------------------------------- */
8573
0
    char szCode[32] = {};
8574
8575
0
    snprintf(szCode, sizeof(szCode), "%d", nCode);
8576
8577
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8578
0
    poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8579
0
    poAuthNode->AddChild(new OGR_SRSNode(szCode));
8580
8581
0
    poNode->AddChild(poAuthNode);
8582
8583
0
    return OGRERR_NONE;
8584
0
}
8585
8586
/************************************************************************/
8587
/*                          OSRSetAuthority()                           */
8588
/************************************************************************/
8589
8590
/**
8591
 * \brief Set the authority for a node.
8592
 *
8593
 * This function is the same as OGRSpatialReference::SetAuthority().
8594
 */
8595
OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8596
                       const char *pszAuthority, int nCode)
8597
8598
0
{
8599
0
    VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8600
8601
0
    return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8602
0
}
8603
8604
/************************************************************************/
8605
/*                          GetAuthorityCode()                          */
8606
/************************************************************************/
8607
8608
/**
8609
 * \brief Get the authority code for a node.
8610
 *
8611
 * This method is used to query an AUTHORITY[] node from within the
8612
 * WKT tree, and fetch the code value.
8613
 *
8614
 * While in theory values may be non-numeric, for the EPSG authority all
8615
 * code values should be integral.
8616
 *
8617
 * This method is the same as the C function OSRGetAuthorityCode().
8618
 *
8619
 * @param pszTargetKey the partial or complete path to the node to
8620
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8621
 * search for an authority node on the root element.
8622
 *
8623
 * @return value code from authority node, or NULL on failure.  The value
8624
 * returned is internal and should not be freed or modified.
8625
 */
8626
8627
const char *
8628
OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8629
8630
0
{
8631
0
    TAKE_OPTIONAL_LOCK();
8632
8633
0
    d->refreshProjObj();
8634
0
    const char *pszInputTargetKey = pszTargetKey;
8635
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8636
0
    if (pszTargetKey == nullptr)
8637
0
    {
8638
0
        if (!d->m_pj_crs)
8639
0
        {
8640
0
            return nullptr;
8641
0
        }
8642
0
        d->demoteFromBoundCRS();
8643
0
        auto ret = proj_get_id_code(d->m_pj_crs, 0);
8644
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8645
0
        {
8646
0
            auto ctxt = d->getPROJContext();
8647
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8648
0
            if (cs)
8649
0
            {
8650
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8651
0
                proj_destroy(cs);
8652
0
                if (axisCount == 3)
8653
0
                {
8654
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8655
                    // 2002 in which case, using the WKT1 representation will
8656
                    // enable us to recover the EPSG code.
8657
0
                    pszTargetKey = pszInputTargetKey;
8658
0
                }
8659
0
            }
8660
0
        }
8661
0
        d->undoDemoteFromBoundCRS();
8662
0
        if (ret != nullptr || pszTargetKey == nullptr)
8663
0
        {
8664
0
            return ret;
8665
0
        }
8666
0
    }
8667
8668
    // Special key for that context
8669
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8670
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8671
0
    {
8672
0
        auto ctxt = d->getPROJContext();
8673
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8674
0
        if (crs)
8675
0
        {
8676
0
            const char *ret = proj_get_id_code(crs, 0);
8677
0
            if (ret)
8678
0
                ret = CPLSPrintf("%s", ret);
8679
0
            proj_destroy(crs);
8680
0
            return ret;
8681
0
        }
8682
0
    }
8683
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8684
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8685
0
    {
8686
0
        auto ctxt = d->getPROJContext();
8687
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8688
0
        if (crs)
8689
0
        {
8690
0
            const char *ret = proj_get_id_code(crs, 0);
8691
0
            if (ret)
8692
0
                ret = CPLSPrintf("%s", ret);
8693
0
            proj_destroy(crs);
8694
0
            return ret;
8695
0
        }
8696
0
    }
8697
8698
    /* -------------------------------------------------------------------- */
8699
    /*      Find the node below which the authority should be put.          */
8700
    /* -------------------------------------------------------------------- */
8701
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8702
8703
0
    if (poNode == nullptr)
8704
0
        return nullptr;
8705
8706
    /* -------------------------------------------------------------------- */
8707
    /*      Fetch AUTHORITY child if there is one.                          */
8708
    /* -------------------------------------------------------------------- */
8709
0
    if (poNode->FindChild("AUTHORITY") == -1)
8710
0
        return nullptr;
8711
8712
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8713
8714
    /* -------------------------------------------------------------------- */
8715
    /*      Create a new authority node.                                    */
8716
    /* -------------------------------------------------------------------- */
8717
0
    if (poNode->GetChildCount() < 2)
8718
0
        return nullptr;
8719
8720
0
    return poNode->GetChild(1)->GetValue();
8721
0
}
8722
8723
/************************************************************************/
8724
/*                          OSRGetAuthorityCode()                       */
8725
/************************************************************************/
8726
8727
/**
8728
 * \brief Get the authority code for a node.
8729
 *
8730
 * This function is the same as OGRSpatialReference::GetAuthorityCode().
8731
 */
8732
const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8733
                                const char *pszTargetKey)
8734
8735
0
{
8736
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8737
8738
0
    return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8739
0
}
8740
8741
/************************************************************************/
8742
/*                          GetAuthorityName()                          */
8743
/************************************************************************/
8744
8745
/**
8746
 * \brief Get the authority name for a node.
8747
 *
8748
 * This method is used to query an AUTHORITY[] node from within the
8749
 * WKT tree, and fetch the authority name value.
8750
 *
8751
 * The most common authority is "EPSG".
8752
 *
8753
 * This method is the same as the C function OSRGetAuthorityName().
8754
 *
8755
 * @param pszTargetKey the partial or complete path to the node to
8756
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8757
 * search for an authority node on the root element.
8758
 *
8759
 * @return value code from authority node, or NULL on failure. The value
8760
 * returned is internal and should not be freed or modified.
8761
 */
8762
8763
const char *
8764
OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8765
8766
0
{
8767
0
    TAKE_OPTIONAL_LOCK();
8768
8769
0
    d->refreshProjObj();
8770
0
    const char *pszInputTargetKey = pszTargetKey;
8771
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8772
0
    if (pszTargetKey == nullptr)
8773
0
    {
8774
0
        if (!d->m_pj_crs)
8775
0
        {
8776
0
            return nullptr;
8777
0
        }
8778
0
        d->demoteFromBoundCRS();
8779
0
        auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8780
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8781
0
        {
8782
0
            auto ctxt = d->getPROJContext();
8783
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8784
0
            if (cs)
8785
0
            {
8786
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8787
0
                proj_destroy(cs);
8788
0
                if (axisCount == 3)
8789
0
                {
8790
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8791
                    // 2002 in which case, using the WKT1 representation will
8792
                    // enable us to recover the EPSG code.
8793
0
                    pszTargetKey = pszInputTargetKey;
8794
0
                }
8795
0
            }
8796
0
        }
8797
0
        d->undoDemoteFromBoundCRS();
8798
0
        if (ret != nullptr || pszTargetKey == nullptr)
8799
0
        {
8800
0
            return ret;
8801
0
        }
8802
0
    }
8803
8804
    // Special key for that context
8805
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8806
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8807
0
    {
8808
0
        auto ctxt = d->getPROJContext();
8809
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8810
0
        if (crs)
8811
0
        {
8812
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8813
0
            if (ret)
8814
0
                ret = CPLSPrintf("%s", ret);
8815
0
            proj_destroy(crs);
8816
0
            return ret;
8817
0
        }
8818
0
    }
8819
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8820
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8821
0
    {
8822
0
        auto ctxt = d->getPROJContext();
8823
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8824
0
        if (crs)
8825
0
        {
8826
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8827
0
            if (ret)
8828
0
                ret = CPLSPrintf("%s", ret);
8829
0
            proj_destroy(crs);
8830
0
            return ret;
8831
0
        }
8832
0
    }
8833
8834
    /* -------------------------------------------------------------------- */
8835
    /*      Find the node below which the authority should be put.          */
8836
    /* -------------------------------------------------------------------- */
8837
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8838
8839
0
    if (poNode == nullptr)
8840
0
        return nullptr;
8841
8842
    /* -------------------------------------------------------------------- */
8843
    /*      Fetch AUTHORITY child if there is one.                          */
8844
    /* -------------------------------------------------------------------- */
8845
0
    if (poNode->FindChild("AUTHORITY") == -1)
8846
0
        return nullptr;
8847
8848
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8849
8850
    /* -------------------------------------------------------------------- */
8851
    /*      Create a new authority node.                                    */
8852
    /* -------------------------------------------------------------------- */
8853
0
    if (poNode->GetChildCount() < 2)
8854
0
        return nullptr;
8855
8856
0
    return poNode->GetChild(0)->GetValue();
8857
0
}
8858
8859
/************************************************************************/
8860
/*                        OSRGetAuthorityName()                         */
8861
/************************************************************************/
8862
8863
/**
8864
 * \brief Get the authority name for a node.
8865
 *
8866
 * This function is the same as OGRSpatialReference::GetAuthorityName().
8867
 */
8868
const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8869
                                const char *pszTargetKey)
8870
8871
0
{
8872
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8873
8874
0
    return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8875
0
}
8876
8877
/************************************************************************/
8878
/*                          GetOGCURN()                                 */
8879
/************************************************************************/
8880
8881
/**
8882
 * \brief Get a OGC URN string describing the CRS, when possible
8883
 *
8884
 * This method assumes that the CRS has a top-level identifier, or is
8885
 * a compound CRS whose horizontal and vertical parts have a top-level
8886
 * identifier.
8887
 *
8888
 * @return a string to free with CPLFree(), or nullptr when no result can be
8889
 * generated
8890
 *
8891
 * @since GDAL 3.5
8892
 */
8893
8894
char *OGRSpatialReference::GetOGCURN() const
8895
8896
0
{
8897
0
    TAKE_OPTIONAL_LOCK();
8898
8899
0
    const char *pszAuthName = GetAuthorityName(nullptr);
8900
0
    const char *pszAuthCode = GetAuthorityCode(nullptr);
8901
0
    if (pszAuthName && pszAuthCode)
8902
0
        return CPLStrdup(
8903
0
            CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8904
0
    if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8905
0
        return nullptr;
8906
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8907
0
    auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8908
0
    char *pszRet = nullptr;
8909
0
    if (horizCRS && vertCRS)
8910
0
    {
8911
0
        auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8912
0
        auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8913
0
        auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8914
0
        auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8915
0
        if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8916
0
        {
8917
0
            pszRet = CPLStrdup(CPLSPrintf(
8918
0
                "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8919
0
                horizAuthCode, vertAuthName, vertAuthCode));
8920
0
        }
8921
0
    }
8922
0
    proj_destroy(horizCRS);
8923
0
    proj_destroy(vertCRS);
8924
0
    return pszRet;
8925
0
}
8926
8927
/************************************************************************/
8928
/*                           StripVertical()                            */
8929
/************************************************************************/
8930
8931
/**
8932
 * \brief Convert a compound cs into a horizontal CS.
8933
 *
8934
 * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8935
 * nodes are stripped resulting and only the horizontal coordinate system
8936
 * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8937
 *
8938
 * If this is not a compound coordinate system then nothing is changed.
8939
 *
8940
 * This method is the same as the C function OSRStripVertical().
8941
 *
8942
 */
8943
8944
OGRErr OGRSpatialReference::StripVertical()
8945
8946
0
{
8947
0
    TAKE_OPTIONAL_LOCK();
8948
8949
0
    d->refreshProjObj();
8950
0
    d->demoteFromBoundCRS();
8951
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8952
0
    {
8953
0
        d->undoDemoteFromBoundCRS();
8954
0
        return OGRERR_NONE;
8955
0
    }
8956
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8957
0
    if (!horizCRS)
8958
0
    {
8959
0
        d->undoDemoteFromBoundCRS();
8960
0
        return OGRERR_FAILURE;
8961
0
    }
8962
8963
0
    bool reuseExistingBoundCRS = false;
8964
0
    if (d->m_pj_bound_crs_target)
8965
0
    {
8966
0
        auto type = proj_get_type(d->m_pj_bound_crs_target);
8967
0
        reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8968
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8969
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8970
0
    }
8971
8972
0
    if (reuseExistingBoundCRS)
8973
0
    {
8974
0
        auto newBoundCRS = proj_crs_create_bound_crs(
8975
0
            d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8976
0
            d->m_pj_bound_crs_co);
8977
0
        proj_destroy(horizCRS);
8978
0
        d->undoDemoteFromBoundCRS();
8979
0
        d->setPjCRS(newBoundCRS);
8980
0
    }
8981
0
    else
8982
0
    {
8983
0
        d->undoDemoteFromBoundCRS();
8984
0
        d->setPjCRS(horizCRS);
8985
0
    }
8986
8987
0
    return OGRERR_NONE;
8988
0
}
8989
8990
/************************************************************************/
8991
/*                            OSRStripVertical()                             */
8992
/************************************************************************/
8993
/**
8994
 * \brief Convert a compound cs into a horizontal CS.
8995
 *
8996
 * This function is the same as the C++ method
8997
 * OGRSpatialReference::StripVertical().
8998
 */
8999
OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
9000
9001
0
{
9002
0
    VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
9003
9004
0
    return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
9005
0
}
9006
9007
/************************************************************************/
9008
/*                   StripTOWGS84IfKnownDatumAndAllowed()               */
9009
/************************************************************************/
9010
9011
/**
9012
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9013
 *        and this is allowed by the user.
9014
 *
9015
 * The default behavior is to remove TOWGS84 information if the CRS has a
9016
 * known horizontal datum. This can be disabled by setting the
9017
 * OSR_STRIP_TOWGS84 configuration option to NO.
9018
 *
9019
 * @return true if TOWGS84 has been removed.
9020
 * @since OGR 3.1.0
9021
 */
9022
9023
bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
9024
0
{
9025
0
    if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
9026
0
    {
9027
0
        if (StripTOWGS84IfKnownDatum())
9028
0
        {
9029
0
            CPLDebug("OSR", "TOWGS84 information has been removed. "
9030
0
                            "It can be kept by setting the OSR_STRIP_TOWGS84 "
9031
0
                            "configuration option to NO");
9032
0
            return true;
9033
0
        }
9034
0
    }
9035
0
    return false;
9036
0
}
9037
9038
/************************************************************************/
9039
/*                      StripTOWGS84IfKnownDatum()                      */
9040
/************************************************************************/
9041
9042
/**
9043
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
9044
 *
9045
 * @return true if TOWGS84 has been removed.
9046
 * @since OGR 3.1.0
9047
 */
9048
9049
bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
9050
9051
0
{
9052
0
    TAKE_OPTIONAL_LOCK();
9053
9054
0
    d->refreshProjObj();
9055
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
9056
0
    {
9057
0
        return false;
9058
0
    }
9059
0
    auto ctxt = d->getPROJContext();
9060
0
    auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
9061
0
    if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
9062
0
    {
9063
0
        proj_destroy(baseCRS);
9064
0
        return false;
9065
0
    }
9066
9067
    // Known base CRS code ? Return base CRS
9068
0
    const char *pszCode = proj_get_id_code(baseCRS, 0);
9069
0
    if (pszCode)
9070
0
    {
9071
0
        d->setPjCRS(baseCRS);
9072
0
        return true;
9073
0
    }
9074
9075
0
    auto datum = proj_crs_get_datum(ctxt, baseCRS);
9076
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9077
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9078
0
    if (datum == nullptr)
9079
0
    {
9080
0
        datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
9081
0
    }
9082
0
#endif
9083
0
    if (!datum)
9084
0
    {
9085
0
        proj_destroy(baseCRS);
9086
0
        return false;
9087
0
    }
9088
9089
    // Known datum code ? Return base CRS
9090
0
    pszCode = proj_get_id_code(datum, 0);
9091
0
    if (pszCode)
9092
0
    {
9093
0
        proj_destroy(datum);
9094
0
        d->setPjCRS(baseCRS);
9095
0
        return true;
9096
0
    }
9097
9098
0
    const char *name = proj_get_name(datum);
9099
0
    if (EQUAL(name, "unknown"))
9100
0
    {
9101
0
        proj_destroy(datum);
9102
0
        proj_destroy(baseCRS);
9103
0
        return false;
9104
0
    }
9105
0
    const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
9106
0
    PJ_OBJ_LIST *list =
9107
0
        proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
9108
9109
0
    bool knownDatumName = false;
9110
0
    if (list)
9111
0
    {
9112
0
        if (proj_list_get_count(list) == 1)
9113
0
        {
9114
0
            knownDatumName = true;
9115
0
        }
9116
0
        proj_list_destroy(list);
9117
0
    }
9118
9119
0
    proj_destroy(datum);
9120
0
    if (knownDatumName)
9121
0
    {
9122
0
        d->setPjCRS(baseCRS);
9123
0
        return true;
9124
0
    }
9125
0
    proj_destroy(baseCRS);
9126
0
    return false;
9127
0
}
9128
9129
/************************************************************************/
9130
/*                             IsCompound()                             */
9131
/************************************************************************/
9132
9133
/**
9134
 * \brief Check if coordinate system is compound.
9135
 *
9136
 * This method is the same as the C function OSRIsCompound().
9137
 *
9138
 * @return TRUE if this is rooted with a COMPD_CS node.
9139
 */
9140
9141
int OGRSpatialReference::IsCompound() const
9142
9143
0
{
9144
0
    TAKE_OPTIONAL_LOCK();
9145
9146
0
    d->refreshProjObj();
9147
0
    d->demoteFromBoundCRS();
9148
0
    bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9149
0
    d->undoDemoteFromBoundCRS();
9150
0
    return isCompound;
9151
0
}
9152
9153
/************************************************************************/
9154
/*                           OSRIsCompound()                            */
9155
/************************************************************************/
9156
9157
/**
9158
 * \brief Check if the coordinate system is compound.
9159
 *
9160
 * This function is the same as OGRSpatialReference::IsCompound().
9161
 */
9162
int OSRIsCompound(OGRSpatialReferenceH hSRS)
9163
9164
0
{
9165
0
    VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9166
9167
0
    return ToPointer(hSRS)->IsCompound();
9168
0
}
9169
9170
/************************************************************************/
9171
/*                            IsProjected()                             */
9172
/************************************************************************/
9173
9174
/**
9175
 * \brief Check if projected coordinate system.
9176
 *
9177
 * This method is the same as the C function OSRIsProjected().
9178
 *
9179
 * @return TRUE if this contains a PROJCS node indicating a it is a
9180
 * projected coordinate system. Also if it is a CompoundCRS made of a
9181
 * ProjectedCRS
9182
 */
9183
9184
int OGRSpatialReference::IsProjected() const
9185
9186
0
{
9187
0
    TAKE_OPTIONAL_LOCK();
9188
9189
0
    d->refreshProjObj();
9190
0
    d->demoteFromBoundCRS();
9191
0
    bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9192
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9193
0
    {
9194
0
        auto horizCRS =
9195
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9196
0
        if (horizCRS)
9197
0
        {
9198
0
            auto horizCRSType = proj_get_type(horizCRS);
9199
0
            isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9200
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9201
0
            {
9202
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9203
0
                if (base)
9204
0
                {
9205
0
                    isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9206
0
                    proj_destroy(base);
9207
0
                }
9208
0
            }
9209
0
            proj_destroy(horizCRS);
9210
0
        }
9211
0
    }
9212
0
    d->undoDemoteFromBoundCRS();
9213
0
    return isProjected;
9214
0
}
9215
9216
/************************************************************************/
9217
/*                           OSRIsProjected()                           */
9218
/************************************************************************/
9219
/**
9220
 * \brief Check if projected coordinate system.
9221
 *
9222
 * This function is the same as OGRSpatialReference::IsProjected().
9223
 */
9224
int OSRIsProjected(OGRSpatialReferenceH hSRS)
9225
9226
0
{
9227
0
    VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9228
9229
0
    return ToPointer(hSRS)->IsProjected();
9230
0
}
9231
9232
/************************************************************************/
9233
/*                            IsGeocentric()                            */
9234
/************************************************************************/
9235
9236
/**
9237
 * \brief Check if geocentric coordinate system.
9238
 *
9239
 * This method is the same as the C function OSRIsGeocentric().
9240
 *
9241
 * @return TRUE if this contains a GEOCCS node indicating a it is a
9242
 * geocentric coordinate system.
9243
 *
9244
 */
9245
9246
int OGRSpatialReference::IsGeocentric() const
9247
9248
0
{
9249
0
    TAKE_OPTIONAL_LOCK();
9250
9251
0
    d->refreshProjObj();
9252
0
    d->demoteFromBoundCRS();
9253
0
    bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9254
0
    d->undoDemoteFromBoundCRS();
9255
0
    return isGeocentric;
9256
0
}
9257
9258
/************************************************************************/
9259
/*                           OSRIsGeocentric()                          */
9260
/************************************************************************/
9261
/**
9262
 * \brief Check if geocentric coordinate system.
9263
 *
9264
 * This function is the same as OGRSpatialReference::IsGeocentric().
9265
 *
9266
 */
9267
int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9268
9269
0
{
9270
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9271
9272
0
    return ToPointer(hSRS)->IsGeocentric();
9273
0
}
9274
9275
/************************************************************************/
9276
/*                            IsEmpty()                                 */
9277
/************************************************************************/
9278
9279
/**
9280
 * \brief Return if the SRS is not set.
9281
 */
9282
9283
bool OGRSpatialReference::IsEmpty() const
9284
0
{
9285
0
    TAKE_OPTIONAL_LOCK();
9286
9287
0
    d->refreshProjObj();
9288
0
    return d->m_pj_crs == nullptr;
9289
0
}
9290
9291
/************************************************************************/
9292
/*                            IsGeographic()                            */
9293
/************************************************************************/
9294
9295
/**
9296
 * \brief Check if geographic coordinate system.
9297
 *
9298
 * This method is the same as the C function OSRIsGeographic().
9299
 *
9300
 * @return TRUE if this spatial reference is geographic ... that is the
9301
 * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9302
 * GeographicCRS
9303
 */
9304
9305
int OGRSpatialReference::IsGeographic() const
9306
9307
0
{
9308
0
    TAKE_OPTIONAL_LOCK();
9309
9310
0
    d->refreshProjObj();
9311
0
    d->demoteFromBoundCRS();
9312
0
    bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9313
0
                  d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9314
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9315
0
    {
9316
0
        auto horizCRS =
9317
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9318
0
        if (horizCRS)
9319
0
        {
9320
0
            auto horizCRSType = proj_get_type(horizCRS);
9321
0
            isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9322
0
                     horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9323
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9324
0
            {
9325
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9326
0
                if (base)
9327
0
                {
9328
0
                    horizCRSType = proj_get_type(base);
9329
0
                    isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9330
0
                             horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9331
0
                    proj_destroy(base);
9332
0
                }
9333
0
            }
9334
0
            proj_destroy(horizCRS);
9335
0
        }
9336
0
    }
9337
0
    d->undoDemoteFromBoundCRS();
9338
0
    return isGeog;
9339
0
}
9340
9341
/************************************************************************/
9342
/*                          OSRIsGeographic()                           */
9343
/************************************************************************/
9344
/**
9345
 * \brief Check if geographic coordinate system.
9346
 *
9347
 * This function is the same as OGRSpatialReference::IsGeographic().
9348
 */
9349
int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9350
9351
0
{
9352
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9353
9354
0
    return ToPointer(hSRS)->IsGeographic();
9355
0
}
9356
9357
/************************************************************************/
9358
/*                      IsDerivedGeographic()                           */
9359
/************************************************************************/
9360
9361
/**
9362
 * \brief Check if the CRS is a derived geographic coordinate system.
9363
 * (for example a rotated long/lat grid)
9364
 *
9365
 * This method is the same as the C function OSRIsDerivedGeographic().
9366
 *
9367
 * @since GDAL 3.1.0 and PROJ 6.3.0
9368
 */
9369
9370
int OGRSpatialReference::IsDerivedGeographic() const
9371
9372
0
{
9373
0
    TAKE_OPTIONAL_LOCK();
9374
9375
0
    d->refreshProjObj();
9376
0
    d->demoteFromBoundCRS();
9377
0
    const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9378
0
                        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9379
0
    const bool isDerivedGeographic =
9380
0
        isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9381
0
    d->undoDemoteFromBoundCRS();
9382
0
    return isDerivedGeographic ? TRUE : FALSE;
9383
0
}
9384
9385
/************************************************************************/
9386
/*                      OSRIsDerivedGeographic()                        */
9387
/************************************************************************/
9388
/**
9389
 * \brief Check if the CRS is a derived geographic coordinate system.
9390
 * (for example a rotated long/lat grid)
9391
 *
9392
 * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9393
 */
9394
int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9395
9396
0
{
9397
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9398
9399
0
    return ToPointer(hSRS)->IsDerivedGeographic();
9400
0
}
9401
9402
/************************************************************************/
9403
/*                      IsDerivedProjected()                            */
9404
/************************************************************************/
9405
9406
/**
9407
 * \brief Check if the CRS is a derived projected coordinate system.
9408
 *
9409
 * This method is the same as the C function OSRIsDerivedGeographic().
9410
 *
9411
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9412
 */
9413
9414
int OGRSpatialReference::IsDerivedProjected() const
9415
9416
0
{
9417
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
9418
0
    TAKE_OPTIONAL_LOCK();
9419
0
    d->refreshProjObj();
9420
0
    d->demoteFromBoundCRS();
9421
0
    const bool isDerivedProjected =
9422
0
        d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9423
0
    d->undoDemoteFromBoundCRS();
9424
0
    return isDerivedProjected ? TRUE : FALSE;
9425
#else
9426
    return FALSE;
9427
#endif
9428
0
}
9429
9430
/************************************************************************/
9431
/*                      OSRIsDerivedProjected()                         */
9432
/************************************************************************/
9433
/**
9434
 * \brief Check if the CRS is a derived projected coordinate system.
9435
 *
9436
 * This function is the same as OGRSpatialReference::IsDerivedProjected().
9437
 *
9438
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9439
 */
9440
int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9441
9442
0
{
9443
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9444
9445
0
    return ToPointer(hSRS)->IsDerivedProjected();
9446
0
}
9447
9448
/************************************************************************/
9449
/*                              IsLocal()                               */
9450
/************************************************************************/
9451
9452
/**
9453
 * \brief Check if local coordinate system.
9454
 *
9455
 * This method is the same as the C function OSRIsLocal().
9456
 *
9457
 * @return TRUE if this spatial reference is local ... that is the
9458
 * root is a LOCAL_CS node.
9459
 */
9460
9461
int OGRSpatialReference::IsLocal() const
9462
9463
0
{
9464
0
    TAKE_OPTIONAL_LOCK();
9465
0
    d->refreshProjObj();
9466
0
    return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9467
0
}
9468
9469
/************************************************************************/
9470
/*                          OSRIsLocal()                                */
9471
/************************************************************************/
9472
/**
9473
 * \brief Check if local coordinate system.
9474
 *
9475
 * This function is the same as OGRSpatialReference::IsLocal().
9476
 */
9477
int OSRIsLocal(OGRSpatialReferenceH hSRS)
9478
9479
0
{
9480
0
    VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9481
9482
0
    return ToPointer(hSRS)->IsLocal();
9483
0
}
9484
9485
/************************************************************************/
9486
/*                            IsVertical()                              */
9487
/************************************************************************/
9488
9489
/**
9490
 * \brief Check if vertical coordinate system.
9491
 *
9492
 * This method is the same as the C function OSRIsVertical().
9493
 *
9494
 * @return TRUE if this contains a VERT_CS node indicating a it is a
9495
 * vertical coordinate system. Also if it is a CompoundCRS made of a
9496
 * VerticalCRS
9497
 *
9498
 */
9499
9500
int OGRSpatialReference::IsVertical() const
9501
9502
0
{
9503
0
    TAKE_OPTIONAL_LOCK();
9504
0
    d->refreshProjObj();
9505
0
    d->demoteFromBoundCRS();
9506
0
    bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9507
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9508
0
    {
9509
0
        auto vertCRS =
9510
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9511
0
        if (vertCRS)
9512
0
        {
9513
0
            const auto vertCRSType = proj_get_type(vertCRS);
9514
0
            isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9515
0
            if (vertCRSType == PJ_TYPE_BOUND_CRS)
9516
0
            {
9517
0
                auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9518
0
                if (base)
9519
0
                {
9520
0
                    isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9521
0
                    proj_destroy(base);
9522
0
                }
9523
0
            }
9524
0
            proj_destroy(vertCRS);
9525
0
        }
9526
0
    }
9527
0
    d->undoDemoteFromBoundCRS();
9528
0
    return isVertical;
9529
0
}
9530
9531
/************************************************************************/
9532
/*                           OSRIsVertical()                            */
9533
/************************************************************************/
9534
/**
9535
 * \brief Check if vertical coordinate system.
9536
 *
9537
 * This function is the same as OGRSpatialReference::IsVertical().
9538
 *
9539
 */
9540
int OSRIsVertical(OGRSpatialReferenceH hSRS)
9541
9542
0
{
9543
0
    VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9544
9545
0
    return ToPointer(hSRS)->IsVertical();
9546
0
}
9547
9548
/************************************************************************/
9549
/*                            IsDynamic()                               */
9550
/************************************************************************/
9551
9552
/**
9553
 * \brief Check if a CRS is a dynamic CRS.
9554
 *
9555
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9556
 * plate-fixed.
9557
 *
9558
 * This method is the same as the C function OSRIsDynamic().
9559
 *
9560
 * @return true if the CRS is dynamic
9561
 *
9562
 * @since OGR 3.4.0
9563
 *
9564
 * @see HasPointMotionOperation()
9565
 */
9566
9567
bool OGRSpatialReference::IsDynamic() const
9568
9569
0
{
9570
0
    TAKE_OPTIONAL_LOCK();
9571
0
    bool isDynamic = false;
9572
0
    d->refreshProjObj();
9573
0
    d->demoteFromBoundCRS();
9574
0
    auto ctxt = d->getPROJContext();
9575
0
    PJ *horiz = nullptr;
9576
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9577
0
    {
9578
0
        horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9579
0
    }
9580
0
    else if (d->m_pj_crs)
9581
0
    {
9582
0
        horiz = proj_clone(ctxt, d->m_pj_crs);
9583
0
    }
9584
0
    if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9585
0
    {
9586
0
        auto baseCRS = proj_get_source_crs(ctxt, horiz);
9587
0
        if (baseCRS)
9588
0
        {
9589
0
            proj_destroy(horiz);
9590
0
            horiz = baseCRS;
9591
0
        }
9592
0
    }
9593
0
    auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9594
0
    if (datum)
9595
0
    {
9596
0
        const auto type = proj_get_type(datum);
9597
0
        isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9598
0
                    type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9599
0
        if (!isDynamic)
9600
0
        {
9601
0
            const char *auth_name = proj_get_id_auth_name(datum, 0);
9602
0
            const char *code = proj_get_id_code(datum, 0);
9603
0
            if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9604
0
                EQUAL(code, "6326"))
9605
0
            {
9606
0
                isDynamic = true;
9607
0
            }
9608
0
        }
9609
0
        proj_destroy(datum);
9610
0
    }
9611
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9612
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9613
0
    else
9614
0
    {
9615
0
        auto ensemble =
9616
0
            horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9617
0
        if (ensemble)
9618
0
        {
9619
0
            auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9620
0
            if (member)
9621
0
            {
9622
0
                const auto type = proj_get_type(member);
9623
0
                isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9624
0
                            type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9625
0
                proj_destroy(member);
9626
0
            }
9627
0
            proj_destroy(ensemble);
9628
0
        }
9629
0
    }
9630
0
#endif
9631
0
    proj_destroy(horiz);
9632
0
    d->undoDemoteFromBoundCRS();
9633
0
    return isDynamic;
9634
0
}
9635
9636
/************************************************************************/
9637
/*                           OSRIsDynamic()                             */
9638
/************************************************************************/
9639
/**
9640
 * \brief Check if a CRS is a dynamic CRS.
9641
 *
9642
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9643
 * plate-fixed.
9644
 *
9645
 * This function is the same as OGRSpatialReference::IsDynamic().
9646
 *
9647
 * @since OGR 3.4.0
9648
 */
9649
int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9650
9651
0
{
9652
0
    VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9653
9654
0
    return ToPointer(hSRS)->IsDynamic();
9655
0
}
9656
9657
/************************************************************************/
9658
/*                         HasPointMotionOperation()                    */
9659
/************************************************************************/
9660
9661
/**
9662
 * \brief Check if a CRS has at least an associated point motion operation.
9663
 *
9664
 * Some CRS are not formally declared as dynamic, but may behave as such
9665
 * in practice due to the presence of point motion operation, to perform
9666
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9667
 *
9668
 * @return true if the CRS has at least an associated point motion operation.
9669
 *
9670
 * @since OGR 3.8.0 and PROJ 9.4.0
9671
 *
9672
 * @see IsDynamic()
9673
 */
9674
9675
bool OGRSpatialReference::HasPointMotionOperation() const
9676
9677
0
{
9678
0
#if PROJ_VERSION_MAJOR > 9 ||                                                  \
9679
0
    (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9680
0
    TAKE_OPTIONAL_LOCK();
9681
0
    d->refreshProjObj();
9682
0
    d->demoteFromBoundCRS();
9683
0
    auto ctxt = d->getPROJContext();
9684
0
    auto res =
9685
0
        CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9686
0
    d->undoDemoteFromBoundCRS();
9687
0
    return res;
9688
#else
9689
    return false;
9690
#endif
9691
0
}
9692
9693
/************************************************************************/
9694
/*                      OSRHasPointMotionOperation()                    */
9695
/************************************************************************/
9696
9697
/**
9698
 * \brief Check if a CRS has at least an associated point motion operation.
9699
 *
9700
 * Some CRS are not formally declared as dynamic, but may behave as such
9701
 * in practice due to the presence of point motion operation, to perform
9702
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9703
 *
9704
 * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9705
 *
9706
 * @since OGR 3.8.0 and PROJ 9.4.0
9707
 */
9708
int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9709
9710
0
{
9711
0
    VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9712
9713
0
    return ToPointer(hSRS)->HasPointMotionOperation();
9714
0
}
9715
9716
/************************************************************************/
9717
/*                            CloneGeogCS()                             */
9718
/************************************************************************/
9719
9720
/**
9721
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9722
 * object.
9723
 *
9724
 * @return a new SRS, which becomes the responsibility of the caller.
9725
 */
9726
OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9727
9728
0
{
9729
0
    TAKE_OPTIONAL_LOCK();
9730
0
    d->refreshProjObj();
9731
0
    if (d->m_pj_crs)
9732
0
    {
9733
0
        if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9734
0
            return nullptr;
9735
9736
0
        auto geodCRS =
9737
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9738
0
        if (geodCRS)
9739
0
        {
9740
0
            OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9741
0
            if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9742
0
            {
9743
0
                PJ *hub_crs =
9744
0
                    proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9745
0
                PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9746
0
                                                     d->m_pj_crs);
9747
0
                auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9748
0
                                                      geodCRS, hub_crs, co);
9749
0
                proj_destroy(geodCRS);
9750
0
                geodCRS = temp;
9751
0
                proj_destroy(hub_crs);
9752
0
                proj_destroy(co);
9753
0
            }
9754
9755
            /* --------------------------------------------------------------------
9756
             */
9757
            /*      We have to reconstruct the GEOGCS node for geocentric */
9758
            /*      coordinate systems. */
9759
            /* --------------------------------------------------------------------
9760
             */
9761
0
            if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9762
0
            {
9763
0
                auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9764
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9765
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9766
0
                if (datum == nullptr)
9767
0
                {
9768
0
                    datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9769
0
                                                        geodCRS);
9770
0
                }
9771
0
#endif
9772
0
                if (datum)
9773
0
                {
9774
0
                    auto cs = proj_create_ellipsoidal_2D_cs(
9775
0
                        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9776
0
                        nullptr, 0);
9777
0
                    auto temp = proj_create_geographic_crs_from_datum(
9778
0
                        d->getPROJContext(), "unnamed", datum, cs);
9779
0
                    proj_destroy(datum);
9780
0
                    proj_destroy(cs);
9781
0
                    proj_destroy(geodCRS);
9782
0
                    geodCRS = temp;
9783
0
                }
9784
0
            }
9785
9786
0
            poNewSRS->d->setPjCRS(geodCRS);
9787
0
            if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9788
0
                poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9789
0
            return poNewSRS;
9790
0
        }
9791
0
    }
9792
0
    return nullptr;
9793
0
}
9794
9795
/************************************************************************/
9796
/*                           OSRCloneGeogCS()                           */
9797
/************************************************************************/
9798
/**
9799
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9800
 * object.
9801
 *
9802
 * This function is the same as OGRSpatialReference::CloneGeogCS().
9803
 */
9804
OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9805
9806
0
{
9807
0
    VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9808
9809
0
    return ToHandle(ToPointer(hSource)->CloneGeogCS());
9810
0
}
9811
9812
/************************************************************************/
9813
/*                            IsSameGeogCS()                            */
9814
/************************************************************************/
9815
9816
/**
9817
 * \brief Do the GeogCS'es match?
9818
 *
9819
 * This method is the same as the C function OSRIsSameGeogCS().
9820
 *
9821
 * @param poOther the SRS being compared against.
9822
 *
9823
 * @return TRUE if they are the same or FALSE otherwise.
9824
 */
9825
9826
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9827
9828
0
{
9829
0
    return IsSameGeogCS(poOther, nullptr);
9830
0
}
9831
9832
/**
9833
 * \brief Do the GeogCS'es match?
9834
 *
9835
 * This method is the same as the C function OSRIsSameGeogCS().
9836
 *
9837
 * @param poOther the SRS being compared against.
9838
 * @param papszOptions options. ignored
9839
 *
9840
 * @return TRUE if they are the same or FALSE otherwise.
9841
 */
9842
9843
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9844
                                      const char *const *papszOptions) const
9845
9846
0
{
9847
0
    TAKE_OPTIONAL_LOCK();
9848
9849
0
    CPL_IGNORE_RET_VAL(papszOptions);
9850
9851
0
    d->refreshProjObj();
9852
0
    poOther->d->refreshProjObj();
9853
0
    if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9854
0
        return FALSE;
9855
0
    if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9856
0
        d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9857
0
        poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9858
0
        poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9859
0
    {
9860
0
        return FALSE;
9861
0
    }
9862
9863
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9864
0
    auto otherGeodCRS =
9865
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9866
0
    if (!geodCRS || !otherGeodCRS)
9867
0
    {
9868
0
        proj_destroy(geodCRS);
9869
0
        proj_destroy(otherGeodCRS);
9870
0
        return FALSE;
9871
0
    }
9872
9873
0
    int ret = proj_is_equivalent_to(
9874
0
        geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9875
9876
0
    proj_destroy(geodCRS);
9877
0
    proj_destroy(otherGeodCRS);
9878
0
    return ret;
9879
0
}
9880
9881
/************************************************************************/
9882
/*                          OSRIsSameGeogCS()                           */
9883
/************************************************************************/
9884
9885
/**
9886
 * \brief Do the GeogCS'es match?
9887
 *
9888
 * This function is the same as OGRSpatialReference::IsSameGeogCS().
9889
 */
9890
int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9891
9892
0
{
9893
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9894
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9895
9896
0
    return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9897
0
}
9898
9899
/************************************************************************/
9900
/*                            IsSameVertCS()                            */
9901
/************************************************************************/
9902
9903
/**
9904
 * \brief Do the VertCS'es match?
9905
 *
9906
 * This method is the same as the C function OSRIsSameVertCS().
9907
 *
9908
 * @param poOther the SRS being compared against.
9909
 *
9910
 * @return TRUE if they are the same or FALSE otherwise.
9911
 */
9912
9913
int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9914
9915
0
{
9916
0
    TAKE_OPTIONAL_LOCK();
9917
9918
    /* -------------------------------------------------------------------- */
9919
    /*      Does the datum name match?                                      */
9920
    /* -------------------------------------------------------------------- */
9921
0
    const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9922
0
    const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9923
9924
0
    if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9925
0
        !EQUAL(pszThisValue, pszOtherValue))
9926
0
        return FALSE;
9927
9928
    /* -------------------------------------------------------------------- */
9929
    /*      Do the units match?                                             */
9930
    /* -------------------------------------------------------------------- */
9931
0
    pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9932
0
    if (pszThisValue == nullptr)
9933
0
        pszThisValue = "1.0";
9934
9935
0
    pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9936
0
    if (pszOtherValue == nullptr)
9937
0
        pszOtherValue = "1.0";
9938
9939
0
    if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9940
0
        return FALSE;
9941
9942
0
    return TRUE;
9943
0
}
9944
9945
/************************************************************************/
9946
/*                          OSRIsSameVertCS()                           */
9947
/************************************************************************/
9948
9949
/**
9950
 * \brief Do the VertCS'es match?
9951
 *
9952
 * This function is the same as OGRSpatialReference::IsSameVertCS().
9953
 */
9954
int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9955
9956
0
{
9957
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9958
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9959
9960
0
    return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9961
0
}
9962
9963
/************************************************************************/
9964
/*                               IsSame()                               */
9965
/************************************************************************/
9966
9967
/**
9968
 * \brief Do these two spatial references describe the same system ?
9969
 *
9970
 * @param poOtherSRS the SRS being compared to.
9971
 *
9972
 * @return TRUE if equivalent or FALSE otherwise.
9973
 */
9974
9975
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9976
9977
0
{
9978
0
    return IsSame(poOtherSRS, nullptr);
9979
0
}
9980
9981
/**
9982
 * \brief Do these two spatial references describe the same system ?
9983
 *
9984
 * This also takes into account the data axis to CRS axis mapping by default
9985
 *
9986
 * @param poOtherSRS the SRS being compared to.
9987
 * @param papszOptions options. NULL or NULL terminated list of options.
9988
 * Currently supported options are:
9989
 * <ul>
9990
 * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9991
 * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9992
 *     Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9993
 * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9994
 * </ul>
9995
 *
9996
 * @return TRUE if equivalent or FALSE otherwise.
9997
 */
9998
9999
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
10000
                                const char *const *papszOptions) const
10001
10002
0
{
10003
0
    TAKE_OPTIONAL_LOCK();
10004
10005
0
    d->refreshProjObj();
10006
0
    poOtherSRS->d->refreshProjObj();
10007
0
    if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
10008
0
        return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
10009
0
    if (!CPLTestBool(CSLFetchNameValueDef(
10010
0
            papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
10011
0
    {
10012
0
        if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
10013
0
            return false;
10014
0
    }
10015
10016
0
    if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
10017
0
                                          "IGNORE_COORDINATE_EPOCH", "NO")))
10018
0
    {
10019
0
        if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
10020
0
            return false;
10021
0
    }
10022
10023
0
    bool reboundSelf = false;
10024
0
    bool reboundOther = false;
10025
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
10026
0
        poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
10027
0
    {
10028
0
        d->demoteFromBoundCRS();
10029
0
        reboundSelf = true;
10030
0
    }
10031
0
    else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
10032
0
             poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
10033
0
    {
10034
0
        poOtherSRS->d->demoteFromBoundCRS();
10035
0
        reboundOther = true;
10036
0
    }
10037
10038
0
    PJ_COMPARISON_CRITERION criterion =
10039
0
        PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
10040
0
    const char *pszCriterion = CSLFetchNameValueDef(
10041
0
        papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
10042
0
    if (EQUAL(pszCriterion, "STRICT"))
10043
0
        criterion = PJ_COMP_STRICT;
10044
0
    else if (EQUAL(pszCriterion, "EQUIVALENT"))
10045
0
        criterion = PJ_COMP_EQUIVALENT;
10046
0
    else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
10047
0
    {
10048
0
        CPLError(CE_Warning, CPLE_NotSupported,
10049
0
                 "Unsupported value for CRITERION: %s", pszCriterion);
10050
0
    }
10051
0
    int ret =
10052
0
        proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
10053
0
    if (reboundSelf)
10054
0
        d->undoDemoteFromBoundCRS();
10055
0
    if (reboundOther)
10056
0
        poOtherSRS->d->undoDemoteFromBoundCRS();
10057
10058
0
    return ret;
10059
0
}
10060
10061
/************************************************************************/
10062
/*                             OSRIsSame()                              */
10063
/************************************************************************/
10064
10065
/**
10066
 * \brief Do these two spatial references describe the same system ?
10067
 *
10068
 * This function is the same as OGRSpatialReference::IsSame().
10069
 */
10070
int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
10071
10072
0
{
10073
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10074
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10075
10076
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
10077
0
}
10078
10079
/************************************************************************/
10080
/*                             OSRIsSameEx()                            */
10081
/************************************************************************/
10082
10083
/**
10084
 * \brief Do these two spatial references describe the same system ?
10085
 *
10086
 * This function is the same as OGRSpatialReference::IsSame().
10087
 */
10088
int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
10089
                const char *const *papszOptions)
10090
0
{
10091
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
10092
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
10093
10094
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
10095
0
}
10096
10097
/************************************************************************/
10098
/*                    convertToOtherProjection()                        */
10099
/************************************************************************/
10100
10101
/**
10102
 * \brief Convert to another equivalent projection
10103
 *
10104
 * Currently implemented:
10105
 * <ul>
10106
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10107
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10108
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10109
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10110
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10111
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10112
 * </ul>
10113
 *
10114
 * @param pszTargetProjection target projection.
10115
 * @param papszOptions lists of options. None supported currently.
10116
 * @return a new SRS, or NULL in case of error.
10117
 *
10118
 */
10119
OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10120
    const char *pszTargetProjection,
10121
    CPL_UNUSED const char *const *papszOptions) const
10122
0
{
10123
0
    TAKE_OPTIONAL_LOCK();
10124
10125
0
    if (pszTargetProjection == nullptr)
10126
0
        return nullptr;
10127
0
    int new_code;
10128
0
    if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10129
0
    {
10130
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10131
0
    }
10132
0
    else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10133
0
    {
10134
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10135
0
    }
10136
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10137
0
    {
10138
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10139
0
    }
10140
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10141
0
    {
10142
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10143
0
    }
10144
0
    else
10145
0
    {
10146
0
        return nullptr;
10147
0
    }
10148
10149
0
    d->refreshProjObj();
10150
0
    d->demoteFromBoundCRS();
10151
0
    OGRSpatialReference *poNewSRS = nullptr;
10152
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10153
0
    {
10154
0
        auto conv =
10155
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10156
0
        auto new_conv = proj_convert_conversion_to_other_method(
10157
0
            d->getPROJContext(), conv, new_code, nullptr);
10158
0
        proj_destroy(conv);
10159
0
        if (new_conv)
10160
0
        {
10161
0
            auto geodCRS =
10162
0
                proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10163
0
            auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10164
0
                                                     d->m_pj_crs);
10165
0
            if (geodCRS && cs)
10166
0
            {
10167
0
                auto new_proj_crs = proj_create_projected_crs(
10168
0
                    d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10169
0
                    new_conv, cs);
10170
0
                proj_destroy(new_conv);
10171
0
                if (new_proj_crs)
10172
0
                {
10173
0
                    poNewSRS = new OGRSpatialReference();
10174
10175
0
                    if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10176
0
                    {
10177
0
                        auto boundCRS = proj_crs_create_bound_crs(
10178
0
                            d->getPROJContext(), new_proj_crs,
10179
0
                            d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10180
0
                        if (boundCRS)
10181
0
                        {
10182
0
                            proj_destroy(new_proj_crs);
10183
0
                            new_proj_crs = boundCRS;
10184
0
                        }
10185
0
                    }
10186
10187
0
                    poNewSRS->d->setPjCRS(new_proj_crs);
10188
0
                }
10189
0
            }
10190
0
            proj_destroy(geodCRS);
10191
0
            proj_destroy(cs);
10192
0
        }
10193
0
    }
10194
0
    d->undoDemoteFromBoundCRS();
10195
0
    return poNewSRS;
10196
0
}
10197
10198
/************************************************************************/
10199
/*                    OSRConvertToOtherProjection()                     */
10200
/************************************************************************/
10201
10202
/**
10203
 * \brief Convert to another equivalent projection
10204
 *
10205
 * Currently implemented:
10206
 * <ul>
10207
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10208
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10209
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10210
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10211
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10212
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10213
 * </ul>
10214
 *
10215
 * @param hSRS source SRS
10216
 * @param pszTargetProjection target projection.
10217
 * @param papszOptions lists of options. None supported currently.
10218
 * @return a new SRS, or NULL in case of error.
10219
 *
10220
 */
10221
OGRSpatialReferenceH
10222
OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10223
                            const char *pszTargetProjection,
10224
                            const char *const *papszOptions)
10225
0
{
10226
0
    VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10227
0
    return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10228
0
        pszTargetProjection, papszOptions));
10229
0
}
10230
10231
/************************************************************************/
10232
/*                           OSRFindMatches()                           */
10233
/************************************************************************/
10234
10235
/**
10236
 * \brief Try to identify a match between the passed SRS and a related SRS
10237
 * in a catalog.
10238
 *
10239
 * Matching may be partial, or may fail.
10240
 * Returned entries will be sorted by decreasing match confidence (first
10241
 * entry has the highest match confidence).
10242
 *
10243
 * The exact way matching is done may change in future versions. Starting with
10244
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
10245
 *
10246
 * This function is the same as OGRSpatialReference::FindMatches().
10247
 *
10248
 * @param hSRS SRS to match
10249
 * @param papszOptions NULL terminated list of options or NULL
10250
 * @param pnEntries Output parameter. Number of values in the returned array.
10251
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10252
 * will be allocated to an array of *pnEntries whose values between 0 and 100
10253
 * indicate the confidence in the match. 100 is the highest confidence level.
10254
 * The array must be freed with CPLFree().
10255
 *
10256
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10257
 * with OSRFreeSRSArray()
10258
 *
10259
 */
10260
OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10261
                                     char **papszOptions, int *pnEntries,
10262
                                     int **ppanMatchConfidence)
10263
0
{
10264
0
    if (pnEntries)
10265
0
        *pnEntries = 0;
10266
0
    if (ppanMatchConfidence)
10267
0
        *ppanMatchConfidence = nullptr;
10268
0
    VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10269
10270
0
    OGRSpatialReference *poSRS = ToPointer(hSRS);
10271
0
    return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10272
0
}
10273
10274
/************************************************************************/
10275
/*                           OSRFreeSRSArray()                          */
10276
/************************************************************************/
10277
10278
/**
10279
 * \brief Free return of OSRIdentifyMatches()
10280
 *
10281
 * @param pahSRS array of SRS (must be NULL terminated)
10282
 */
10283
void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10284
0
{
10285
0
    if (pahSRS != nullptr)
10286
0
    {
10287
0
        for (int i = 0; pahSRS[i] != nullptr; ++i)
10288
0
        {
10289
0
            OSRRelease(pahSRS[i]);
10290
0
        }
10291
0
        CPLFree(pahSRS);
10292
0
    }
10293
0
}
10294
10295
/************************************************************************/
10296
/*                         FindBestMatch()                              */
10297
/************************************************************************/
10298
10299
/**
10300
 * \brief Try to identify the best match between the passed SRS and a related
10301
 * SRS in a catalog.
10302
 *
10303
 * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10304
 * of filtering its output.
10305
 * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10306
 * will be considered. If there is a single match, it is returned.
10307
 * If there are several matches, only return the one under the
10308
 * pszPreferredAuthority, if there is a single one under that authority.
10309
 *
10310
 * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10311
 * 100). If set to 0, 90 is used.
10312
 * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10313
 * "EPSG" is used.
10314
 * @param papszOptions NULL terminated list of options or NULL. No option is
10315
 * defined at time of writing.
10316
 *
10317
 * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10318
 *
10319
 * @since GDAL 3.6
10320
 * @see OGRSpatialReference::FindMatches()
10321
 */
10322
OGRSpatialReference *
10323
OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10324
                                   const char *pszPreferredAuthority,
10325
                                   CSLConstList papszOptions) const
10326
0
{
10327
0
    TAKE_OPTIONAL_LOCK();
10328
10329
0
    CPL_IGNORE_RET_VAL(papszOptions);  // ignored for now.
10330
10331
0
    if (nMinimumMatchConfidence == 0)
10332
0
        nMinimumMatchConfidence = 90;
10333
0
    if (pszPreferredAuthority == nullptr)
10334
0
        pszPreferredAuthority = "EPSG";
10335
10336
    // Try to identify the CRS with the database
10337
0
    int nEntries = 0;
10338
0
    int *panConfidence = nullptr;
10339
0
    OGRSpatialReferenceH *pahSRS =
10340
0
        FindMatches(nullptr, &nEntries, &panConfidence);
10341
0
    if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10342
0
    {
10343
0
        std::vector<double> adfTOWGS84(7);
10344
0
        if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10345
0
        {
10346
0
            adfTOWGS84.clear();
10347
0
        }
10348
10349
0
        auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10350
10351
0
        auto poBaseGeogCRS =
10352
0
            std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10353
0
        if (poBaseGeogCRS)
10354
0
        {
10355
            // If the base geographic SRS of the SRS is EPSG:4326
10356
            // with TOWGS84[0,0,0,0,0,0], then just use the official
10357
            // SRS code
10358
            // Same with EPSG:4258 (ETRS89), since it's the only known
10359
            // TOWGS84[] style transformation to WGS 84, and given the
10360
            // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10361
            // chance that a non-NULL TOWGS84[] will emerge.
10362
0
            const char *pszAuthorityName = nullptr;
10363
0
            const char *pszAuthorityCode = nullptr;
10364
0
            const char *pszBaseAuthorityName = nullptr;
10365
0
            const char *pszBaseAuthorityCode = nullptr;
10366
0
            const char *pszBaseName = poBaseGeogCRS->GetName();
10367
0
            if (adfTOWGS84 == std::vector<double>(7) &&
10368
0
                (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) !=
10369
0
                    nullptr &&
10370
0
                EQUAL(pszAuthorityName, "EPSG") &&
10371
0
                (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) !=
10372
0
                    nullptr &&
10373
0
                (pszBaseAuthorityName =
10374
0
                     poBaseGeogCRS->GetAuthorityName(nullptr)) != nullptr &&
10375
0
                EQUAL(pszBaseAuthorityName, "EPSG") &&
10376
0
                (pszBaseAuthorityCode =
10377
0
                     poBaseGeogCRS->GetAuthorityCode(nullptr)) != nullptr &&
10378
0
                (EQUAL(pszBaseAuthorityCode, "4326") ||
10379
0
                 EQUAL(pszBaseAuthorityCode, "4258") ||
10380
                 // For ETRS89-XXX [...] new CRS added in EPSG 12.033+
10381
0
                 (pszBaseName && STARTS_WITH(pszBaseName, "ETRS89"))))
10382
0
            {
10383
0
                poSRS->importFromEPSG(atoi(pszAuthorityCode));
10384
0
            }
10385
0
        }
10386
10387
0
        CPLFree(pahSRS);
10388
0
        CPLFree(panConfidence);
10389
10390
0
        return poSRS;
10391
0
    }
10392
0
    else
10393
0
    {
10394
        // If there are several matches >= nMinimumMatchConfidence, take the
10395
        // only one that is under pszPreferredAuthority
10396
0
        int iBestEntry = -1;
10397
0
        for (int i = 0; i < nEntries; i++)
10398
0
        {
10399
0
            if (panConfidence[i] >= nMinimumMatchConfidence)
10400
0
            {
10401
0
                const char *pszAuthName =
10402
0
                    OGRSpatialReference::FromHandle(pahSRS[i])
10403
0
                        ->GetAuthorityName(nullptr);
10404
0
                if (pszAuthName != nullptr &&
10405
0
                    EQUAL(pszAuthName, pszPreferredAuthority))
10406
0
                {
10407
0
                    if (iBestEntry < 0)
10408
0
                        iBestEntry = i;
10409
0
                    else
10410
0
                    {
10411
0
                        iBestEntry = -1;
10412
0
                        break;
10413
0
                    }
10414
0
                }
10415
0
            }
10416
0
        }
10417
0
        if (iBestEntry >= 0)
10418
0
        {
10419
0
            auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10420
0
            OSRFreeSRSArray(pahSRS);
10421
0
            CPLFree(panConfidence);
10422
0
            return poRet;
10423
0
        }
10424
0
    }
10425
0
    OSRFreeSRSArray(pahSRS);
10426
0
    CPLFree(panConfidence);
10427
0
    return nullptr;
10428
0
}
10429
10430
/************************************************************************/
10431
/*                             SetTOWGS84()                             */
10432
/************************************************************************/
10433
10434
/**
10435
 * \brief Set the Bursa-Wolf conversion to WGS84.
10436
 *
10437
 * This will create the TOWGS84 node as a child of the DATUM.  It will fail
10438
 * if there is no existing DATUM node. It will replace
10439
 * an existing TOWGS84 node if there is one.
10440
 *
10441
 * The parameters have the same meaning as EPSG transformation 9606
10442
 * (Position Vector 7-param. transformation).
10443
 *
10444
 * This method is the same as the C function OSRSetTOWGS84().
10445
 *
10446
 * @param dfDX X child in meters.
10447
 * @param dfDY Y child in meters.
10448
 * @param dfDZ Z child in meters.
10449
 * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10450
 * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10451
 * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10452
 * @param dfPPM scaling factor (parts per million).
10453
 *
10454
 * @return OGRERR_NONE on success.
10455
 */
10456
10457
OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10458
                                       double dfEX, double dfEY, double dfEZ,
10459
                                       double dfPPM)
10460
10461
0
{
10462
0
    TAKE_OPTIONAL_LOCK();
10463
10464
0
    d->refreshProjObj();
10465
0
    if (d->m_pj_crs == nullptr)
10466
0
    {
10467
0
        return OGRERR_FAILURE;
10468
0
    }
10469
10470
    // Remove existing BoundCRS
10471
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10472
0
    {
10473
0
        auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10474
0
        if (!baseCRS)
10475
0
            return OGRERR_FAILURE;
10476
0
        d->setPjCRS(baseCRS);
10477
0
    }
10478
10479
0
    PJ_PARAM_DESCRIPTION params[7];
10480
10481
0
    params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10482
0
    params[0].auth_name = "EPSG";
10483
0
    params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10484
0
    params[0].value = dfDX;
10485
0
    params[0].unit_name = "metre";
10486
0
    params[0].unit_conv_factor = 1.0;
10487
0
    params[0].unit_type = PJ_UT_LINEAR;
10488
10489
0
    params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10490
0
    params[1].auth_name = "EPSG";
10491
0
    params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10492
0
    params[1].value = dfDY;
10493
0
    params[1].unit_name = "metre";
10494
0
    params[1].unit_conv_factor = 1.0;
10495
0
    params[1].unit_type = PJ_UT_LINEAR;
10496
10497
0
    params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10498
0
    params[2].auth_name = "EPSG";
10499
0
    params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10500
0
    params[2].value = dfDZ;
10501
0
    params[2].unit_name = "metre";
10502
0
    params[2].unit_conv_factor = 1.0;
10503
0
    params[2].unit_type = PJ_UT_LINEAR;
10504
10505
0
    params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10506
0
    params[3].auth_name = "EPSG";
10507
0
    params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10508
0
    params[3].value = dfEX;
10509
0
    params[3].unit_name = "arc-second";
10510
0
    params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10511
0
    params[3].unit_type = PJ_UT_ANGULAR;
10512
10513
0
    params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10514
0
    params[4].auth_name = "EPSG";
10515
0
    params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10516
0
    params[4].value = dfEY;
10517
0
    params[4].unit_name = "arc-second";
10518
0
    params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10519
0
    params[4].unit_type = PJ_UT_ANGULAR;
10520
10521
0
    params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10522
0
    params[5].auth_name = "EPSG";
10523
0
    params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10524
0
    params[5].value = dfEZ;
10525
0
    params[5].unit_name = "arc-second";
10526
0
    params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10527
0
    params[5].unit_type = PJ_UT_ANGULAR;
10528
10529
0
    params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10530
0
    params[6].auth_name = "EPSG";
10531
0
    params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10532
0
    params[6].value = dfPPM;
10533
0
    params[6].unit_name = "parts per million";
10534
0
    params[6].unit_conv_factor = 1e-6;
10535
0
    params[6].unit_type = PJ_UT_SCALE;
10536
10537
0
    auto sourceCRS =
10538
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10539
0
    if (!sourceCRS)
10540
0
    {
10541
0
        return OGRERR_FAILURE;
10542
0
    }
10543
10544
0
    const auto sourceType = proj_get_type(sourceCRS);
10545
10546
0
    auto targetCRS = proj_create_from_database(
10547
0
        d->getPROJContext(), "EPSG",
10548
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS   ? "4326"
10549
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10550
0
                                                  : "4978",
10551
0
        PJ_CATEGORY_CRS, false, nullptr);
10552
0
    if (!targetCRS)
10553
0
    {
10554
0
        proj_destroy(sourceCRS);
10555
0
        return OGRERR_FAILURE;
10556
0
    }
10557
10558
0
    CPLString osMethodCode;
10559
0
    osMethodCode.Printf("%d",
10560
0
                        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10561
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10562
0
                        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10563
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10564
0
                            : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10565
10566
0
    auto transf = proj_create_transformation(
10567
0
        d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10568
0
        sourceCRS, targetCRS, nullptr,
10569
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10570
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10571
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10572
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10573
0
            : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10574
0
        "EPSG", osMethodCode.c_str(), 7, params, -1);
10575
0
    proj_destroy(sourceCRS);
10576
0
    if (!transf)
10577
0
    {
10578
0
        proj_destroy(targetCRS);
10579
0
        return OGRERR_FAILURE;
10580
0
    }
10581
10582
0
    auto newBoundCRS = proj_crs_create_bound_crs(
10583
0
        d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10584
0
    proj_destroy(transf);
10585
0
    proj_destroy(targetCRS);
10586
0
    if (!newBoundCRS)
10587
0
    {
10588
0
        return OGRERR_FAILURE;
10589
0
    }
10590
10591
0
    d->setPjCRS(newBoundCRS);
10592
0
    return OGRERR_NONE;
10593
0
}
10594
10595
/************************************************************************/
10596
/*                           OSRSetTOWGS84()                            */
10597
/************************************************************************/
10598
10599
/**
10600
 * \brief Set the Bursa-Wolf conversion to WGS84.
10601
 *
10602
 * This function is the same as OGRSpatialReference::SetTOWGS84().
10603
 */
10604
OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10605
                     double dfDZ, double dfEX, double dfEY, double dfEZ,
10606
                     double dfPPM)
10607
10608
0
{
10609
0
    VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10610
10611
0
    return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10612
0
                                       dfPPM);
10613
0
}
10614
10615
/************************************************************************/
10616
/*                             GetTOWGS84()                             */
10617
/************************************************************************/
10618
10619
/**
10620
 * \brief Fetch TOWGS84 parameters, if available.
10621
 *
10622
 * The parameters have the same meaning as EPSG transformation 9606
10623
 * (Position Vector 7-param. transformation).
10624
 *
10625
 * @param padfCoeff array into which up to 7 coefficients are placed.
10626
 * @param nCoeffCount size of padfCoeff - defaults to 7.
10627
 *
10628
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10629
 * TOWGS84 node available.
10630
 */
10631
10632
OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10633
10634
0
{
10635
0
    TAKE_OPTIONAL_LOCK();
10636
10637
0
    d->refreshProjObj();
10638
0
    if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10639
0
        return OGRERR_FAILURE;
10640
10641
0
    memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10642
10643
0
    auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10644
0
    int success = proj_coordoperation_get_towgs84_values(
10645
0
        d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10646
0
    proj_destroy(transf);
10647
10648
0
    return success ? OGRERR_NONE : OGRERR_FAILURE;
10649
0
}
10650
10651
/************************************************************************/
10652
/*                           OSRGetTOWGS84()                            */
10653
/************************************************************************/
10654
10655
/**
10656
 * \brief Fetch TOWGS84 parameters, if available.
10657
 *
10658
 * This function is the same as OGRSpatialReference::GetTOWGS84().
10659
 */
10660
OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10661
                     int nCoeffCount)
10662
10663
0
{
10664
0
    VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10665
10666
0
    return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10667
0
}
10668
10669
/************************************************************************/
10670
/*                         IsAngularParameter()                         */
10671
/************************************************************************/
10672
10673
/** Is the passed projection parameter an angular one?
10674
 *
10675
 * @return TRUE or FALSE
10676
 */
10677
10678
/* static */
10679
int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10680
10681
0
{
10682
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10683
0
        STARTS_WITH_CI(pszParameterName, "lati") ||
10684
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10685
0
        STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10686
0
        EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10687
0
        EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10688
0
        return TRUE;
10689
10690
0
    return FALSE;
10691
0
}
10692
10693
/************************************************************************/
10694
/*                        IsLongitudeParameter()                        */
10695
/************************************************************************/
10696
10697
/** Is the passed projection parameter an angular longitude
10698
 * (relative to a prime meridian)?
10699
 *
10700
 * @return TRUE or FALSE
10701
 */
10702
10703
/* static */
10704
int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10705
10706
0
{
10707
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10708
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10709
0
        return TRUE;
10710
10711
0
    return FALSE;
10712
0
}
10713
10714
/************************************************************************/
10715
/*                         IsLinearParameter()                          */
10716
/************************************************************************/
10717
10718
/** Is the passed projection parameter an linear one measured in meters or
10719
 * some similar linear measure.
10720
 *
10721
 * @return TRUE or FALSE
10722
 */
10723
10724
/* static */
10725
int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10726
10727
0
{
10728
0
    if (STARTS_WITH_CI(pszParameterName, "false_") ||
10729
0
        EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10730
0
        return TRUE;
10731
10732
0
    return FALSE;
10733
0
}
10734
10735
/************************************************************************/
10736
/*                            GetNormInfo()                             */
10737
/************************************************************************/
10738
10739
/**
10740
 * \brief Set the internal information for normalizing linear, and angular
10741
 * values.
10742
 */
10743
void OGRSpatialReference::GetNormInfo() const
10744
10745
0
{
10746
0
    TAKE_OPTIONAL_LOCK();
10747
10748
0
    if (d->bNormInfoSet)
10749
0
        return;
10750
10751
    /* -------------------------------------------------------------------- */
10752
    /*      Initialize values.                                              */
10753
    /* -------------------------------------------------------------------- */
10754
0
    d->bNormInfoSet = TRUE;
10755
10756
0
    d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10757
0
    d->dfToMeter = GetLinearUnits(nullptr);
10758
0
    d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10759
0
    if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10760
0
        d->dfToDegrees = 1.0;
10761
0
}
10762
10763
/************************************************************************/
10764
/*                            GetExtension()                            */
10765
/************************************************************************/
10766
10767
/**
10768
 * \brief Fetch extension value.
10769
 *
10770
 * Fetch the value of the named EXTENSION item for the identified
10771
 * target node.
10772
 *
10773
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10774
 * @param pszName the name of the extension being fetched.
10775
 * @param pszDefault the value to return if the extension is not found.
10776
 *
10777
 * @return node value if successful or pszDefault on failure.
10778
 */
10779
10780
const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10781
                                              const char *pszName,
10782
                                              const char *pszDefault) const
10783
10784
0
{
10785
0
    TAKE_OPTIONAL_LOCK();
10786
10787
    /* -------------------------------------------------------------------- */
10788
    /*      Find the target node.                                           */
10789
    /* -------------------------------------------------------------------- */
10790
0
    const OGR_SRSNode *poNode =
10791
0
        pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10792
10793
0
    if (poNode == nullptr)
10794
0
        return nullptr;
10795
10796
    /* -------------------------------------------------------------------- */
10797
    /*      Fetch matching EXTENSION if there is one.                       */
10798
    /* -------------------------------------------------------------------- */
10799
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10800
0
    {
10801
0
        const OGR_SRSNode *poChild = poNode->GetChild(i);
10802
10803
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10804
0
            poChild->GetChildCount() >= 2)
10805
0
        {
10806
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10807
0
                return poChild->GetChild(1)->GetValue();
10808
0
        }
10809
0
    }
10810
10811
0
    return pszDefault;
10812
0
}
10813
10814
/************************************************************************/
10815
/*                            SetExtension()                            */
10816
/************************************************************************/
10817
/**
10818
 * \brief Set extension value.
10819
 *
10820
 * Set the value of the named EXTENSION item for the identified
10821
 * target node.
10822
 *
10823
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10824
 * @param pszName the name of the extension being fetched.
10825
 * @param pszValue the value to set
10826
 *
10827
 * @return OGRERR_NONE on success
10828
 */
10829
10830
OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10831
                                         const char *pszName,
10832
                                         const char *pszValue)
10833
10834
0
{
10835
0
    TAKE_OPTIONAL_LOCK();
10836
10837
    /* -------------------------------------------------------------------- */
10838
    /*      Find the target node.                                           */
10839
    /* -------------------------------------------------------------------- */
10840
0
    OGR_SRSNode *poNode = nullptr;
10841
10842
0
    if (pszTargetKey == nullptr)
10843
0
        poNode = GetRoot();
10844
0
    else
10845
0
        poNode = GetAttrNode(pszTargetKey);
10846
10847
0
    if (poNode == nullptr)
10848
0
        return OGRERR_FAILURE;
10849
10850
    /* -------------------------------------------------------------------- */
10851
    /*      Fetch matching EXTENSION if there is one.                       */
10852
    /* -------------------------------------------------------------------- */
10853
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10854
0
    {
10855
0
        OGR_SRSNode *poChild = poNode->GetChild(i);
10856
10857
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10858
0
            poChild->GetChildCount() >= 2)
10859
0
        {
10860
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10861
0
            {
10862
0
                poChild->GetChild(1)->SetValue(pszValue);
10863
0
                return OGRERR_NONE;
10864
0
            }
10865
0
        }
10866
0
    }
10867
10868
    /* -------------------------------------------------------------------- */
10869
    /*      Create a new EXTENSION node.                                    */
10870
    /* -------------------------------------------------------------------- */
10871
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10872
0
    poAuthNode->AddChild(new OGR_SRSNode(pszName));
10873
0
    poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10874
10875
0
    poNode->AddChild(poAuthNode);
10876
10877
0
    return OGRERR_NONE;
10878
0
}
10879
10880
/************************************************************************/
10881
/*                             OSRCleanup()                             */
10882
/************************************************************************/
10883
10884
static void CleanupSRSWGS84Mutex();
10885
10886
/**
10887
 * \brief Cleanup cached SRS related memory.
10888
 *
10889
 * This function will attempt to cleanup any cache spatial reference
10890
 * related information, such as cached tables of coordinate systems.
10891
 *
10892
 * This function should not be called concurrently with any other GDAL/OGR
10893
 * function. It is meant at being called once before process termination
10894
 * (typically from the main thread). CPLCleanupTLS() might be used to clean
10895
 * thread-specific resources before thread termination.
10896
 */
10897
void OSRCleanup(void)
10898
10899
0
{
10900
0
    OGRCTDumpStatistics();
10901
0
    CSVDeaccess(nullptr);
10902
0
    CleanupSRSWGS84Mutex();
10903
0
    OSRCTCleanCache();
10904
0
    OSRCleanupTLSContext();
10905
0
}
10906
10907
/************************************************************************/
10908
/*                              GetAxesCount()                          */
10909
/************************************************************************/
10910
10911
/**
10912
 * \brief Return the number of axis of the coordinate system of the CRS.
10913
 *
10914
 * @since GDAL 3.0
10915
 */
10916
int OGRSpatialReference::GetAxesCount() const
10917
0
{
10918
0
    TAKE_OPTIONAL_LOCK();
10919
10920
0
    int axisCount = 0;
10921
0
    d->refreshProjObj();
10922
0
    if (d->m_pj_crs == nullptr)
10923
0
    {
10924
0
        return 0;
10925
0
    }
10926
0
    d->demoteFromBoundCRS();
10927
0
    auto ctxt = d->getPROJContext();
10928
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10929
0
    {
10930
0
        for (int i = 0;; i++)
10931
0
        {
10932
0
            auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10933
0
            if (!subCRS)
10934
0
                break;
10935
0
            if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10936
0
            {
10937
0
                auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10938
0
                if (baseCRS)
10939
0
                {
10940
0
                    proj_destroy(subCRS);
10941
0
                    subCRS = baseCRS;
10942
0
                }
10943
0
            }
10944
0
            auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10945
0
            if (cs)
10946
0
            {
10947
0
                axisCount += proj_cs_get_axis_count(ctxt, cs);
10948
0
                proj_destroy(cs);
10949
0
            }
10950
0
            proj_destroy(subCRS);
10951
0
        }
10952
0
    }
10953
0
    else
10954
0
    {
10955
0
        auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10956
0
        if (cs)
10957
0
        {
10958
0
            axisCount = proj_cs_get_axis_count(ctxt, cs);
10959
0
            proj_destroy(cs);
10960
0
        }
10961
0
    }
10962
0
    d->undoDemoteFromBoundCRS();
10963
0
    return axisCount;
10964
0
}
10965
10966
/************************************************************************/
10967
/*                           OSRGetAxesCount()                          */
10968
/************************************************************************/
10969
10970
/**
10971
 * \brief Return the number of axis of the coordinate system of the CRS.
10972
 *
10973
 * This method is the equivalent of the C++ method
10974
 * OGRSpatialReference::GetAxesCount()
10975
 *
10976
 * @since GDAL 3.1
10977
 */
10978
int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10979
10980
0
{
10981
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10982
10983
0
    return ToPointer(hSRS)->GetAxesCount();
10984
0
}
10985
10986
/************************************************************************/
10987
/*                              GetAxis()                               */
10988
/************************************************************************/
10989
10990
/**
10991
 * \brief Fetch the orientation of one axis.
10992
 *
10993
 * Fetches the request axis (iAxis - zero based) from the
10994
 * indicated portion of the coordinate system (pszTargetKey) which
10995
 * should be either "GEOGCS" or "PROJCS".
10996
 *
10997
 * No CPLError is issued on routine failures (such as not finding the AXIS).
10998
 *
10999
 * This method is equivalent to the C function OSRGetAxis().
11000
 *
11001
 * @param pszTargetKey the coordinate system part to query ("PROJCS" or
11002
 * "GEOGCS").
11003
 * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
11004
 * @param peOrientation location into which to place the fetch orientation, may
11005
 * be NULL.
11006
 * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
11007
 * factor. May be NULL. Only set if pszTargetKey == NULL
11008
 *
11009
 * @return the name of the axis or NULL on failure.
11010
 */
11011
11012
const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
11013
                                         OGRAxisOrientation *peOrientation,
11014
                                         double *pdfConvUnit) const
11015
11016
0
{
11017
0
    TAKE_OPTIONAL_LOCK();
11018
11019
0
    if (peOrientation != nullptr)
11020
0
        *peOrientation = OAO_Other;
11021
0
    if (pdfConvUnit != nullptr)
11022
0
        *pdfConvUnit = 0;
11023
11024
0
    d->refreshProjObj();
11025
0
    if (d->m_pj_crs == nullptr)
11026
0
    {
11027
0
        return nullptr;
11028
0
    }
11029
11030
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
11031
0
    if (pszTargetKey == nullptr && iAxis <= 2)
11032
0
    {
11033
0
        auto ctxt = d->getPROJContext();
11034
11035
0
        int iAxisModified = iAxis;
11036
11037
0
        d->demoteFromBoundCRS();
11038
11039
0
        PJ *cs = nullptr;
11040
0
        if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
11041
0
        {
11042
0
            auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
11043
0
            if (horizCRS)
11044
0
            {
11045
0
                if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
11046
0
                {
11047
0
                    auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
11048
0
                    if (baseCRS)
11049
0
                    {
11050
0
                        proj_destroy(horizCRS);
11051
0
                        horizCRS = baseCRS;
11052
0
                    }
11053
0
                }
11054
0
                cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
11055
0
                proj_destroy(horizCRS);
11056
0
                if (cs)
11057
0
                {
11058
0
                    if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
11059
0
                    {
11060
0
                        iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
11061
0
                        proj_destroy(cs);
11062
0
                        cs = nullptr;
11063
0
                    }
11064
0
                }
11065
0
            }
11066
11067
0
            if (cs == nullptr)
11068
0
            {
11069
0
                auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
11070
0
                if (vertCRS)
11071
0
                {
11072
0
                    if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
11073
0
                    {
11074
0
                        auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
11075
0
                        if (baseCRS)
11076
0
                        {
11077
0
                            proj_destroy(vertCRS);
11078
0
                            vertCRS = baseCRS;
11079
0
                        }
11080
0
                    }
11081
11082
0
                    cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
11083
0
                    proj_destroy(vertCRS);
11084
0
                }
11085
0
            }
11086
0
        }
11087
0
        else
11088
0
        {
11089
0
            cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
11090
0
        }
11091
11092
0
        if (cs)
11093
0
        {
11094
0
            const char *pszName = nullptr;
11095
0
            const char *pszOrientation = nullptr;
11096
0
            double dfConvFactor = 0.0;
11097
0
            proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
11098
0
                                  &pszOrientation, &dfConvFactor, nullptr,
11099
0
                                  nullptr, nullptr);
11100
11101
0
            if (pdfConvUnit != nullptr)
11102
0
            {
11103
0
                *pdfConvUnit = dfConvFactor;
11104
0
            }
11105
11106
0
            if (pszName && pszOrientation)
11107
0
            {
11108
0
                d->m_osAxisName[iAxis] = pszName;
11109
0
                if (peOrientation)
11110
0
                {
11111
0
                    if (EQUAL(pszOrientation, "NORTH"))
11112
0
                        *peOrientation = OAO_North;
11113
0
                    else if (EQUAL(pszOrientation, "EAST"))
11114
0
                        *peOrientation = OAO_East;
11115
0
                    else if (EQUAL(pszOrientation, "SOUTH"))
11116
0
                        *peOrientation = OAO_South;
11117
0
                    else if (EQUAL(pszOrientation, "WEST"))
11118
0
                        *peOrientation = OAO_West;
11119
0
                    else if (EQUAL(pszOrientation, "UP"))
11120
0
                        *peOrientation = OAO_Up;
11121
0
                    else if (EQUAL(pszOrientation, "DOWN"))
11122
0
                        *peOrientation = OAO_Down;
11123
0
                }
11124
0
                proj_destroy(cs);
11125
0
                d->undoDemoteFromBoundCRS();
11126
0
                return d->m_osAxisName[iAxis].c_str();
11127
0
            }
11128
0
            proj_destroy(cs);
11129
0
        }
11130
0
        d->undoDemoteFromBoundCRS();
11131
0
    }
11132
11133
    /* -------------------------------------------------------------------- */
11134
    /*      Find the target node.                                           */
11135
    /* -------------------------------------------------------------------- */
11136
0
    const OGR_SRSNode *poNode = nullptr;
11137
11138
0
    if (pszTargetKey == nullptr)
11139
0
        poNode = GetRoot();
11140
0
    else
11141
0
        poNode = GetAttrNode(pszTargetKey);
11142
11143
0
    if (poNode == nullptr)
11144
0
        return nullptr;
11145
11146
    /* -------------------------------------------------------------------- */
11147
    /*      Find desired child AXIS.                                        */
11148
    /* -------------------------------------------------------------------- */
11149
0
    const OGR_SRSNode *poAxis = nullptr;
11150
0
    const int nChildCount = poNode->GetChildCount();
11151
11152
0
    for (int iChild = 0; iChild < nChildCount; iChild++)
11153
0
    {
11154
0
        const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11155
11156
0
        if (!EQUAL(poChild->GetValue(), "AXIS"))
11157
0
            continue;
11158
11159
0
        if (iAxis == 0)
11160
0
        {
11161
0
            poAxis = poChild;
11162
0
            break;
11163
0
        }
11164
0
        iAxis--;
11165
0
    }
11166
11167
0
    if (poAxis == nullptr)
11168
0
        return nullptr;
11169
11170
0
    if (poAxis->GetChildCount() < 2)
11171
0
        return nullptr;
11172
11173
    /* -------------------------------------------------------------------- */
11174
    /*      Extract name and orientation if possible.                       */
11175
    /* -------------------------------------------------------------------- */
11176
0
    if (peOrientation != nullptr)
11177
0
    {
11178
0
        const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11179
11180
0
        if (EQUAL(pszOrientation, "NORTH"))
11181
0
            *peOrientation = OAO_North;
11182
0
        else if (EQUAL(pszOrientation, "EAST"))
11183
0
            *peOrientation = OAO_East;
11184
0
        else if (EQUAL(pszOrientation, "SOUTH"))
11185
0
            *peOrientation = OAO_South;
11186
0
        else if (EQUAL(pszOrientation, "WEST"))
11187
0
            *peOrientation = OAO_West;
11188
0
        else if (EQUAL(pszOrientation, "UP"))
11189
0
            *peOrientation = OAO_Up;
11190
0
        else if (EQUAL(pszOrientation, "DOWN"))
11191
0
            *peOrientation = OAO_Down;
11192
0
        else if (EQUAL(pszOrientation, "OTHER"))
11193
0
            *peOrientation = OAO_Other;
11194
0
        else
11195
0
        {
11196
0
            CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11197
0
                     pszOrientation);
11198
0
        }
11199
0
    }
11200
11201
0
    return poAxis->GetChild(0)->GetValue();
11202
0
}
11203
11204
/************************************************************************/
11205
/*                             OSRGetAxis()                             */
11206
/************************************************************************/
11207
11208
/**
11209
 * \brief Fetch the orientation of one axis.
11210
 *
11211
 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11212
 */
11213
const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11214
                       int iAxis, OGRAxisOrientation *peOrientation)
11215
11216
0
{
11217
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11218
11219
0
    return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11220
0
}
11221
11222
/************************************************************************/
11223
/*                         OSRAxisEnumToName()                          */
11224
/************************************************************************/
11225
11226
/**
11227
 * \brief Return the string representation for the OGRAxisOrientation
11228
 * enumeration.
11229
 *
11230
 * For example "NORTH" for OAO_North.
11231
 *
11232
 * @return an internal string
11233
 */
11234
const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11235
11236
0
{
11237
0
    if (eOrientation == OAO_North)
11238
0
        return "NORTH";
11239
0
    if (eOrientation == OAO_East)
11240
0
        return "EAST";
11241
0
    if (eOrientation == OAO_South)
11242
0
        return "SOUTH";
11243
0
    if (eOrientation == OAO_West)
11244
0
        return "WEST";
11245
0
    if (eOrientation == OAO_Up)
11246
0
        return "UP";
11247
0
    if (eOrientation == OAO_Down)
11248
0
        return "DOWN";
11249
0
    if (eOrientation == OAO_Other)
11250
0
        return "OTHER";
11251
11252
0
    return "UNKNOWN";
11253
0
}
11254
11255
/************************************************************************/
11256
/*                              SetAxes()                               */
11257
/************************************************************************/
11258
11259
/**
11260
 * \brief Set the axes for a coordinate system.
11261
 *
11262
 * Set the names, and orientations of the axes for either a projected
11263
 * (PROJCS) or geographic (GEOGCS) coordinate system.
11264
 *
11265
 * This method is equivalent to the C function OSRSetAxes().
11266
 *
11267
 * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11268
 * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11269
 * @param eXAxisOrientation normally OAO_East.
11270
 * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11271
 * @param eYAxisOrientation normally OAO_North.
11272
 *
11273
 * @return OGRERR_NONE on success or an error code.
11274
 */
11275
11276
OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11277
                                    const char *pszXAxisName,
11278
                                    OGRAxisOrientation eXAxisOrientation,
11279
                                    const char *pszYAxisName,
11280
                                    OGRAxisOrientation eYAxisOrientation)
11281
11282
0
{
11283
0
    TAKE_OPTIONAL_LOCK();
11284
11285
    /* -------------------------------------------------------------------- */
11286
    /*      Find the target node.                                           */
11287
    /* -------------------------------------------------------------------- */
11288
0
    OGR_SRSNode *poNode = nullptr;
11289
11290
0
    if (pszTargetKey == nullptr)
11291
0
        poNode = GetRoot();
11292
0
    else
11293
0
        poNode = GetAttrNode(pszTargetKey);
11294
11295
0
    if (poNode == nullptr)
11296
0
        return OGRERR_FAILURE;
11297
11298
    /* -------------------------------------------------------------------- */
11299
    /*      Strip any existing AXIS children.                               */
11300
    /* -------------------------------------------------------------------- */
11301
0
    while (poNode->FindChild("AXIS") >= 0)
11302
0
        poNode->DestroyChild(poNode->FindChild("AXIS"));
11303
11304
    /* -------------------------------------------------------------------- */
11305
    /*      Insert desired axes                                             */
11306
    /* -------------------------------------------------------------------- */
11307
0
    OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11308
11309
0
    poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11310
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11311
11312
0
    poNode->AddChild(poAxis);
11313
11314
0
    poAxis = new OGR_SRSNode("AXIS");
11315
11316
0
    poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11317
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11318
11319
0
    poNode->AddChild(poAxis);
11320
11321
0
    return OGRERR_NONE;
11322
0
}
11323
11324
/************************************************************************/
11325
/*                             OSRSetAxes()                             */
11326
/************************************************************************/
11327
/**
11328
 * \brief Set the axes for a coordinate system.
11329
 *
11330
 * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11331
 */
11332
OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11333
                  const char *pszXAxisName,
11334
                  OGRAxisOrientation eXAxisOrientation,
11335
                  const char *pszYAxisName,
11336
                  OGRAxisOrientation eYAxisOrientation)
11337
0
{
11338
0
    VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11339
11340
0
    return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11341
0
                                    eXAxisOrientation, pszYAxisName,
11342
0
                                    eYAxisOrientation);
11343
0
}
11344
11345
/************************************************************************/
11346
/*                       OSRExportToMICoordSys()                        */
11347
/************************************************************************/
11348
/**
11349
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11350
 *
11351
 * This method is the equivalent of the C++ method
11352
 * OGRSpatialReference::exportToMICoordSys
11353
 */
11354
OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11355
11356
0
{
11357
0
    VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11358
11359
0
    *ppszReturn = nullptr;
11360
11361
0
    return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11362
0
}
11363
11364
/************************************************************************/
11365
/*                         exportToMICoordSys()                         */
11366
/************************************************************************/
11367
11368
/**
11369
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11370
 *
11371
 * Note that the returned WKT string should be freed with
11372
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
11373
 *
11374
 * This method is the same as the C function OSRExportToMICoordSys().
11375
 *
11376
 * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11377
 * definition will be assigned.
11378
 *
11379
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11380
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11381
 */
11382
11383
OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11384
11385
0
{
11386
0
    *ppszResult = MITABSpatialRef2CoordSys(this);
11387
0
    if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11388
0
        return OGRERR_NONE;
11389
11390
0
    return OGRERR_FAILURE;
11391
0
}
11392
11393
/************************************************************************/
11394
/*                       OSRImportFromMICoordSys()                      */
11395
/************************************************************************/
11396
/**
11397
 * \brief Import Mapinfo style CoordSys definition.
11398
 *
11399
 * This method is the equivalent of the C++ method
11400
 * OGRSpatialReference::importFromMICoordSys
11401
 */
11402
11403
OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11404
                               const char *pszCoordSys)
11405
11406
0
{
11407
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11408
11409
0
    return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11410
0
}
11411
11412
/************************************************************************/
11413
/*                        importFromMICoordSys()                        */
11414
/************************************************************************/
11415
11416
/**
11417
 * \brief Import Mapinfo style CoordSys definition.
11418
 *
11419
 * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11420
 * definition string.
11421
 *
11422
 * This method is the equivalent of the C function OSRImportFromMICoordSys().
11423
 *
11424
 * @param pszCoordSys Mapinfo style CoordSys definition string.
11425
 *
11426
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11427
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11428
 */
11429
11430
OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11431
11432
0
{
11433
0
    OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11434
11435
0
    if (poResult == nullptr)
11436
0
        return OGRERR_FAILURE;
11437
11438
0
    *this = *poResult;
11439
0
    delete poResult;
11440
11441
0
    return OGRERR_NONE;
11442
0
}
11443
11444
/************************************************************************/
11445
/*                        OSRCalcInvFlattening()                        */
11446
/************************************************************************/
11447
11448
/**
11449
 * \brief Compute inverse flattening from semi-major and semi-minor axis
11450
 *
11451
 * @param dfSemiMajor Semi-major axis length.
11452
 * @param dfSemiMinor Semi-minor axis length.
11453
 *
11454
 * @return inverse flattening, or 0 if both axis are equal.
11455
 */
11456
11457
double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11458
0
{
11459
0
    if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11460
0
        return 0;
11461
0
    if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11462
0
    {
11463
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11464
0
                 "OSRCalcInvFlattening(): Wrong input values");
11465
0
        return 0;
11466
0
    }
11467
11468
0
    return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11469
0
}
11470
11471
/************************************************************************/
11472
/*                        OSRCalcInvFlattening()                        */
11473
/************************************************************************/
11474
11475
/**
11476
 * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11477
 *
11478
 * @param dfSemiMajor Semi-major axis length.
11479
 * @param dfInvFlattening Inverse flattening or 0 for sphere.
11480
 *
11481
 * @return semi-minor axis
11482
 */
11483
11484
double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11485
                                         double dfInvFlattening)
11486
0
{
11487
0
    if (fabs(dfInvFlattening) < 0.000000000001)
11488
0
        return dfSemiMajor;
11489
0
    if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11490
0
    {
11491
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11492
0
                 "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11493
0
        return dfSemiMajor;
11494
0
    }
11495
11496
0
    return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11497
0
}
11498
11499
/************************************************************************/
11500
/*                        GetWGS84SRS()                                 */
11501
/************************************************************************/
11502
11503
static OGRSpatialReference *poSRSWGS84 = nullptr;
11504
static CPLMutex *hMutex = nullptr;
11505
11506
/**
11507
 * \brief Returns an instance of a SRS object with WGS84 WKT.
11508
 *
11509
 * Note: the instance will have
11510
 * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11511
 *
11512
 * The reference counter of the returned object is not increased by this
11513
 * operation.
11514
 *
11515
 * @return instance.
11516
 */
11517
11518
OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11519
0
{
11520
0
    CPLMutexHolderD(&hMutex);
11521
0
    if (poSRSWGS84 == nullptr)
11522
0
    {
11523
0
        poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11524
0
        poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11525
0
    }
11526
0
    return poSRSWGS84;
11527
0
}
11528
11529
/************************************************************************/
11530
/*                        CleanupSRSWGS84Mutex()                       */
11531
/************************************************************************/
11532
11533
static void CleanupSRSWGS84Mutex()
11534
0
{
11535
0
    if (hMutex != nullptr)
11536
0
    {
11537
0
        poSRSWGS84->Release();
11538
0
        poSRSWGS84 = nullptr;
11539
0
        CPLDestroyMutex(hMutex);
11540
0
        hMutex = nullptr;
11541
0
    }
11542
0
}
11543
11544
/************************************************************************/
11545
/*                         OSRImportFromProj4()                         */
11546
/************************************************************************/
11547
/**
11548
 * \brief Import PROJ coordinate string.
11549
 *
11550
 * This function is the same as OGRSpatialReference::importFromProj4().
11551
 */
11552
OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11553
11554
0
{
11555
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11556
11557
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11558
0
}
11559
11560
/************************************************************************/
11561
/*                          importFromProj4()                           */
11562
/************************************************************************/
11563
11564
/**
11565
 * \brief Import PROJ coordinate string.
11566
 *
11567
 * The OGRSpatialReference is initialized from the passed PROJs style
11568
 * coordinate system string.
11569
 *
11570
 * Example:
11571
 *   pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11572
 *
11573
 * It is also possible to import "+init=epsg:n" style definitions. Those are
11574
 * a legacy syntax that should be avoided in the future. In particular they will
11575
 * result in CRS objects whose axis order might not correspond to the official
11576
 * EPSG axis order.
11577
 *
11578
 * This method is the equivalent of the C function OSRImportFromProj4().
11579
 *
11580
 * @param pszProj4 the PROJ style string.
11581
 *
11582
 * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11583
 */
11584
11585
OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11586
11587
0
{
11588
0
    TAKE_OPTIONAL_LOCK();
11589
11590
0
    if (strlen(pszProj4) >= 10000)
11591
0
    {
11592
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11593
0
        return OGRERR_CORRUPT_DATA;
11594
0
    }
11595
11596
    /* -------------------------------------------------------------------- */
11597
    /*      Clear any existing definition.                                  */
11598
    /* -------------------------------------------------------------------- */
11599
0
    Clear();
11600
11601
0
    CPLString osProj4(pszProj4);
11602
0
    if (osProj4.find("type=crs") == std::string::npos)
11603
0
    {
11604
0
        osProj4 += " +type=crs";
11605
0
    }
11606
11607
0
    if (osProj4.find("+init=epsg:") != std::string::npos &&
11608
0
        getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11609
0
    {
11610
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11611
0
                     "+init=epsg:XXXX syntax is deprecated. It might return "
11612
0
                     "a CRS with a non-EPSG compliant axis order.");
11613
0
    }
11614
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11615
0
    d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11616
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11617
0
    return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11618
0
}
11619
11620
/************************************************************************/
11621
/*                          OSRExportToProj4()                          */
11622
/************************************************************************/
11623
/**
11624
 * \brief Export coordinate system in PROJ.4 legacy format.
11625
 *
11626
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11627
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11628
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11629
 * will be missing most of the time. PROJ strings to encode CRS should be
11630
 * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11631
 * is the recommended way.
11632
 *
11633
 * This function is the same as OGRSpatialReference::exportToProj4().
11634
 */
11635
OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11636
                                    char **ppszReturn)
11637
11638
0
{
11639
0
    VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11640
11641
0
    *ppszReturn = nullptr;
11642
11643
0
    return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11644
0
}
11645
11646
/************************************************************************/
11647
/*                           exportToProj4()                            */
11648
/************************************************************************/
11649
11650
/**
11651
 * \brief Export coordinate system in PROJ.4 legacy format.
11652
 *
11653
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11654
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11655
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11656
 * will be missing most of the time. PROJ strings to encode CRS should be
11657
 * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11658
 * representation is the recommended way.
11659
 *
11660
 * Converts the loaded coordinate reference system into PROJ format
11661
 * to the extent possible.  The string returned in ppszProj4 should be
11662
 * deallocated by the caller with CPLFree() when no longer needed.
11663
 *
11664
 * LOCAL_CS coordinate systems are not translatable.  An empty string
11665
 * will be returned along with OGRERR_NONE.
11666
 *
11667
 * Special processing for Transverse Mercator:
11668
 * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11669
 * set to YES, the PROJ definition built from the SRS will use the +approx flag
11670
 * for the tmerc and utm projection methods, rather than the more accurate
11671
 * method.
11672
 *
11673
 * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11674
 * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11675
 * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11676
 * added. This automatic addition may be disabled by setting the
11677
 * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11678
 *
11679
 * This method is the equivalent of the C function OSRExportToProj4().
11680
 *
11681
 * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11682
 * will be assigned.
11683
 *
11684
 * @return OGRERR_NONE on success or an error code on failure.
11685
 */
11686
11687
OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11688
11689
0
{
11690
    // In the past calling this method was thread-safe, even if we never
11691
    // guaranteed it. Now proj_as_proj_string() will cache the result
11692
    // internally, so this is no longer thread-safe.
11693
0
    std::lock_guard oLock(d->m_mutex);
11694
11695
0
    d->refreshProjObj();
11696
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11697
0
    {
11698
0
        *ppszProj4 = CPLStrdup("");
11699
0
        return OGRERR_FAILURE;
11700
0
    }
11701
11702
    // OSR_USE_ETMERC is here just for legacy
11703
0
    bool bForceApproxTMerc = false;
11704
0
    const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11705
0
    if (pszUseETMERC && pszUseETMERC[0])
11706
0
    {
11707
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11708
0
                     "OSR_USE_ETMERC is a legacy configuration option, which "
11709
0
                     "now has only effect when set to NO (YES is the default). "
11710
0
                     "Use OSR_USE_APPROX_TMERC=YES instead");
11711
0
        bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11712
0
    }
11713
0
    else
11714
0
    {
11715
0
        const char *pszUseApproxTMERC =
11716
0
            CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11717
0
        if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11718
0
        {
11719
0
            bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11720
0
        }
11721
0
    }
11722
0
    const char *options[] = {
11723
0
        bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11724
11725
0
    const char *projString = proj_as_proj_string(
11726
0
        d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11727
11728
0
    PJ *boundCRS = nullptr;
11729
0
    if (projString &&
11730
0
        (strstr(projString, "+datum=") == nullptr ||
11731
0
         d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11732
0
        CPLTestBool(
11733
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11734
0
    {
11735
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11736
0
            d->getPROJContext(), d->m_pj_crs, true,
11737
0
            strstr(projString, "+datum=") == nullptr);
11738
0
        if (boundCRS)
11739
0
        {
11740
0
            projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11741
0
                                             PJ_PROJ_4, options);
11742
0
        }
11743
0
    }
11744
11745
0
    if (projString == nullptr)
11746
0
    {
11747
0
        *ppszProj4 = CPLStrdup("");
11748
0
        proj_destroy(boundCRS);
11749
0
        return OGRERR_FAILURE;
11750
0
    }
11751
0
    *ppszProj4 = CPLStrdup(projString);
11752
0
    proj_destroy(boundCRS);
11753
0
    char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11754
0
    if (pszTypeCrs)
11755
0
        *pszTypeCrs = '\0';
11756
0
    return OGRERR_NONE;
11757
0
}
11758
11759
/************************************************************************/
11760
/*                            morphToESRI()                             */
11761
/************************************************************************/
11762
/**
11763
 * \brief Convert in place to ESRI WKT format.
11764
 *
11765
 * The value nodes of this coordinate system are modified in various manners
11766
 * more closely map onto the ESRI concept of WKT format.  This includes
11767
 * renaming a variety of projections and arguments, and stripping out
11768
 * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11769
 *
11770
 * \note Since GDAL 3.0, this function has only user-visible effects at
11771
 * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11772
 * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11773
 *
11774
 * This does the same as the C function OSRMorphToESRI().
11775
 *
11776
 * @return OGRERR_NONE unless something goes badly wrong.
11777
 * @deprecated
11778
 */
11779
11780
OGRErr OGRSpatialReference::morphToESRI()
11781
11782
0
{
11783
0
    TAKE_OPTIONAL_LOCK();
11784
11785
0
    d->refreshProjObj();
11786
0
    d->setMorphToESRI(true);
11787
11788
0
    return OGRERR_NONE;
11789
0
}
11790
11791
/************************************************************************/
11792
/*                           OSRMorphToESRI()                           */
11793
/************************************************************************/
11794
11795
/**
11796
 * \brief Convert in place to ESRI WKT format.
11797
 *
11798
 * This function is the same as the C++ method
11799
 * OGRSpatialReference::morphToESRI().
11800
 */
11801
OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11802
11803
0
{
11804
0
    VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11805
11806
0
    return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11807
0
}
11808
11809
/************************************************************************/
11810
/*                           morphFromESRI()                            */
11811
/************************************************************************/
11812
11813
/**
11814
 * \brief Convert in place from ESRI WKT format.
11815
 *
11816
 * The value notes of this coordinate system are modified in various manners
11817
 * to adhere more closely to the WKT standard.  This mostly involves
11818
 * translating a variety of ESRI names for projections, arguments and
11819
 * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11820
 * translation of EPSG to WKT for the CT specification.
11821
 *
11822
 * \note Since GDAL 3.0, this function is essentially a no-operation, since
11823
 * morphing from ESRI is automatically done by importFromWkt(). Its only
11824
 * effect is to undo the effect of a potential prior call to morphToESRI().
11825
 *
11826
 * This does the same as the C function OSRMorphFromESRI().
11827
 *
11828
 * @return OGRERR_NONE unless something goes badly wrong.
11829
 * @deprecated
11830
 */
11831
11832
OGRErr OGRSpatialReference::morphFromESRI()
11833
11834
0
{
11835
0
    TAKE_OPTIONAL_LOCK();
11836
11837
0
    d->refreshProjObj();
11838
0
    d->setMorphToESRI(false);
11839
11840
0
    return OGRERR_NONE;
11841
0
}
11842
11843
/************************************************************************/
11844
/*                          OSRMorphFromESRI()                          */
11845
/************************************************************************/
11846
11847
/**
11848
 * \brief Convert in place from ESRI WKT format.
11849
 *
11850
 * This function is the same as the C++ method
11851
 * OGRSpatialReference::morphFromESRI().
11852
 */
11853
OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11854
11855
0
{
11856
0
    VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11857
11858
0
    return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11859
0
}
11860
11861
/************************************************************************/
11862
/*                            FindMatches()                             */
11863
/************************************************************************/
11864
11865
/**
11866
 * \brief Try to identify a match between the passed SRS and a related SRS
11867
 * in a catalog.
11868
 *
11869
 * Matching may be partial, or may fail.
11870
 * Returned entries will be sorted by decreasing match confidence (first
11871
 * entry has the highest match confidence).
11872
 *
11873
 * The exact way matching is done may change in future versions. Starting with
11874
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
11875
 *
11876
 * This method is the same as OSRFindMatches().
11877
 *
11878
 * @param papszOptions NULL terminated list of options or NULL
11879
 * @param pnEntries Output parameter. Number of values in the returned array.
11880
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11881
 * will be allocated to an array of *pnEntries whose values between 0 and 100
11882
 * indicate the confidence in the match. 100 is the highest confidence level.
11883
 * The array must be freed with CPLFree().
11884
 *
11885
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11886
 * with OSRFreeSRSArray()
11887
 *
11888
 *
11889
 * @see OGRSpatialReference::FindBestMatch()
11890
 */
11891
OGRSpatialReferenceH *
11892
OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11893
                                 int **ppanMatchConfidence) const
11894
0
{
11895
0
    TAKE_OPTIONAL_LOCK();
11896
11897
0
    CPL_IGNORE_RET_VAL(papszOptions);
11898
11899
0
    if (pnEntries)
11900
0
        *pnEntries = 0;
11901
0
    if (ppanMatchConfidence)
11902
0
        *ppanMatchConfidence = nullptr;
11903
11904
0
    d->refreshProjObj();
11905
0
    if (!d->m_pj_crs)
11906
0
        return nullptr;
11907
11908
0
    int *panConfidence = nullptr;
11909
0
    auto ctxt = d->getPROJContext();
11910
0
    auto list =
11911
0
        proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11912
0
    if (!list)
11913
0
        return nullptr;
11914
11915
0
    const int nMatches = proj_list_get_count(list);
11916
11917
0
    if (pnEntries)
11918
0
        *pnEntries = static_cast<int>(nMatches);
11919
0
    OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11920
0
        CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11921
0
    if (ppanMatchConfidence)
11922
0
    {
11923
0
        *ppanMatchConfidence =
11924
0
            static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11925
0
    }
11926
11927
0
    bool bSortAgain = false;
11928
11929
0
    for (int i = 0; i < nMatches; i++)
11930
0
    {
11931
0
        PJ *obj = proj_list_get(ctxt, list, i);
11932
0
        CPLAssert(obj);
11933
0
        OGRSpatialReference *poSRS = new OGRSpatialReference();
11934
0
        poSRS->d->setPjCRS(obj);
11935
0
        pahRet[i] = ToHandle(poSRS);
11936
11937
        // Identify matches that only differ by axis order
11938
0
        if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11939
0
            poSRS->GetAxesCount() == 2 &&
11940
0
            GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11941
0
        {
11942
0
            OGRAxisOrientation eThisAxis0 = OAO_Other;
11943
0
            OGRAxisOrientation eThisAxis1 = OAO_Other;
11944
0
            OGRAxisOrientation eSRSAxis0 = OAO_Other;
11945
0
            OGRAxisOrientation eSRSAxis1 = OAO_Other;
11946
0
            GetAxis(nullptr, 0, &eThisAxis0);
11947
0
            GetAxis(nullptr, 1, &eThisAxis1);
11948
0
            poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11949
0
            poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11950
0
            if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11951
0
                eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11952
0
            {
11953
0
                auto pj_crs_normalized =
11954
0
                    proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11955
0
                if (pj_crs_normalized)
11956
0
                {
11957
0
                    if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11958
0
                                              PJ_COMP_EQUIVALENT))
11959
0
                    {
11960
0
                        bSortAgain = true;
11961
0
                        panConfidence[i] = 90;
11962
0
                        poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11963
0
                    }
11964
0
                    proj_destroy(pj_crs_normalized);
11965
0
                }
11966
0
            }
11967
0
        }
11968
11969
0
        if (ppanMatchConfidence)
11970
0
            (*ppanMatchConfidence)[i] = panConfidence[i];
11971
0
    }
11972
11973
0
    if (bSortAgain)
11974
0
    {
11975
0
        std::vector<int> anIndices;
11976
0
        for (int i = 0; i < nMatches; ++i)
11977
0
            anIndices.push_back(i);
11978
11979
0
        std::stable_sort(anIndices.begin(), anIndices.end(),
11980
0
                         [&panConfidence](int i, int j)
11981
0
                         { return panConfidence[i] > panConfidence[j]; });
11982
11983
0
        OGRSpatialReferenceH *pahRetSorted =
11984
0
            static_cast<OGRSpatialReferenceH *>(
11985
0
                CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11986
0
        for (int i = 0; i < nMatches; ++i)
11987
0
        {
11988
0
            pahRetSorted[i] = pahRet[anIndices[i]];
11989
0
            if (ppanMatchConfidence)
11990
0
                (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11991
0
        }
11992
0
        CPLFree(pahRet);
11993
0
        pahRet = pahRetSorted;
11994
0
    }
11995
11996
0
    pahRet[nMatches] = nullptr;
11997
0
    proj_list_destroy(list);
11998
0
    proj_int_list_destroy(panConfidence);
11999
12000
0
    return pahRet;
12001
0
}
12002
12003
/************************************************************************/
12004
/*                          importFromEPSGA()                           */
12005
/************************************************************************/
12006
12007
/**
12008
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12009
 * code.
12010
 *
12011
 * This method will initialize the spatial reference based on the
12012
 * passed in EPSG CRS code found in the PROJ database.
12013
 *
12014
 * Since GDAL 3.0, this method is identical to importFromEPSG().
12015
 *
12016
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12017
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12018
 * such method available for the CRS. This behavior might not always be
12019
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12020
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12021
 * The AddGuessedTOWGS84() method can also be used for that purpose.
12022
 *
12023
 * The method will also by default substitute a deprecated EPSG code by its
12024
 * non-deprecated replacement. If this behavior is not desired, the
12025
 * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
12026
 *
12027
 * This method is the same as the C function OSRImportFromEPSGA().
12028
 *
12029
 * @param nCode a CRS code.
12030
 *
12031
 * @return OGRERR_NONE on success, or an error code on failure.
12032
 */
12033
12034
OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
12035
12036
0
{
12037
0
    TAKE_OPTIONAL_LOCK();
12038
12039
0
    Clear();
12040
12041
0
    const char *pszUseNonDeprecated =
12042
0
        CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
12043
0
    const bool bUseNonDeprecated =
12044
0
        CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
12045
0
    const bool bAddTOWGS84 = CPLTestBool(
12046
0
        CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
12047
0
    auto tlsCache = OSRGetProjTLSCache();
12048
0
    if (tlsCache)
12049
0
    {
12050
0
        auto cachedObj =
12051
0
            tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
12052
0
        if (cachedObj)
12053
0
        {
12054
0
            d->setPjCRS(cachedObj);
12055
0
            return OGRERR_NONE;
12056
0
        }
12057
0
    }
12058
12059
0
    CPLString osCode;
12060
0
    osCode.Printf("%d", nCode);
12061
0
    PJ *obj;
12062
0
    constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
12063
0
    if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
12064
0
    {
12065
0
        obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12066
0
                                        osCode.c_str(), PJ_CATEGORY_CRS, true,
12067
0
                                        nullptr);
12068
0
        if (!obj)
12069
0
        {
12070
0
            return OGRERR_FAILURE;
12071
0
        }
12072
0
    }
12073
0
    else
12074
0
    {
12075
        // Likely to be an ESRI CRS...
12076
0
        CPLErr eLastErrorType = CE_None;
12077
0
        CPLErrorNum eLastErrorNum = CPLE_None;
12078
0
        std::string osLastErrorMsg;
12079
0
        bool bIsESRI = false;
12080
0
        {
12081
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
12082
0
            CPLErrorReset();
12083
0
            obj = proj_create_from_database(d->getPROJContext(), "EPSG",
12084
0
                                            osCode.c_str(), PJ_CATEGORY_CRS,
12085
0
                                            true, nullptr);
12086
0
            if (!obj)
12087
0
            {
12088
0
                eLastErrorType = CPLGetLastErrorType();
12089
0
                eLastErrorNum = CPLGetLastErrorNo();
12090
0
                osLastErrorMsg = CPLGetLastErrorMsg();
12091
0
                obj = proj_create_from_database(d->getPROJContext(), "ESRI",
12092
0
                                                osCode.c_str(), PJ_CATEGORY_CRS,
12093
0
                                                true, nullptr);
12094
0
                if (obj)
12095
0
                    bIsESRI = true;
12096
0
            }
12097
0
        }
12098
0
        if (!obj)
12099
0
        {
12100
0
            if (eLastErrorType != CE_None)
12101
0
                CPLError(eLastErrorType, eLastErrorNum, "%s",
12102
0
                         osLastErrorMsg.c_str());
12103
0
            return OGRERR_FAILURE;
12104
0
        }
12105
0
        if (bIsESRI)
12106
0
        {
12107
0
            CPLError(CE_Warning, CPLE_AppDefined,
12108
0
                     "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
12109
0
                     "Assuming ESRI:%d was meant",
12110
0
                     nCode, nCode, nCode);
12111
0
        }
12112
0
    }
12113
12114
0
    if (bUseNonDeprecated && proj_is_deprecated(obj))
12115
0
    {
12116
0
        auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
12117
0
        if (list)
12118
0
        {
12119
0
            const auto count = proj_list_get_count(list);
12120
0
            if (count == 1)
12121
0
            {
12122
0
                auto nonDeprecated =
12123
0
                    proj_list_get(d->getPROJContext(), list, 0);
12124
0
                if (nonDeprecated)
12125
0
                {
12126
0
                    if (pszUseNonDeprecated == nullptr)
12127
0
                    {
12128
0
                        const char *pszNewAuth =
12129
0
                            proj_get_id_auth_name(nonDeprecated, 0);
12130
0
                        const char *pszNewCode =
12131
0
                            proj_get_id_code(nonDeprecated, 0);
12132
0
                        CPLError(CE_Warning, CPLE_AppDefined,
12133
0
                                 "CRS EPSG:%d is deprecated. "
12134
0
                                 "Its non-deprecated replacement %s:%s "
12135
0
                                 "will be used instead. "
12136
0
                                 "To use the original CRS, set the "
12137
0
                                 "OSR_USE_NON_DEPRECATED "
12138
0
                                 "configuration option to NO.",
12139
0
                                 nCode, pszNewAuth ? pszNewAuth : "(null)",
12140
0
                                 pszNewCode ? pszNewCode : "(null)");
12141
0
                    }
12142
0
                    proj_destroy(obj);
12143
0
                    obj = nonDeprecated;
12144
0
                }
12145
0
            }
12146
0
        }
12147
0
        proj_list_destroy(list);
12148
0
    }
12149
12150
0
    if (bAddTOWGS84)
12151
0
    {
12152
0
        auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12153
0
                                                           obj, nullptr);
12154
0
        if (boundCRS)
12155
0
        {
12156
0
            proj_destroy(obj);
12157
0
            obj = boundCRS;
12158
0
        }
12159
0
    }
12160
12161
0
    d->setPjCRS(obj);
12162
12163
0
    if (tlsCache)
12164
0
    {
12165
0
        tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12166
0
                                     obj);
12167
0
    }
12168
12169
0
    return OGRERR_NONE;
12170
0
}
12171
12172
/************************************************************************/
12173
/*                          AddGuessedTOWGS84()                         */
12174
/************************************************************************/
12175
12176
/**
12177
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12178
 * to WGS84.
12179
 *
12180
 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12181
 * to WGS84 when there is one and only one such method available for the CRS.
12182
 * Note: this is more restrictive to how GDAL < 3 worked.
12183
 *
12184
 * This method is the same as the C function OSRAddGuessedTOWGS84().
12185
 *
12186
 * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12187
 * already a transformation to WGS84 or none matching could be found).
12188
 *
12189
 * @since GDAL 3.0.3
12190
 */
12191
OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12192
0
{
12193
0
    TAKE_OPTIONAL_LOCK();
12194
12195
0
    d->refreshProjObj();
12196
0
    if (!d->m_pj_crs)
12197
0
        return OGRERR_FAILURE;
12198
0
    auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12199
0
        d->getPROJContext(), d->m_pj_crs, false, true);
12200
0
    if (!boundCRS)
12201
0
    {
12202
0
        return OGRERR_FAILURE;
12203
0
    }
12204
0
    d->setPjCRS(boundCRS);
12205
0
    return OGRERR_NONE;
12206
0
}
12207
12208
/************************************************************************/
12209
/*                         OSRImportFromEPSGA()                         */
12210
/************************************************************************/
12211
12212
/**
12213
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12214
 * to WGS84.
12215
 *
12216
 * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12217
 *
12218
 * @since GDAL 3.0.3
12219
 */
12220
12221
OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12222
12223
0
{
12224
0
    VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12225
12226
0
    return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12227
0
}
12228
12229
/************************************************************************/
12230
/*                         OSRImportFromEPSGA()                         */
12231
/************************************************************************/
12232
12233
/**
12234
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12235
 * code.
12236
 *
12237
 * This function is the same as OGRSpatialReference::importFromEPSGA().
12238
 */
12239
12240
OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12241
12242
0
{
12243
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12244
12245
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12246
0
}
12247
12248
/************************************************************************/
12249
/*                           importFromEPSG()                           */
12250
/************************************************************************/
12251
12252
/**
12253
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12254
 * code.
12255
 *
12256
 * This method will initialize the spatial reference based on the
12257
 * passed in EPSG CRS code found in the PROJ database.
12258
 *
12259
 * This method is the same as the C function OSRImportFromEPSG().
12260
 *
12261
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12262
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12263
 * such method available for the CRS. This behavior might not always be
12264
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12265
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12266
 *
12267
 * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12268
 *
12269
 * @return OGRERR_NONE on success, or an error code on failure.
12270
 */
12271
12272
OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12273
12274
0
{
12275
0
    return importFromEPSGA(nCode);
12276
0
}
12277
12278
/************************************************************************/
12279
/*                         OSRImportFromEPSG()                          */
12280
/************************************************************************/
12281
12282
/**
12283
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12284
 * code.
12285
 *
12286
 * This function is the same as OGRSpatialReference::importFromEPSG().
12287
 */
12288
12289
OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12290
12291
0
{
12292
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12293
12294
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12295
0
}
12296
12297
/************************************************************************/
12298
/*                        EPSGTreatsAsLatLong()                         */
12299
/************************************************************************/
12300
12301
/**
12302
 * \brief This method returns TRUE if this geographic coordinate
12303
 * system should be treated as having lat/long coordinate ordering.
12304
 *
12305
 * Currently this returns TRUE for all geographic coordinate systems
12306
 * with axes set defining it as lat, long (prior to GDAL 3.10, it
12307
 * also checked that the CRS had belonged to EPSG authority, but this check
12308
 * has now been removed).
12309
 *
12310
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12311
 * geographic CRS imported with importFromEPSG() would cause this method to
12312
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12313
 * is now equivalent to importFromEPSGA().
12314
 *
12315
 * FALSE will be returned for all coordinate systems that are not geographic,
12316
 * or whose axes ordering is not latitude, longitude.
12317
 *
12318
 * This method is the same as the C function OSREPSGTreatsAsLatLong().
12319
 *
12320
 * @return TRUE or FALSE.
12321
 */
12322
12323
int OGRSpatialReference::EPSGTreatsAsLatLong() const
12324
12325
0
{
12326
0
    TAKE_OPTIONAL_LOCK();
12327
12328
0
    if (!IsGeographic())
12329
0
        return FALSE;
12330
12331
0
    d->demoteFromBoundCRS();
12332
12333
0
    bool ret = false;
12334
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12335
0
    {
12336
0
        auto horizCRS =
12337
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12338
0
        if (horizCRS)
12339
0
        {
12340
0
            auto cs =
12341
0
                proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
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,
12347
0
                                          nullptr, 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
12358
0
            proj_destroy(horizCRS);
12359
0
        }
12360
0
    }
12361
0
    else
12362
0
    {
12363
0
        auto cs =
12364
0
            proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12365
0
        if (cs)
12366
0
        {
12367
0
            const char *pszDirection = nullptr;
12368
0
            if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12369
0
                                      nullptr, &pszDirection, nullptr, nullptr,
12370
0
                                      nullptr, nullptr))
12371
0
            {
12372
0
                if (EQUAL(pszDirection, "north"))
12373
0
                {
12374
0
                    ret = true;
12375
0
                }
12376
0
            }
12377
12378
0
            proj_destroy(cs);
12379
0
        }
12380
0
    }
12381
0
    d->undoDemoteFromBoundCRS();
12382
12383
0
    return ret;
12384
0
}
12385
12386
/************************************************************************/
12387
/*                       OSREPSGTreatsAsLatLong()                       */
12388
/************************************************************************/
12389
12390
/**
12391
 * \brief This function returns TRUE if this geographic coordinate
12392
 * system should be treated as having lat/long coordinate ordering.
12393
 *
12394
 * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12395
 */
12396
12397
int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12398
12399
0
{
12400
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12401
12402
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12403
0
}
12404
12405
/************************************************************************/
12406
/*                     EPSGTreatsAsNorthingEasting()                    */
12407
/************************************************************************/
12408
12409
/**
12410
 * \brief This method returns TRUE if this projected coordinate
12411
 * system should be treated as having northing/easting coordinate ordering.
12412
 *
12413
 * Currently this returns TRUE for all projected coordinate systems
12414
 * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12415
 * also checked that the CRS had belonged to EPSG authority, but this check
12416
 * has now been removed).
12417
 *
12418
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12419
 * projected CRS with northing, easting axis order imported with
12420
 * importFromEPSG() would cause this method to
12421
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12422
 * is now equivalent to importFromEPSGA().
12423
 *
12424
 * FALSE will be returned for all coordinate systems that are not projected,
12425
 * or whose axes ordering is not northing, easting.
12426
 *
12427
 * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12428
 *
12429
 * @return TRUE or FALSE.
12430
 *
12431
 */
12432
12433
int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12434
12435
0
{
12436
0
    TAKE_OPTIONAL_LOCK();
12437
12438
0
    if (!IsProjected())
12439
0
        return FALSE;
12440
12441
0
    d->demoteFromBoundCRS();
12442
0
    PJ *projCRS;
12443
0
    const auto ctxt = d->getPROJContext();
12444
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12445
0
    {
12446
0
        projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12447
0
        if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12448
0
        {
12449
0
            d->undoDemoteFromBoundCRS();
12450
0
            proj_destroy(projCRS);
12451
0
            return FALSE;
12452
0
        }
12453
0
    }
12454
0
    else
12455
0
    {
12456
0
        projCRS = proj_clone(ctxt, d->m_pj_crs);
12457
0
    }
12458
12459
0
    bool ret = false;
12460
0
    auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12461
0
    proj_destroy(projCRS);
12462
0
    d->undoDemoteFromBoundCRS();
12463
12464
0
    if (cs)
12465
0
    {
12466
0
        ret = isNorthEastAxisOrder(ctxt, cs);
12467
0
        proj_destroy(cs);
12468
0
    }
12469
12470
0
    return ret;
12471
0
}
12472
12473
/************************************************************************/
12474
/*                     OSREPSGTreatsAsNorthingEasting()                 */
12475
/************************************************************************/
12476
12477
/**
12478
 * \brief This function returns TRUE if this projected coordinate
12479
 * system should be treated as having northing/easting coordinate ordering.
12480
 *
12481
 * This function is the same as
12482
 * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12483
 *
12484
 */
12485
12486
int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12487
12488
0
{
12489
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12490
12491
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12492
0
}
12493
12494
/************************************************************************/
12495
/*                     ImportFromESRIWisconsinWKT()                     */
12496
/*                                                                      */
12497
/*      Search a ESRI State Plane WKT and import it.                    */
12498
/************************************************************************/
12499
12500
// This is only used by the HFA driver and somewhat dubious we really need that
12501
// Coming from an old ESRI merge
12502
12503
OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12504
                                                       double centralMeridian,
12505
                                                       double latOfOrigin,
12506
                                                       const char *unitsName,
12507
                                                       const char *crsName)
12508
0
{
12509
0
    TAKE_OPTIONAL_LOCK();
12510
12511
0
    if (centralMeridian < -93 || centralMeridian > -87)
12512
0
        return OGRERR_FAILURE;
12513
0
    if (latOfOrigin < 40 || latOfOrigin > 47)
12514
0
        return OGRERR_FAILURE;
12515
12516
    // If the CS name is known.
12517
0
    if (!prjName && !unitsName && crsName)
12518
0
    {
12519
0
        const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12520
0
        PJ_OBJ_LIST *list = proj_create_from_name(
12521
0
            d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12522
0
        if (list)
12523
0
        {
12524
0
            if (proj_list_get_count(list) == 1)
12525
0
            {
12526
0
                auto crs = proj_list_get(d->getPROJContext(), list, 0);
12527
0
                if (crs)
12528
0
                {
12529
0
                    Clear();
12530
0
                    d->setPjCRS(crs);
12531
0
                    proj_list_destroy(list);
12532
0
                    return OGRERR_NONE;
12533
0
                }
12534
0
            }
12535
0
            proj_list_destroy(list);
12536
0
        }
12537
0
        return OGRERR_FAILURE;
12538
0
    }
12539
12540
0
    if (prjName == nullptr || unitsName == nullptr)
12541
0
    {
12542
0
        return OGRERR_FAILURE;
12543
0
    }
12544
12545
0
    const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12546
0
    PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12547
0
                                              "NAD_1983_HARN_WISCRS_", &type, 1,
12548
0
                                              true, 0, nullptr);
12549
0
    if (list)
12550
0
    {
12551
0
        const auto listSize = proj_list_get_count(list);
12552
0
        for (int i = 0; i < listSize; i++)
12553
0
        {
12554
0
            auto crs = proj_list_get(d->getPROJContext(), list, i);
12555
0
            if (!crs)
12556
0
            {
12557
0
                continue;
12558
0
            }
12559
12560
0
            auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12561
0
            if (!conv)
12562
0
            {
12563
0
                proj_destroy(crs);
12564
0
                continue;
12565
0
            }
12566
0
            const char *pszMethodCode = nullptr;
12567
0
            proj_coordoperation_get_method_info(
12568
0
                d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12569
0
            const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12570
0
            if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12571
0
                   nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12572
0
                  (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12573
0
                   nMethodCode ==
12574
0
                       EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12575
0
            {
12576
0
                proj_destroy(crs);
12577
0
                proj_destroy(conv);
12578
0
                continue;
12579
0
            }
12580
12581
0
            auto coordSys =
12582
0
                proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12583
0
            if (!coordSys)
12584
0
            {
12585
0
                proj_destroy(crs);
12586
0
                proj_destroy(conv);
12587
0
                continue;
12588
0
            }
12589
12590
0
            double dfConvFactor = 0.0;
12591
0
            proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12592
0
                                  nullptr, nullptr, &dfConvFactor, nullptr,
12593
0
                                  nullptr, nullptr);
12594
0
            proj_destroy(coordSys);
12595
12596
0
            if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12597
0
                (!EQUAL(unitsName, "meters") &&
12598
0
                 std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12599
0
                     1e-10))
12600
0
            {
12601
0
                proj_destroy(crs);
12602
0
                proj_destroy(conv);
12603
0
                continue;
12604
0
            }
12605
12606
0
            int idx_lat = proj_coordoperation_get_param_index(
12607
0
                d->getPROJContext(), conv,
12608
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12609
0
            double valueLat = -1000;
12610
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12611
0
                                          nullptr, nullptr, nullptr, &valueLat,
12612
0
                                          nullptr, nullptr, nullptr, nullptr,
12613
0
                                          nullptr, nullptr);
12614
0
            int idx_lon = proj_coordoperation_get_param_index(
12615
0
                d->getPROJContext(), conv,
12616
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12617
0
            double valueLong = -1000;
12618
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12619
0
                                          nullptr, nullptr, nullptr, &valueLong,
12620
0
                                          nullptr, nullptr, nullptr, nullptr,
12621
0
                                          nullptr, nullptr);
12622
0
            if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12623
0
                std::fabs(latOfOrigin - valueLat) <= 1e-10)
12624
0
            {
12625
0
                Clear();
12626
0
                d->setPjCRS(crs);
12627
0
                proj_list_destroy(list);
12628
0
                proj_destroy(conv);
12629
0
                return OGRERR_NONE;
12630
0
            }
12631
12632
0
            proj_destroy(crs);
12633
0
            proj_destroy(conv);
12634
0
        }
12635
0
        proj_list_destroy(list);
12636
0
    }
12637
12638
0
    return OGRERR_FAILURE;
12639
0
}
12640
12641
/************************************************************************/
12642
/*                      GetAxisMappingStrategy()                        */
12643
/************************************************************************/
12644
12645
/** \brief Return the data axis to CRS axis mapping strategy.
12646
 *
12647
 * <ul>
12648
 * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12649
 *     lat/long order, the data will still be long/lat ordered. Similarly for
12650
 *     a projected CRS with northing/easting order, the data will still be
12651
 *     easting/northing ordered.
12652
 * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12653
 *     the CRS axis.
12654
 * <li>OAMS_CUSTOM means that the data axis are customly defined with
12655
 *     SetDataAxisToSRSAxisMapping()
12656
 * </ul>
12657
 * @return the data axis to CRS axis mapping strategy.
12658
 * @since GDAL 3.0
12659
 */
12660
OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12661
0
{
12662
0
    TAKE_OPTIONAL_LOCK();
12663
12664
0
    return d->m_axisMappingStrategy;
12665
0
}
12666
12667
/************************************************************************/
12668
/*                      OSRGetAxisMappingStrategy()                     */
12669
/************************************************************************/
12670
12671
/** \brief Return the data axis to CRS axis mapping strategy.
12672
 *
12673
 * See OGRSpatialReference::GetAxisMappingStrategy()
12674
 * @since GDAL 3.0
12675
 */
12676
OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12677
0
{
12678
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12679
12680
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12681
0
}
12682
12683
/************************************************************************/
12684
/*                      SetAxisMappingStrategy()                        */
12685
/************************************************************************/
12686
12687
/** \brief Set the data axis to CRS axis mapping strategy.
12688
 *
12689
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12690
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12691
 * later being the default value when the option is not set) to control the
12692
 * value of the data axis to CRS axis mapping strategy when a
12693
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12694
 * override this default value.
12695
 *
12696
 * See OGRSpatialReference::GetAxisMappingStrategy()
12697
 * @since GDAL 3.0
12698
 */
12699
void OGRSpatialReference::SetAxisMappingStrategy(
12700
    OSRAxisMappingStrategy strategy)
12701
0
{
12702
0
    TAKE_OPTIONAL_LOCK();
12703
12704
0
    d->m_axisMappingStrategy = strategy;
12705
0
    d->refreshAxisMapping();
12706
0
}
12707
12708
/************************************************************************/
12709
/*                      OSRSetAxisMappingStrategy()                     */
12710
/************************************************************************/
12711
12712
/** \brief Set the data axis to CRS axis mapping strategy.
12713
 *
12714
 * See OGRSpatialReference::SetAxisMappingStrategy()
12715
 * @since GDAL 3.0
12716
 */
12717
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12718
                               OSRAxisMappingStrategy strategy)
12719
0
{
12720
0
    VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12721
12722
0
    OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12723
0
}
12724
12725
/************************************************************************/
12726
/*                      GetDataAxisToSRSAxisMapping()                   */
12727
/************************************************************************/
12728
12729
/** \brief Return the data axis to SRS axis mapping.
12730
 *
12731
 * The number of elements of the vector will be the number of axis of the CRS.
12732
 * Values start at 1.
12733
 *
12734
 * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12735
 * for the first axis of the CRS.
12736
 *
12737
 * @since GDAL 3.0
12738
 */
12739
const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12740
0
{
12741
0
    TAKE_OPTIONAL_LOCK();
12742
12743
0
    return d->m_axisMapping;
12744
0
}
12745
12746
/************************************************************************/
12747
/*                     OSRGetDataAxisToSRSAxisMapping()                 */
12748
/************************************************************************/
12749
12750
/** \brief Return the data axis to SRS axis mapping.
12751
 *
12752
 * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12753
 *
12754
 * @since GDAL 3.0
12755
 */
12756
const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12757
                                          int *pnCount)
12758
0
{
12759
0
    VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12760
0
    VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12761
12762
0
    const auto &v =
12763
0
        OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12764
0
    *pnCount = static_cast<int>(v.size());
12765
0
    return v.data();
12766
0
}
12767
12768
/************************************************************************/
12769
/*                      SetDataAxisToSRSAxisMapping()                   */
12770
/************************************************************************/
12771
12772
/** \brief Set a custom data axis to CRS axis mapping.
12773
 *
12774
 * The number of elements of the mapping vector should be the number of axis
12775
 * of the CRS (as returned by GetAxesCount()) (although this method does not
12776
 * check that, beyond checking there are at least 2 elements, so that this
12777
 * method and setting the CRS can be done in any order).
12778
 * This is taken into account by OGRCoordinateTransformation to transform the
12779
 * order of coordinates to the order expected by the CRS before
12780
 * transformation, and back to the data order after transformation.
12781
 *
12782
 * The mapping[i] value (one based) represents the data axis number for the i(th)
12783
 * axis of the CRS. A negative value can also be used to ask for a sign
12784
 * reversal during coordinate transformation (to deal with northing vs southing,
12785
 * easting vs westing, heights vs depths).
12786
 *
12787
 * When used with OGRCoordinateTransformation,
12788
 * - the only valid values for mapping[0] (data axis number for the first axis
12789
 *   of the CRS) are 1, 2, -1, -2.
12790
 * - the only valid values for mapping[1] (data axis number for the second axis
12791
 *   of the CRS) are 1, 2, -1, -2.
12792
 *  - the only valid values mapping[2] are 3 or -3.
12793
 * Note: this method does not validate the values of mapping[].
12794
 *
12795
 * mapping=[2,1] typically expresses the inversion of axis between the data
12796
 * axis and the CRS axis for a 2D CRS.
12797
 *
12798
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12799
 *
12800
 * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12801
 *
12802
 * @param mapping The new data axis to CRS axis mapping.
12803
 *
12804
 * @since GDAL 3.0
12805
 * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12806
 */
12807
OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12808
    const std::vector<int> &mapping)
12809
0
{
12810
0
    TAKE_OPTIONAL_LOCK();
12811
12812
0
    if (mapping.size() < 2)
12813
0
        return OGRERR_FAILURE;
12814
0
    d->m_axisMappingStrategy = OAMS_CUSTOM;
12815
0
    d->m_axisMapping = mapping;
12816
0
    return OGRERR_NONE;
12817
0
}
12818
12819
/************************************************************************/
12820
/*                     OSRSetDataAxisToSRSAxisMapping()                 */
12821
/************************************************************************/
12822
12823
/** \brief Set a custom data axis to CRS axis mapping.
12824
 *
12825
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12826
 *
12827
 * This is the same as the C++ method
12828
 * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12829
 *
12830
 * @since GDAL 3.1
12831
 */
12832
OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12833
                                      int nMappingSize, const int *panMapping)
12834
0
{
12835
0
    VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12836
0
    VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12837
0
                      OGRERR_FAILURE);
12838
12839
0
    if (nMappingSize < 0)
12840
0
        return OGRERR_FAILURE;
12841
12842
0
    std::vector<int> mapping(nMappingSize);
12843
0
    if (nMappingSize)
12844
0
        memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12845
0
    return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12846
0
        mapping);
12847
0
}
12848
12849
/************************************************************************/
12850
/*                               GetAreaOfUse()                         */
12851
/************************************************************************/
12852
12853
/** \brief Return the area of use of the CRS.
12854
 *
12855
 * This method is the same as the OSRGetAreaOfUse() function.
12856
 *
12857
 * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12858
 * longitude, expressed in degree. Might be NULL. If the returned value is
12859
 * -1000, the bounding box is unknown.
12860
 * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12861
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12862
 * the bounding box is unknown.
12863
 * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12864
 * longitude, expressed in degree. Might be NULL. If the returned value is
12865
 * -1000, the bounding box is unknown.
12866
 * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12867
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12868
 * the bounding box is unknown.
12869
 * @param ppszAreaName Pointer to a string to receive the name of the area of
12870
 * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12871
 * invalidated by further calls.
12872
 * @return true in case of success
12873
 * @since GDAL 3.0
12874
 */
12875
bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12876
                                       double *pdfSouthLatitudeDeg,
12877
                                       double *pdfEastLongitudeDeg,
12878
                                       double *pdfNorthLatitudeDeg,
12879
                                       const char **ppszAreaName) const
12880
0
{
12881
0
    TAKE_OPTIONAL_LOCK();
12882
12883
0
    d->refreshProjObj();
12884
0
    if (!d->m_pj_crs)
12885
0
    {
12886
0
        return false;
12887
0
    }
12888
0
    d->demoteFromBoundCRS();
12889
0
    const char *pszAreaName = nullptr;
12890
0
    int bSuccess = proj_get_area_of_use(
12891
0
        d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12892
0
        pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12893
0
        &pszAreaName);
12894
0
    d->undoDemoteFromBoundCRS();
12895
0
    d->m_osAreaName = pszAreaName ? pszAreaName : "";
12896
0
    if (ppszAreaName)
12897
0
        *ppszAreaName = d->m_osAreaName.c_str();
12898
0
    return CPL_TO_BOOL(bSuccess);
12899
0
}
12900
12901
/************************************************************************/
12902
/*                               GetAreaOfUse()                         */
12903
/************************************************************************/
12904
12905
/** \brief Return the area of use of the CRS.
12906
 *
12907
 * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12908
 *
12909
 * @since GDAL 3.0
12910
 */
12911
int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12912
                    double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12913
                    double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12914
0
{
12915
0
    VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12916
12917
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12918
0
        pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12919
0
        pdfNorthLatitudeDeg, ppszAreaName);
12920
0
}
12921
12922
/************************************************************************/
12923
/*                     OSRGetCRSInfoListFromDatabase()                  */
12924
/************************************************************************/
12925
12926
/** \brief Enumerate CRS objects from the database.
12927
 *
12928
 * The returned object is an array of OSRCRSInfo* pointers, whose last
12929
 * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12930
 *
12931
 * @param pszAuthName Authority name, used to restrict the search.
12932
 * Or NULL for all authorities.
12933
 * @param params Additional criteria. Must be set to NULL for now.
12934
 * @param pnOutResultCount Output parameter pointing to an integer to receive
12935
 * the size of the result list. Might be NULL
12936
 * @return an array of OSRCRSInfo* pointers to be freed with
12937
 * OSRDestroyCRSInfoList(), or NULL in case of error.
12938
 *
12939
 * @since GDAL 3.0
12940
 */
12941
OSRCRSInfo **
12942
OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12943
                              CPL_UNUSED const OSRCRSListParameters *params,
12944
                              int *pnOutResultCount)
12945
0
{
12946
0
    int nResultCount = 0;
12947
0
    auto projList = proj_get_crs_info_list_from_database(
12948
0
        OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12949
0
    if (pnOutResultCount)
12950
0
        *pnOutResultCount = nResultCount;
12951
0
    if (!projList)
12952
0
    {
12953
0
        return nullptr;
12954
0
    }
12955
0
    auto res = new OSRCRSInfo *[nResultCount + 1];
12956
0
    for (int i = 0; i < nResultCount; i++)
12957
0
    {
12958
0
        res[i] = new OSRCRSInfo;
12959
0
        res[i]->pszAuthName = projList[i]->auth_name
12960
0
                                  ? CPLStrdup(projList[i]->auth_name)
12961
0
                                  : nullptr;
12962
0
        res[i]->pszCode =
12963
0
            projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12964
0
        res[i]->pszName =
12965
0
            projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12966
0
        res[i]->eType = OSR_CRS_TYPE_OTHER;
12967
0
        switch (projList[i]->type)
12968
0
        {
12969
0
            case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12970
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12971
0
                break;
12972
0
            case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12973
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12974
0
                break;
12975
0
            case PJ_TYPE_GEOCENTRIC_CRS:
12976
0
                res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12977
0
                break;
12978
0
            case PJ_TYPE_PROJECTED_CRS:
12979
0
                res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12980
0
                break;
12981
0
            case PJ_TYPE_VERTICAL_CRS:
12982
0
                res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12983
0
                break;
12984
0
            case PJ_TYPE_COMPOUND_CRS:
12985
0
                res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12986
0
                break;
12987
0
            default:
12988
0
                break;
12989
0
        }
12990
0
        res[i]->bDeprecated = projList[i]->deprecated;
12991
0
        res[i]->bBboxValid = projList[i]->bbox_valid;
12992
0
        res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12993
0
        res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12994
0
        res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12995
0
        res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12996
0
        res[i]->pszAreaName = projList[i]->area_name
12997
0
                                  ? CPLStrdup(projList[i]->area_name)
12998
0
                                  : nullptr;
12999
0
        res[i]->pszProjectionMethod =
13000
0
            projList[i]->projection_method_name
13001
0
                ? CPLStrdup(projList[i]->projection_method_name)
13002
0
                : nullptr;
13003
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
13004
0
        res[i]->pszCelestialBodyName =
13005
0
            projList[i]->celestial_body_name
13006
0
                ? CPLStrdup(projList[i]->celestial_body_name)
13007
0
                : nullptr;
13008
#else
13009
        res[i]->pszCelestialBodyName =
13010
            res[i]->pszAuthName && EQUAL(res[i]->pszAuthName, "EPSG")
13011
                ? CPLStrdup("Earth")
13012
                : nullptr;
13013
#endif
13014
0
    }
13015
0
    res[nResultCount] = nullptr;
13016
0
    proj_crs_info_list_destroy(projList);
13017
0
    return res;
13018
0
}
13019
13020
/************************************************************************/
13021
/*                        OSRDestroyCRSInfoList()                       */
13022
/************************************************************************/
13023
13024
/** \brief Destroy the result returned by
13025
 * OSRGetCRSInfoListFromDatabase().
13026
 *
13027
 * @since GDAL 3.0
13028
 */
13029
void OSRDestroyCRSInfoList(OSRCRSInfo **list)
13030
0
{
13031
0
    if (list)
13032
0
    {
13033
0
        for (int i = 0; list[i] != nullptr; i++)
13034
0
        {
13035
0
            CPLFree(list[i]->pszAuthName);
13036
0
            CPLFree(list[i]->pszCode);
13037
0
            CPLFree(list[i]->pszName);
13038
0
            CPLFree(list[i]->pszAreaName);
13039
0
            CPLFree(list[i]->pszProjectionMethod);
13040
0
            CPLFree(list[i]->pszCelestialBodyName);
13041
0
            delete list[i];
13042
0
        }
13043
0
        delete[] list;
13044
0
    }
13045
0
}
13046
13047
/************************************************************************/
13048
/*                   OSRGetAuthorityListFromDatabase()                  */
13049
/************************************************************************/
13050
13051
/** \brief Return the list of CRS authorities used in the PROJ database.
13052
 *
13053
 * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
13054
 *
13055
 * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
13056
 *
13057
 * @return nullptr in case of error, or a NULL terminated list of strings to
13058
 * free with CSLDestroy()
13059
 * @since GDAL 3.10
13060
 */
13061
char **OSRGetAuthorityListFromDatabase()
13062
0
{
13063
0
    PROJ_STRING_LIST list =
13064
0
        proj_get_authorities_from_database(OSRGetProjTLSContext());
13065
0
    if (!list)
13066
0
    {
13067
0
        return nullptr;
13068
0
    }
13069
0
    int count = 0;
13070
0
    while (list[count])
13071
0
        ++count;
13072
0
    char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
13073
0
    for (int i = 0; i < count; ++i)
13074
0
        res[i] = CPLStrdup(list[i]);
13075
0
    proj_string_list_destroy(list);
13076
0
    return res;
13077
0
}
13078
13079
/************************************************************************/
13080
/*                    UpdateCoordinateSystemFromGeogCRS()               */
13081
/************************************************************************/
13082
13083
/*! @cond Doxygen_Suppress */
13084
/** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
13085
 *
13086
 * @since GDAL 3.1
13087
 */
13088
void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
13089
0
{
13090
0
    TAKE_OPTIONAL_LOCK();
13091
13092
0
    d->refreshProjObj();
13093
0
    if (!d->m_pj_crs)
13094
0
        return;
13095
0
    if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
13096
0
        return;
13097
0
    if (GetAxesCount() == 3)
13098
0
        return;
13099
0
    auto ctxt = d->getPROJContext();
13100
0
    auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
13101
0
    if (!baseCRS)
13102
0
        return;
13103
0
    auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
13104
0
    if (!baseCRSCS)
13105
0
    {
13106
0
        proj_destroy(baseCRS);
13107
0
        return;
13108
0
    }
13109
0
    if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
13110
0
    {
13111
0
        proj_destroy(baseCRSCS);
13112
0
        proj_destroy(baseCRS);
13113
0
        return;
13114
0
    }
13115
0
    auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
13116
0
    if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
13117
0
    {
13118
0
        proj_destroy(baseCRSCS);
13119
0
        proj_destroy(baseCRS);
13120
0
        proj_destroy(projCS);
13121
0
        return;
13122
0
    }
13123
13124
0
    PJ_AXIS_DESCRIPTION axis[3];
13125
0
    for (int i = 0; i < 3; i++)
13126
0
    {
13127
0
        const char *name = nullptr;
13128
0
        const char *abbreviation = nullptr;
13129
0
        const char *direction = nullptr;
13130
0
        double unit_conv_factor = 0;
13131
0
        const char *unit_name = nullptr;
13132
0
        proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13133
0
                              &abbreviation, &direction, &unit_conv_factor,
13134
0
                              &unit_name, nullptr, nullptr);
13135
0
        axis[i].name = CPLStrdup(name);
13136
0
        axis[i].abbreviation = CPLStrdup(abbreviation);
13137
0
        axis[i].direction = CPLStrdup(direction);
13138
0
        axis[i].unit_name = CPLStrdup(unit_name);
13139
0
        axis[i].unit_conv_factor = unit_conv_factor;
13140
0
        axis[i].unit_type = PJ_UT_LINEAR;
13141
0
    }
13142
0
    proj_destroy(baseCRSCS);
13143
0
    proj_destroy(projCS);
13144
0
    auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13145
0
    for (int i = 0; i < 3; i++)
13146
0
    {
13147
0
        CPLFree(axis[i].name);
13148
0
        CPLFree(axis[i].abbreviation);
13149
0
        CPLFree(axis[i].direction);
13150
0
        CPLFree(axis[i].unit_name);
13151
0
    }
13152
0
    if (!cs)
13153
0
    {
13154
0
        proj_destroy(baseCRS);
13155
0
        return;
13156
0
    }
13157
0
    auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13158
0
    auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13159
0
                                         conversion, cs);
13160
0
    proj_destroy(baseCRS);
13161
0
    proj_destroy(conversion);
13162
0
    proj_destroy(cs);
13163
0
    d->setPjCRS(crs);
13164
0
}
13165
13166
/*! @endcond */
13167
13168
/************************************************************************/
13169
/*                             PromoteTo3D()                            */
13170
/************************************************************************/
13171
13172
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13173
 *
13174
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
13175
 * units.
13176
 *
13177
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13178
 * be used.
13179
 * @return OGRERR_NONE if no error occurred.
13180
 * @since GDAL 3.1 and PROJ 6.3
13181
 */
13182
OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13183
0
{
13184
0
    TAKE_OPTIONAL_LOCK();
13185
13186
0
    d->refreshProjObj();
13187
0
    if (!d->m_pj_crs)
13188
0
        return OGRERR_FAILURE;
13189
0
    auto newPj =
13190
0
        proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13191
0
    if (!newPj)
13192
0
        return OGRERR_FAILURE;
13193
0
    d->setPjCRS(newPj);
13194
0
    return OGRERR_NONE;
13195
0
}
13196
13197
/************************************************************************/
13198
/*                             OSRPromoteTo3D()                         */
13199
/************************************************************************/
13200
13201
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13202
 *
13203
 * See OGRSpatialReference::PromoteTo3D()
13204
 *
13205
 * @since GDAL 3.1 and PROJ 6.3
13206
 */
13207
OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13208
0
{
13209
0
    VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13210
13211
0
    return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13212
0
}
13213
13214
/************************************************************************/
13215
/*                             DemoteTo2D()                             */
13216
/************************************************************************/
13217
13218
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13219
 *
13220
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13221
 * be used.
13222
 * @return OGRERR_NONE if no error occurred.
13223
 * @since GDAL 3.2 and PROJ 6.3
13224
 */
13225
OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13226
0
{
13227
0
    TAKE_OPTIONAL_LOCK();
13228
13229
0
    d->refreshProjObj();
13230
0
    if (!d->m_pj_crs)
13231
0
        return OGRERR_FAILURE;
13232
0
    auto newPj =
13233
0
        proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13234
0
    if (!newPj)
13235
0
        return OGRERR_FAILURE;
13236
0
    d->setPjCRS(newPj);
13237
0
    return OGRERR_NONE;
13238
0
}
13239
13240
/************************************************************************/
13241
/*                             OSRDemoteTo2D()                          */
13242
/************************************************************************/
13243
13244
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13245
 *
13246
 * See OGRSpatialReference::DemoteTo2D()
13247
 *
13248
 * @since GDAL 3.2 and PROJ 6.3
13249
 */
13250
OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13251
0
{
13252
0
    VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13253
13254
0
    return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13255
0
}
13256
13257
/************************************************************************/
13258
/*                           GetEPSGGeogCS()                            */
13259
/************************************************************************/
13260
13261
/** Try to establish what the EPSG code for this coordinate systems
13262
 * GEOGCS might be.  Returns -1 if no reasonable guess can be made.
13263
 *
13264
 * @return EPSG code
13265
 */
13266
13267
int OGRSpatialReference::GetEPSGGeogCS() const
13268
13269
0
{
13270
0
    TAKE_OPTIONAL_LOCK();
13271
13272
    /* -------------------------------------------------------------------- */
13273
    /*      Check axis order.                                               */
13274
    /* -------------------------------------------------------------------- */
13275
0
    auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13276
0
    if (!poGeogCRS)
13277
0
        return -1;
13278
13279
0
    bool ret = false;
13280
0
    poGeogCRS->d->demoteFromBoundCRS();
13281
0
    auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13282
0
                                             poGeogCRS->d->m_pj_crs);
13283
0
    poGeogCRS->d->undoDemoteFromBoundCRS();
13284
0
    if (cs)
13285
0
    {
13286
0
        const char *pszDirection = nullptr;
13287
0
        if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13288
0
                                  &pszDirection, nullptr, nullptr, nullptr,
13289
0
                                  nullptr))
13290
0
        {
13291
0
            if (EQUAL(pszDirection, "north"))
13292
0
            {
13293
0
                ret = true;
13294
0
            }
13295
0
        }
13296
13297
0
        proj_destroy(cs);
13298
0
    }
13299
0
    if (!ret)
13300
0
        return -1;
13301
13302
    /* -------------------------------------------------------------------- */
13303
    /*      Do we already have it?                                          */
13304
    /* -------------------------------------------------------------------- */
13305
0
    const char *pszAuthName = GetAuthorityName("GEOGCS");
13306
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13307
0
        return atoi(GetAuthorityCode("GEOGCS"));
13308
13309
    /* -------------------------------------------------------------------- */
13310
    /*      Get the datum and geogcs names.                                 */
13311
    /* -------------------------------------------------------------------- */
13312
13313
0
    const char *pszGEOGCS = GetAttrValue("GEOGCS");
13314
0
    const char *pszDatum = GetAttrValue("DATUM");
13315
13316
    // We can only operate on coordinate systems with a geogcs.
13317
0
    OGRSpatialReference oSRSTmp;
13318
0
    if (pszGEOGCS == nullptr || pszDatum == nullptr)
13319
0
    {
13320
        // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13321
        // export to WKT1, so try to extract the geographic CRS through PROJ
13322
        // API with CopyGeogCSFrom() and get the nodes' values from it.
13323
0
        oSRSTmp.CopyGeogCSFrom(this);
13324
0
        pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13325
0
        pszDatum = oSRSTmp.GetAttrValue("DATUM");
13326
0
        if (pszGEOGCS == nullptr || pszDatum == nullptr)
13327
0
        {
13328
0
            return -1;
13329
0
        }
13330
0
    }
13331
13332
    // Lookup geographic CRS name
13333
0
    const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13334
0
    PJ_OBJ_LIST *list = proj_create_from_name(
13335
0
        d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13336
0
    if (list)
13337
0
    {
13338
0
        const auto listSize = proj_list_get_count(list);
13339
0
        if (listSize == 1)
13340
0
        {
13341
0
            auto crs = proj_list_get(d->getPROJContext(), list, 0);
13342
0
            if (crs)
13343
0
            {
13344
0
                pszAuthName = proj_get_id_auth_name(crs, 0);
13345
0
                const char *pszCode = proj_get_id_code(crs, 0);
13346
0
                if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13347
0
                {
13348
0
                    const int nCode = atoi(pszCode);
13349
0
                    proj_destroy(crs);
13350
0
                    proj_list_destroy(list);
13351
0
                    return nCode;
13352
0
                }
13353
0
                proj_destroy(crs);
13354
0
            }
13355
0
        }
13356
0
        proj_list_destroy(list);
13357
0
    }
13358
13359
    /* -------------------------------------------------------------------- */
13360
    /*      Is this a "well known" geographic coordinate system?            */
13361
    /* -------------------------------------------------------------------- */
13362
0
    const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13363
0
                      strstr(pszDatum, "WGS") ||
13364
0
                      strstr(pszGEOGCS, "World Geodetic System") ||
13365
0
                      strstr(pszGEOGCS, "World_Geodetic_System") ||
13366
0
                      strstr(pszDatum, "World Geodetic System") ||
13367
0
                      strstr(pszDatum, "World_Geodetic_System");
13368
13369
0
    const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13370
0
                      strstr(pszDatum, "NAD") ||
13371
0
                      strstr(pszGEOGCS, "North American") ||
13372
0
                      strstr(pszGEOGCS, "North_American") ||
13373
0
                      strstr(pszDatum, "North American") ||
13374
0
                      strstr(pszDatum, "North_American");
13375
13376
0
    if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13377
0
        return 4326;
13378
13379
0
    if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13380
0
        return 4322;
13381
13382
    // This is questionable as there are several 'flavors' of NAD83 that
13383
    // are not the same as 4269
13384
0
    if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13385
0
        return 4269;
13386
13387
0
    if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13388
0
        return 4267;
13389
13390
    /* -------------------------------------------------------------------- */
13391
    /*      If we know the datum, associate the most likely GCS with        */
13392
    /*      it.                                                             */
13393
    /* -------------------------------------------------------------------- */
13394
0
    const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13395
0
    pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13396
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13397
0
        GetPrimeMeridian() == 0.0)
13398
0
    {
13399
0
        const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13400
13401
0
        if (nDatum >= 6000 && nDatum <= 6999)
13402
0
            return nDatum - 2000;
13403
0
    }
13404
13405
0
    return -1;
13406
0
}
13407
13408
/************************************************************************/
13409
/*                          SetCoordinateEpoch()                        */
13410
/************************************************************************/
13411
13412
/** Set the coordinate epoch, as decimal year.
13413
 *
13414
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13415
 * change with time. To be unambiguous the coordinates must always be qualified
13416
 * with the epoch at which they are valid. The coordinate epoch is not
13417
 * necessarily the epoch at which the observation was collected.
13418
 *
13419
 * Pedantically the coordinate epoch of an observation belongs to the
13420
 * observation, and not to the CRS, however it is often more practical to
13421
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13422
 * CRS (see IsDynamic())
13423
 *
13424
 * This method is the same as the OSRSetCoordinateEpoch() function.
13425
 *
13426
 * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13427
 * @since OGR 3.4
13428
 */
13429
13430
void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13431
0
{
13432
0
    d->m_coordinateEpoch = dfCoordinateEpoch;
13433
0
}
13434
13435
/************************************************************************/
13436
/*                      OSRSetCoordinateEpoch()                         */
13437
/************************************************************************/
13438
13439
/** \brief Set the coordinate epoch, as decimal year.
13440
 *
13441
 * See OGRSpatialReference::SetCoordinateEpoch()
13442
 *
13443
 * @since OGR 3.4
13444
 */
13445
void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13446
0
{
13447
0
    VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13448
13449
0
    return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13450
0
        dfCoordinateEpoch);
13451
0
}
13452
13453
/************************************************************************/
13454
/*                          GetCoordinateEpoch()                        */
13455
/************************************************************************/
13456
13457
/** Return the coordinate epoch, as decimal year.
13458
 *
13459
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13460
 * change with time. To be unambiguous the coordinates must always be qualified
13461
 * with the epoch at which they are valid. The coordinate epoch is not
13462
 * necessarily the epoch at which the observation was collected.
13463
 *
13464
 * Pedantically the coordinate epoch of an observation belongs to the
13465
 * observation, and not to the CRS, however it is often more practical to
13466
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13467
 * CRS (see IsDynamic())
13468
 *
13469
 * This method is the same as the OSRGetCoordinateEpoch() function.
13470
 *
13471
 * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13472
 *                         if not set, or relevant.
13473
 * @since OGR 3.4
13474
 */
13475
13476
double OGRSpatialReference::GetCoordinateEpoch() const
13477
0
{
13478
0
    return d->m_coordinateEpoch;
13479
0
}
13480
13481
/************************************************************************/
13482
/*                      OSRGetCoordinateEpoch()                        */
13483
/************************************************************************/
13484
13485
/** \brief Get the coordinate epoch, as decimal year.
13486
 *
13487
 * See OGRSpatialReference::GetCoordinateEpoch()
13488
 *
13489
 * @since OGR 3.4
13490
 */
13491
double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13492
0
{
13493
0
    VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13494
13495
0
    return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13496
0
}