Coverage Report

Created: 2025-06-13 06:29

/src/gdal/ogr/ogrspatialreference.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  OpenGIS Simple Features Reference Implementation
4
 * Purpose:  The OGRSpatialReference class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 1999,  Les Technologies SoftMap Inc.
9
 * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "ogr_spatialref.h"
16
17
#include <cmath>
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <limits>
23
#include <string>
24
#include <mutex>
25
#include <set>
26
#include <vector>
27
28
#include "cpl_atomic_ops.h"
29
#include "cpl_conv.h"
30
#include "cpl_csv.h"
31
#include "cpl_error.h"
32
#include "cpl_error_internal.h"
33
#include "cpl_http.h"
34
#include "cpl_json.h"
35
#include "cpl_multiproc.h"
36
#include "cpl_string.h"
37
#include "cpl_vsi.h"
38
#include "ogr_core.h"
39
#include "ogr_p.h"
40
#include "ogr_proj_p.h"
41
#include "ogr_srs_api.h"
42
#include "ogrmitabspatialref.h"
43
44
#include "proj.h"
45
#include "proj_experimental.h"
46
#include "proj_constants.h"
47
48
bool GDALThreadLocalDatasetCacheIsInDestruction();
49
50
// Exists since 8.0.1
51
#ifndef PROJ_AT_LEAST_VERSION
52
#define PROJ_COMPUTE_VERSION(maj, min, patch)                                  \
53
    ((maj)*10000 + (min)*100 + (patch))
54
#define PROJ_VERSION_NUMBER                                                    \
55
    PROJ_COMPUTE_VERSION(PROJ_VERSION_MAJOR, PROJ_VERSION_MINOR,               \
56
                         PROJ_VERSION_PATCH)
57
#define PROJ_AT_LEAST_VERSION(maj, min, patch)                                 \
58
    (PROJ_VERSION_NUMBER >= PROJ_COMPUTE_VERSION(maj, min, patch))
59
#endif
60
61
0
#define STRINGIFY(s) #s
62
0
#define XSTRINGIFY(s) STRINGIFY(s)
63
64
struct OGRSpatialReference::Private
65
{
66
    struct Listener : public OGR_SRSNode::Listener
67
    {
68
        OGRSpatialReference::Private *m_poObj = nullptr;
69
70
0
        explicit Listener(OGRSpatialReference::Private *poObj) : m_poObj(poObj)
71
0
        {
72
0
        }
73
74
        Listener(const Listener &) = delete;
75
        Listener &operator=(const Listener &) = delete;
76
77
        void notifyChange(OGR_SRSNode *) override;
78
    };
79
80
    OGRSpatialReference *m_poSelf = nullptr;
81
    PJ *m_pj_crs = nullptr;
82
83
    // Temporary state used for object construction
84
    PJ_TYPE m_pjType = PJ_TYPE_UNKNOWN;
85
    CPLString m_osPrimeMeridianName{};
86
    CPLString m_osAngularUnits{};
87
    CPLString m_osLinearUnits{};
88
    CPLString m_osAxisName[3]{};
89
90
    std::vector<std::string> m_wktImportWarnings{};
91
    std::vector<std::string> m_wktImportErrors{};
92
    CPLString m_osAreaName{};
93
94
    bool m_bIsThreadSafe = false;
95
    bool m_bNodesChanged = false;
96
    bool m_bNodesWKT2 = false;
97
    OGR_SRSNode *m_poRoot = nullptr;
98
99
    double dfFromGreenwich = 0.0;
100
    double dfToMeter = 0.0;
101
    double dfToDegrees = 0.0;
102
    double m_dfAngularUnitToRadian = 0.0;
103
104
    int nRefCount = 1;
105
    int bNormInfoSet = FALSE;
106
107
    PJ *m_pj_geod_base_crs_temp = nullptr;
108
    PJ *m_pj_proj_crs_cs_temp = nullptr;
109
110
    bool m_pj_crs_modified_during_demote = false;
111
    PJ *m_pj_bound_crs_target = nullptr;
112
    PJ *m_pj_bound_crs_co = nullptr;
113
    PJ *m_pj_crs_backup = nullptr;
114
    OGR_SRSNode *m_poRootBackup = nullptr;
115
116
    bool m_bMorphToESRI = false;
117
    bool m_bHasCenterLong = false;
118
119
    std::shared_ptr<Listener> m_poListener{};
120
121
    std::recursive_mutex m_mutex{};
122
123
    OSRAxisMappingStrategy m_axisMappingStrategy = OAMS_AUTHORITY_COMPLIANT;
124
    std::vector<int> m_axisMapping{1, 2, 3};
125
126
    double m_coordinateEpoch = 0;  // as decimal year
127
128
    explicit Private(OGRSpatialReference *poSelf);
129
    ~Private();
130
    Private(const Private &) = delete;
131
    Private &operator=(const Private &) = delete;
132
133
    void SetThreadSafe()
134
0
    {
135
0
        m_bIsThreadSafe = true;
136
0
    }
137
138
    void clear();
139
    void setPjCRS(PJ *pj_crsIn, bool doRefreshAxisMapping = true);
140
    void setRoot(OGR_SRSNode *poRoot);
141
    void refreshProjObj();
142
    void nodesChanged();
143
    void refreshRootFromProjObj();
144
    void invalidateNodes();
145
146
    void setMorphToESRI(bool b);
147
148
    PJ *getGeodBaseCRS();
149
    PJ *getProjCRSCoordSys();
150
151
    const char *getProjCRSName();
152
    OGRErr replaceConversionAndUnref(PJ *conv);
153
154
    void demoteFromBoundCRS();
155
    void undoDemoteFromBoundCRS();
156
157
    PJ_CONTEXT *getPROJContext()
158
0
    {
159
0
        return OSRGetProjTLSContext();
160
0
    }
161
162
    const char *nullifyTargetKeyIfPossible(const char *pszTargetKey);
163
164
    void refreshAxisMapping();
165
166
    // This structures enables locking during calls to OGRSpatialReference
167
    // public methods. Locking is only needed for instances of
168
    // OGRSpatialReference that have been asked to be thread-safe at
169
    // construction.
170
    // The lock is not just for a single call to OGRSpatialReference::Private,
171
    // but for the series of calls done by a OGRSpatialReference method.
172
    // We need a recursive mutex, because some OGRSpatialReference methods
173
    // may call other ones.
174
    struct OptionalLockGuard
175
    {
176
        Private &m_private;
177
178
0
        explicit OptionalLockGuard(Private *p) : m_private(*p)
179
0
        {
180
0
            if (m_private.m_bIsThreadSafe)
181
0
                m_private.m_mutex.lock();
182
0
        }
183
184
        ~OptionalLockGuard()
185
0
        {
186
0
            if (m_private.m_bIsThreadSafe)
187
0
                m_private.m_mutex.unlock();
188
0
        }
189
    };
190
191
    inline OptionalLockGuard GetOptionalLockGuard()
192
0
    {
193
0
        return OptionalLockGuard(this);
194
0
    }
195
};
196
197
void OGRSpatialReference::Private::Listener::notifyChange(OGR_SRSNode *)
198
0
{
199
0
    m_poObj->nodesChanged();
200
0
}
201
202
#define TAKE_OPTIONAL_LOCK()                                                   \
203
0
    auto lock = d->GetOptionalLockGuard();                                     \
204
0
    CPL_IGNORE_RET_VAL(lock)
205
206
static OSRAxisMappingStrategy GetDefaultAxisMappingStrategy()
207
0
{
208
0
    const char *pszDefaultAMS =
209
0
        CPLGetConfigOption("OSR_DEFAULT_AXIS_MAPPING_STRATEGY", nullptr);
210
0
    if (pszDefaultAMS)
211
0
    {
212
0
        if (EQUAL(pszDefaultAMS, "AUTHORITY_COMPLIANT"))
213
0
            return OAMS_AUTHORITY_COMPLIANT;
214
0
        else if (EQUAL(pszDefaultAMS, "TRADITIONAL_GIS_ORDER"))
215
0
            return OAMS_TRADITIONAL_GIS_ORDER;
216
0
        else
217
0
        {
218
0
            CPLError(CE_Failure, CPLE_AppDefined,
219
0
                     "Illegal value for OSR_DEFAULT_AXIS_MAPPING_STRATEGY = %s",
220
0
                     pszDefaultAMS);
221
0
        }
222
0
    }
223
0
    return OAMS_AUTHORITY_COMPLIANT;
224
0
}
225
226
OGRSpatialReference::Private::Private(OGRSpatialReference *poSelf)
227
0
    : m_poSelf(poSelf),
228
0
      m_poListener(std::shared_ptr<Listener>(new Listener(this)))
229
0
{
230
    // Get the default value for m_axisMappingStrategy from the
231
    // OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration option, if set.
232
0
    m_axisMappingStrategy = GetDefaultAxisMappingStrategy();
233
0
}
234
235
OGRSpatialReference::Private::~Private()
236
0
{
237
    // In case we destroy the object not in the thread that created it,
238
    // we need to reassign the PROJ context. Having the context bundled inside
239
    // PJ* deeply sucks...
240
0
    PJ_CONTEXT *pj_context_to_destroy = nullptr;
241
0
    PJ_CONTEXT *ctxt;
242
0
    if (GDALThreadLocalDatasetCacheIsInDestruction())
243
0
    {
244
0
        pj_context_to_destroy = proj_context_create();
245
0
        ctxt = pj_context_to_destroy;
246
0
    }
247
0
    else
248
0
    {
249
0
        ctxt = getPROJContext();
250
0
    }
251
252
0
    proj_assign_context(m_pj_crs, ctxt);
253
0
    proj_destroy(m_pj_crs);
254
255
0
    proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
256
0
    proj_destroy(m_pj_geod_base_crs_temp);
257
258
0
    proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
259
0
    proj_destroy(m_pj_proj_crs_cs_temp);
260
261
0
    proj_assign_context(m_pj_bound_crs_target, ctxt);
262
0
    proj_destroy(m_pj_bound_crs_target);
263
264
0
    proj_assign_context(m_pj_bound_crs_co, ctxt);
265
0
    proj_destroy(m_pj_bound_crs_co);
266
267
0
    proj_assign_context(m_pj_crs_backup, ctxt);
268
0
    proj_destroy(m_pj_crs_backup);
269
270
0
    delete m_poRootBackup;
271
0
    delete m_poRoot;
272
0
    proj_context_destroy(pj_context_to_destroy);
273
0
}
274
275
void OGRSpatialReference::Private::clear()
276
0
{
277
0
    proj_assign_context(m_pj_crs, getPROJContext());
278
0
    proj_destroy(m_pj_crs);
279
0
    m_pj_crs = nullptr;
280
281
0
    delete m_poRoot;
282
0
    m_poRoot = nullptr;
283
0
    m_bNodesChanged = false;
284
285
0
    m_wktImportWarnings.clear();
286
0
    m_wktImportErrors.clear();
287
288
0
    m_pj_crs_modified_during_demote = false;
289
0
    m_pjType = PJ_TYPE_UNKNOWN;
290
0
    m_osPrimeMeridianName.clear();
291
0
    m_osAngularUnits.clear();
292
0
    m_osLinearUnits.clear();
293
294
0
    bNormInfoSet = FALSE;
295
0
    dfFromGreenwich = 1.0;
296
0
    dfToMeter = 1.0;
297
0
    dfToDegrees = 1.0;
298
0
    m_dfAngularUnitToRadian = 0.0;
299
300
0
    m_bMorphToESRI = false;
301
0
    m_bHasCenterLong = false;
302
303
0
    m_coordinateEpoch = 0.0;
304
0
}
305
306
void OGRSpatialReference::Private::setRoot(OGR_SRSNode *poRoot)
307
0
{
308
0
    m_poRoot = poRoot;
309
0
    if (m_poRoot)
310
0
    {
311
0
        m_poRoot->RegisterListener(m_poListener);
312
0
    }
313
0
    nodesChanged();
314
0
}
315
316
void OGRSpatialReference::Private::setPjCRS(PJ *pj_crsIn,
317
                                            bool doRefreshAxisMapping)
318
0
{
319
0
    auto ctxt = getPROJContext();
320
321
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
322
0
    if (proj_get_type(pj_crsIn) == PJ_TYPE_COORDINATE_METADATA)
323
0
    {
324
0
        const double dfEpoch =
325
0
            proj_coordinate_metadata_get_epoch(ctxt, pj_crsIn);
326
0
        if (!std::isnan(dfEpoch))
327
0
        {
328
0
            m_poSelf->SetCoordinateEpoch(dfEpoch);
329
0
        }
330
0
        auto crs = proj_get_source_crs(ctxt, pj_crsIn);
331
0
        proj_destroy(pj_crsIn);
332
0
        pj_crsIn = crs;
333
0
    }
334
0
#endif
335
336
0
    proj_assign_context(m_pj_crs, ctxt);
337
0
    proj_destroy(m_pj_crs);
338
0
    m_pj_crs = pj_crsIn;
339
0
    if (m_pj_crs)
340
0
    {
341
0
        m_pjType = proj_get_type(m_pj_crs);
342
0
    }
343
0
    if (m_pj_crs_backup)
344
0
    {
345
0
        m_pj_crs_modified_during_demote = true;
346
0
    }
347
0
    invalidateNodes();
348
0
    if (doRefreshAxisMapping)
349
0
    {
350
0
        refreshAxisMapping();
351
0
    }
352
0
}
353
354
void OGRSpatialReference::Private::refreshProjObj()
355
0
{
356
0
    if (m_bNodesChanged && m_poRoot)
357
0
    {
358
0
        char *pszWKT = nullptr;
359
0
        m_poRoot->exportToWkt(&pszWKT);
360
0
        auto poRootBackup = m_poRoot;
361
0
        m_poRoot = nullptr;
362
0
        const double dfCoordinateEpochBackup = m_coordinateEpoch;
363
0
        clear();
364
0
        m_coordinateEpoch = dfCoordinateEpochBackup;
365
0
        m_bHasCenterLong = strstr(pszWKT, "CENTER_LONG") != nullptr;
366
367
0
        const char *const options[] = {
368
0
            "STRICT=NO",
369
0
#if PROJ_AT_LEAST_VERSION(9, 1, 0)
370
0
            "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=NO",
371
0
#endif
372
0
            nullptr
373
0
        };
374
0
        PROJ_STRING_LIST warnings = nullptr;
375
0
        PROJ_STRING_LIST errors = nullptr;
376
0
        setPjCRS(proj_create_from_wkt(getPROJContext(), pszWKT, options,
377
0
                                      &warnings, &errors));
378
0
        for (auto iter = warnings; iter && *iter; ++iter)
379
0
        {
380
0
            m_wktImportWarnings.push_back(*iter);
381
0
        }
382
0
        for (auto iter = errors; iter && *iter; ++iter)
383
0
        {
384
0
            m_wktImportErrors.push_back(*iter);
385
0
        }
386
0
        proj_string_list_destroy(warnings);
387
0
        proj_string_list_destroy(errors);
388
389
0
        CPLFree(pszWKT);
390
391
0
        m_poRoot = poRootBackup;
392
0
        m_bNodesChanged = false;
393
0
    }
394
0
}
395
396
void OGRSpatialReference::Private::refreshRootFromProjObj()
397
0
{
398
0
    CPLAssert(m_poRoot == nullptr);
399
400
0
    if (m_pj_crs)
401
0
    {
402
0
        CPLStringList aosOptions;
403
0
        if (!m_bMorphToESRI)
404
0
        {
405
0
            aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
406
0
            aosOptions.SetNameValue("MULTILINE", "NO");
407
0
        }
408
0
        aosOptions.SetNameValue("STRICT", "NO");
409
410
0
        const char *pszWKT;
411
0
        {
412
0
            CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
413
0
            pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs,
414
0
                                 m_bMorphToESRI ? PJ_WKT1_ESRI : PJ_WKT1_GDAL,
415
0
                                 aosOptions.List());
416
0
            m_bNodesWKT2 = false;
417
0
        }
418
0
        if (!m_bMorphToESRI && pszWKT == nullptr)
419
0
        {
420
0
            pszWKT = proj_as_wkt(getPROJContext(), m_pj_crs, PJ_WKT2_2018,
421
0
                                 aosOptions.List());
422
0
            m_bNodesWKT2 = true;
423
0
        }
424
0
        if (pszWKT)
425
0
        {
426
0
            auto root = new OGR_SRSNode();
427
0
            setRoot(root);
428
0
            root->importFromWkt(&pszWKT);
429
0
            m_bNodesChanged = false;
430
0
        }
431
0
    }
432
0
}
433
434
static bool isNorthEastAxisOrder(PJ_CONTEXT *ctx, PJ *cs)
435
0
{
436
0
    const char *pszName1 = nullptr;
437
0
    const char *pszDirection1 = nullptr;
438
0
    proj_cs_get_axis_info(ctx, cs, 0, &pszName1, nullptr, &pszDirection1,
439
0
                          nullptr, nullptr, nullptr, nullptr);
440
0
    const char *pszName2 = nullptr;
441
0
    const char *pszDirection2 = nullptr;
442
0
    proj_cs_get_axis_info(ctx, cs, 1, &pszName2, nullptr, &pszDirection2,
443
0
                          nullptr, nullptr, nullptr, nullptr);
444
0
    if (pszDirection1 && EQUAL(pszDirection1, "north") && pszDirection2 &&
445
0
        EQUAL(pszDirection2, "east"))
446
0
    {
447
0
        return true;
448
0
    }
449
0
    if (pszDirection1 && pszDirection2 &&
450
0
        ((EQUAL(pszDirection1, "north") && EQUAL(pszDirection2, "north")) ||
451
0
         (EQUAL(pszDirection1, "south") && EQUAL(pszDirection2, "south"))) &&
452
0
        pszName1 && STARTS_WITH_CI(pszName1, "northing") && pszName2 &&
453
0
        STARTS_WITH_CI(pszName2, "easting"))
454
0
    {
455
0
        return true;
456
0
    }
457
0
    return false;
458
0
}
459
460
void OGRSpatialReference::Private::refreshAxisMapping()
461
0
{
462
0
    if (!m_pj_crs || m_axisMappingStrategy == OAMS_CUSTOM)
463
0
        return;
464
465
0
    bool doUndoDemote = false;
466
0
    if (m_pj_crs_backup == nullptr)
467
0
    {
468
0
        doUndoDemote = true;
469
0
        demoteFromBoundCRS();
470
0
    }
471
0
    const auto ctxt = getPROJContext();
472
0
    PJ *horizCRS = nullptr;
473
0
    int axisCount = 0;
474
0
    if (m_pjType == PJ_TYPE_VERTICAL_CRS)
475
0
    {
476
0
        axisCount = 1;
477
0
    }
478
0
    else if (m_pjType == PJ_TYPE_COMPOUND_CRS)
479
0
    {
480
0
        horizCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 0);
481
0
        if (horizCRS && proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
482
0
        {
483
0
            auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
484
0
            if (baseCRS)
485
0
            {
486
0
                proj_destroy(horizCRS);
487
0
                horizCRS = baseCRS;
488
0
            }
489
0
        }
490
491
0
        auto vertCRS = proj_crs_get_sub_crs(ctxt, m_pj_crs, 1);
492
0
        if (vertCRS)
493
0
        {
494
0
            if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
495
0
            {
496
0
                auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
497
0
                if (baseCRS)
498
0
                {
499
0
                    proj_destroy(vertCRS);
500
0
                    vertCRS = baseCRS;
501
0
                }
502
0
            }
503
504
0
            auto cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
505
0
            if (cs)
506
0
            {
507
0
                axisCount += proj_cs_get_axis_count(ctxt, cs);
508
0
                proj_destroy(cs);
509
0
            }
510
0
            proj_destroy(vertCRS);
511
0
        }
512
0
    }
513
0
    else
514
0
    {
515
0
        horizCRS = m_pj_crs;
516
0
    }
517
518
0
    bool bSwitchForGisFriendlyOrder = false;
519
0
    if (horizCRS)
520
0
    {
521
0
        auto cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
522
0
        if (cs)
523
0
        {
524
0
            int nHorizCSAxisCount = proj_cs_get_axis_count(ctxt, cs);
525
0
            axisCount += nHorizCSAxisCount;
526
0
            if (nHorizCSAxisCount >= 2)
527
0
            {
528
0
                bSwitchForGisFriendlyOrder = isNorthEastAxisOrder(ctxt, cs);
529
0
            }
530
0
            proj_destroy(cs);
531
0
        }
532
0
    }
533
0
    if (horizCRS != m_pj_crs)
534
0
    {
535
0
        proj_destroy(horizCRS);
536
0
    }
537
0
    if (doUndoDemote)
538
0
    {
539
0
        undoDemoteFromBoundCRS();
540
0
    }
541
542
0
    m_axisMapping.resize(axisCount);
543
0
    if (m_axisMappingStrategy == OAMS_AUTHORITY_COMPLIANT ||
544
0
        !bSwitchForGisFriendlyOrder)
545
0
    {
546
0
        for (int i = 0; i < axisCount; i++)
547
0
        {
548
0
            m_axisMapping[i] = i + 1;
549
0
        }
550
0
    }
551
0
    else
552
0
    {
553
0
        m_axisMapping[0] = 2;
554
0
        m_axisMapping[1] = 1;
555
0
        if (axisCount == 3)
556
0
        {
557
0
            m_axisMapping[2] = 3;
558
0
        }
559
0
    }
560
0
}
561
562
void OGRSpatialReference::Private::nodesChanged()
563
0
{
564
0
    m_bNodesChanged = true;
565
0
}
566
567
void OGRSpatialReference::Private::invalidateNodes()
568
0
{
569
0
    delete m_poRoot;
570
0
    m_poRoot = nullptr;
571
0
    m_bNodesChanged = false;
572
0
}
573
574
void OGRSpatialReference::Private::setMorphToESRI(bool b)
575
0
{
576
0
    invalidateNodes();
577
0
    m_bMorphToESRI = b;
578
0
}
579
580
void OGRSpatialReference::Private::demoteFromBoundCRS()
581
0
{
582
0
    CPLAssert(m_pj_bound_crs_target == nullptr);
583
0
    CPLAssert(m_pj_bound_crs_co == nullptr);
584
0
    CPLAssert(m_poRootBackup == nullptr);
585
0
    CPLAssert(m_pj_crs_backup == nullptr);
586
587
0
    m_pj_crs_modified_during_demote = false;
588
589
0
    if (m_pjType == PJ_TYPE_BOUND_CRS)
590
0
    {
591
0
        auto baseCRS = proj_get_source_crs(getPROJContext(), m_pj_crs);
592
0
        m_pj_bound_crs_target = proj_get_target_crs(getPROJContext(), m_pj_crs);
593
0
        m_pj_bound_crs_co =
594
0
            proj_crs_get_coordoperation(getPROJContext(), m_pj_crs);
595
596
0
        m_poRootBackup = m_poRoot;
597
0
        m_poRoot = nullptr;
598
0
        m_pj_crs_backup = m_pj_crs;
599
0
        m_pj_crs = baseCRS;
600
0
        m_pjType = proj_get_type(m_pj_crs);
601
0
    }
602
0
}
603
604
void OGRSpatialReference::Private::undoDemoteFromBoundCRS()
605
0
{
606
0
    if (m_pj_bound_crs_target)
607
0
    {
608
0
        CPLAssert(m_poRoot == nullptr);
609
0
        CPLAssert(m_pj_crs);
610
0
        if (!m_pj_crs_modified_during_demote)
611
0
        {
612
0
            proj_destroy(m_pj_crs);
613
0
            m_pj_crs = m_pj_crs_backup;
614
0
            m_pjType = proj_get_type(m_pj_crs);
615
0
            m_poRoot = m_poRootBackup;
616
0
        }
617
0
        else
618
0
        {
619
0
            delete m_poRootBackup;
620
0
            m_poRootBackup = nullptr;
621
0
            proj_destroy(m_pj_crs_backup);
622
0
            m_pj_crs_backup = nullptr;
623
0
            setPjCRS(proj_crs_create_bound_crs(getPROJContext(), m_pj_crs,
624
0
                                               m_pj_bound_crs_target,
625
0
                                               m_pj_bound_crs_co),
626
0
                     false);
627
0
        }
628
0
    }
629
630
0
    m_poRootBackup = nullptr;
631
0
    m_pj_crs_backup = nullptr;
632
0
    proj_destroy(m_pj_bound_crs_target);
633
0
    m_pj_bound_crs_target = nullptr;
634
0
    proj_destroy(m_pj_bound_crs_co);
635
0
    m_pj_bound_crs_co = nullptr;
636
0
    m_pj_crs_modified_during_demote = false;
637
0
}
638
639
const char *OGRSpatialReference::Private::nullifyTargetKeyIfPossible(
640
    const char *pszTargetKey)
641
0
{
642
0
    if (pszTargetKey)
643
0
    {
644
0
        demoteFromBoundCRS();
645
0
        if ((m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
646
0
             m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS) &&
647
0
            EQUAL(pszTargetKey, "GEOGCS"))
648
0
        {
649
0
            pszTargetKey = nullptr;
650
0
        }
651
0
        else if (m_pjType == PJ_TYPE_GEOCENTRIC_CRS &&
652
0
                 EQUAL(pszTargetKey, "GEOCCS"))
653
0
        {
654
0
            pszTargetKey = nullptr;
655
0
        }
656
0
        else if (m_pjType == PJ_TYPE_PROJECTED_CRS &&
657
0
                 EQUAL(pszTargetKey, "PROJCS"))
658
0
        {
659
0
            pszTargetKey = nullptr;
660
0
        }
661
0
        else if (m_pjType == PJ_TYPE_VERTICAL_CRS &&
662
0
                 EQUAL(pszTargetKey, "VERT_CS"))
663
0
        {
664
0
            pszTargetKey = nullptr;
665
0
        }
666
0
        undoDemoteFromBoundCRS();
667
0
    }
668
0
    return pszTargetKey;
669
0
}
670
671
PJ *OGRSpatialReference::Private::getGeodBaseCRS()
672
0
{
673
0
    if (m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
674
0
        m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
675
0
    {
676
0
        return m_pj_crs;
677
0
    }
678
679
0
    auto ctxt = getPROJContext();
680
0
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
681
0
    {
682
0
        proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
683
0
        proj_destroy(m_pj_geod_base_crs_temp);
684
0
        m_pj_geod_base_crs_temp = proj_crs_get_geodetic_crs(ctxt, m_pj_crs);
685
0
        return m_pj_geod_base_crs_temp;
686
0
    }
687
688
0
    proj_assign_context(m_pj_geod_base_crs_temp, ctxt);
689
0
    proj_destroy(m_pj_geod_base_crs_temp);
690
0
    auto cs = proj_create_ellipsoidal_2D_cs(ctxt, PJ_ELLPS2D_LATITUDE_LONGITUDE,
691
0
                                            nullptr, 0);
692
0
    m_pj_geod_base_crs_temp = proj_create_geographic_crs(
693
0
        ctxt, "WGS 84", "World Geodetic System 1984", "WGS 84",
694
0
        SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING, SRS_PM_GREENWICH, 0.0,
695
0
        SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV), cs);
696
0
    proj_destroy(cs);
697
698
0
    return m_pj_geod_base_crs_temp;
699
0
}
700
701
PJ *OGRSpatialReference::Private::getProjCRSCoordSys()
702
0
{
703
0
    auto ctxt = getPROJContext();
704
0
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
705
0
    {
706
0
        proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
707
0
        proj_destroy(m_pj_proj_crs_cs_temp);
708
0
        m_pj_proj_crs_cs_temp =
709
0
            proj_crs_get_coordinate_system(getPROJContext(), m_pj_crs);
710
0
        return m_pj_proj_crs_cs_temp;
711
0
    }
712
713
0
    proj_assign_context(m_pj_proj_crs_cs_temp, ctxt);
714
0
    proj_destroy(m_pj_proj_crs_cs_temp);
715
0
    m_pj_proj_crs_cs_temp = proj_create_cartesian_2D_cs(
716
0
        ctxt, PJ_CART2D_EASTING_NORTHING, nullptr, 0);
717
0
    return m_pj_proj_crs_cs_temp;
718
0
}
719
720
const char *OGRSpatialReference::Private::getProjCRSName()
721
0
{
722
0
    if (m_pjType == PJ_TYPE_PROJECTED_CRS)
723
0
    {
724
0
        return proj_get_name(m_pj_crs);
725
0
    }
726
727
0
    return "unnamed";
728
0
}
729
730
OGRErr OGRSpatialReference::Private::replaceConversionAndUnref(PJ *conv)
731
0
{
732
0
    refreshProjObj();
733
734
0
    demoteFromBoundCRS();
735
736
0
    auto projCRS =
737
0
        proj_create_projected_crs(getPROJContext(), getProjCRSName(),
738
0
                                  getGeodBaseCRS(), conv, getProjCRSCoordSys());
739
0
    proj_destroy(conv);
740
741
0
    setPjCRS(projCRS);
742
743
0
    undoDemoteFromBoundCRS();
744
0
    return OGRERR_NONE;
745
0
}
746
747
/************************************************************************/
748
/*                           ToPointer()                                */
749
/************************************************************************/
750
751
static inline OGRSpatialReference *ToPointer(OGRSpatialReferenceH hSRS)
752
0
{
753
0
    return OGRSpatialReference::FromHandle(hSRS);
754
0
}
755
756
/************************************************************************/
757
/*                           ToHandle()                                 */
758
/************************************************************************/
759
760
static inline OGRSpatialReferenceH ToHandle(OGRSpatialReference *poSRS)
761
0
{
762
0
    return OGRSpatialReference::ToHandle(poSRS);
763
0
}
764
765
/************************************************************************/
766
/*                           OGRsnPrintDouble()                         */
767
/************************************************************************/
768
769
void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue);
770
771
void OGRsnPrintDouble(char *pszStrBuf, size_t size, double dfValue)
772
773
0
{
774
0
    CPLsnprintf(pszStrBuf, size, "%.16g", dfValue);
775
776
0
    const size_t nLen = strlen(pszStrBuf);
777
778
    // The following hack is intended to truncate some "precision" in cases
779
    // that appear to be roundoff error.
780
0
    if (nLen > 15 && (strcmp(pszStrBuf + nLen - 6, "999999") == 0 ||
781
0
                      strcmp(pszStrBuf + nLen - 6, "000001") == 0))
782
0
    {
783
0
        CPLsnprintf(pszStrBuf, size, "%.15g", dfValue);
784
0
    }
785
786
    // Force to user periods regardless of locale.
787
0
    if (strchr(pszStrBuf, ',') != nullptr)
788
0
    {
789
0
        char *const pszDelim = strchr(pszStrBuf, ',');
790
0
        *pszDelim = '.';
791
0
    }
792
0
}
793
794
/************************************************************************/
795
/*                        OGRSpatialReference()                         */
796
/************************************************************************/
797
798
/**
799
 * \brief Constructor.
800
 *
801
 * This constructor takes an optional string argument which if passed
802
 * should be a WKT representation of an SRS.  Passing this is equivalent
803
 * to not passing it, and then calling importFromWkt() with the WKT string.
804
 *
805
 * Note that newly created objects are given a reference count of one.
806
 *
807
 * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
808
 * object are assumed to be in the order of the axis of the CRS definition
809
 (which
810
 * for example means latitude first, longitude second for geographic CRS
811
 belonging
812
 * to the EPSG authority). It is possible to define a data axis to CRS axis
813
 * mapping strategy with the SetAxisMappingStrategy() method.
814
 *
815
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
816
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
817
 later
818
 * being the default value when the option is not set) to control the value of
819
 the
820
 * data axis to CRS axis mapping strategy when a OSRSpatialReference object is
821
 * created. Calling SetAxisMappingStrategy() will override this default value.
822
823
 * The C function OSRNewSpatialReference() does the same thing as this
824
 * constructor.
825
 *
826
 * @param pszWKT well known text definition to which the object should
827
 * be initialized, or NULL (the default).
828
 */
829
830
OGRSpatialReference::OGRSpatialReference(const char *pszWKT)
831
0
    : d(new Private(this))
832
0
{
833
0
    if (pszWKT != nullptr)
834
0
        importFromWkt(pszWKT);
835
0
}
836
837
/************************************************************************/
838
/*                       OSRNewSpatialReference()                       */
839
/************************************************************************/
840
841
/**
842
 * \brief Constructor.
843
 *
844
 * Starting with GDAL 3.0, coordinates associated with a OGRSpatialReference
845
 * object are assumed to be in the order of the axis of the CRS definition
846
 * (which for example means latitude first, longitude second for geographic CRS
847
 * belonging to the EPSG authority). It is possible to define a data axis to CRS
848
 * axis mapping strategy with the SetAxisMappingStrategy() method.
849
 *
850
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
851
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
852
 * later being the default value when the option is not set) to control the
853
 * value of the data axis to CRS axis mapping strategy when a
854
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
855
 * override this default value.
856
 *
857
 * This function is the same as OGRSpatialReference::OGRSpatialReference()
858
 */
859
OGRSpatialReferenceH CPL_STDCALL OSRNewSpatialReference(const char *pszWKT)
860
861
0
{
862
0
    OGRSpatialReference *poSRS = new OGRSpatialReference();
863
864
0
    if (pszWKT != nullptr && strlen(pszWKT) > 0)
865
0
    {
866
0
        if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
867
0
        {
868
0
            delete poSRS;
869
0
            poSRS = nullptr;
870
0
        }
871
0
    }
872
873
0
    return ToHandle(poSRS);
874
0
}
875
876
/************************************************************************/
877
/*                        OGRSpatialReference()                         */
878
/************************************************************************/
879
880
/** Copy constructor. See also Clone().
881
 * @param oOther other spatial reference
882
 */
883
OGRSpatialReference::OGRSpatialReference(const OGRSpatialReference &oOther)
884
0
    : d(new Private(this))
885
0
{
886
0
    *this = oOther;
887
0
}
888
889
/************************************************************************/
890
/*                        OGRSpatialReference()                         */
891
/************************************************************************/
892
893
/** Move constructor.
894
 * @param oOther other spatial reference
895
 */
896
OGRSpatialReference::OGRSpatialReference(OGRSpatialReference &&oOther)
897
0
    : d(std::move(oOther.d))
898
0
{
899
0
}
900
901
/************************************************************************/
902
/*                        ~OGRSpatialReference()                        */
903
/************************************************************************/
904
905
/**
906
 * \brief OGRSpatialReference destructor.
907
 *
908
 * The C function OSRDestroySpatialReference() does the same thing as this
909
 * method. Preferred C++ method : OGRSpatialReference::DestroySpatialReference()
910
 *
911
 * @deprecated
912
 */
913
914
OGRSpatialReference::~OGRSpatialReference()
915
916
0
{
917
0
}
918
919
/************************************************************************/
920
/*                      DestroySpatialReference()                       */
921
/************************************************************************/
922
923
/**
924
 * \brief OGRSpatialReference destructor.
925
 *
926
 * This static method will destroy a OGRSpatialReference.  It is
927
 * equivalent to calling delete on the object, but it ensures that the
928
 * deallocation is properly executed within the OGR libraries heap on
929
 * platforms where this can matter (win32).
930
 *
931
 * This function is the same as OSRDestroySpatialReference()
932
 *
933
 * @param poSRS the object to delete
934
 *
935
 * @since GDAL 1.7.0
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
/*                               Clone()                                */
1434
/************************************************************************/
1435
1436
/**
1437
 * \brief Make a duplicate of this OGRSpatialReference.
1438
 *
1439
 * This method is the same as the C function OSRClone().
1440
 *
1441
 * @return a new SRS, which becomes the responsibility of the caller.
1442
 */
1443
1444
OGRSpatialReference *OGRSpatialReference::Clone() const
1445
1446
0
{
1447
0
    OGRSpatialReference *poNewRef = new OGRSpatialReference();
1448
1449
0
    TAKE_OPTIONAL_LOCK();
1450
1451
0
    d->refreshProjObj();
1452
0
    if (d->m_pj_crs != nullptr)
1453
0
        poNewRef->d->setPjCRS(proj_clone(d->getPROJContext(), d->m_pj_crs));
1454
0
    if (d->m_bHasCenterLong && d->m_poRoot)
1455
0
    {
1456
0
        poNewRef->d->setRoot(d->m_poRoot->Clone());
1457
0
    }
1458
0
    poNewRef->d->m_axisMapping = d->m_axisMapping;
1459
0
    poNewRef->d->m_axisMappingStrategy = d->m_axisMappingStrategy;
1460
0
    poNewRef->d->m_coordinateEpoch = d->m_coordinateEpoch;
1461
0
    return poNewRef;
1462
0
}
1463
1464
/************************************************************************/
1465
/*                              OSRClone()                              */
1466
/************************************************************************/
1467
1468
/**
1469
 * \brief Make a duplicate of this OGRSpatialReference.
1470
 *
1471
 * This function is the same as OGRSpatialReference::Clone()
1472
 */
1473
OGRSpatialReferenceH CPL_STDCALL OSRClone(OGRSpatialReferenceH hSRS)
1474
1475
0
{
1476
0
    VALIDATE_POINTER1(hSRS, "OSRClone", nullptr);
1477
1478
0
    return ToHandle(ToPointer(hSRS)->Clone());
1479
0
}
1480
1481
/************************************************************************/
1482
/*                            dumpReadable()                            */
1483
/************************************************************************/
1484
1485
/** Dump pretty wkt to stdout, mostly for debugging.
1486
 */
1487
void OGRSpatialReference::dumpReadable()
1488
1489
0
{
1490
0
    char *pszPrettyWkt = nullptr;
1491
1492
0
    const char *const apszOptions[] = {"FORMAT=WKT2", "MULTILINE=YES", nullptr};
1493
0
    exportToWkt(&pszPrettyWkt, apszOptions);
1494
0
    printf("%s\n", pszPrettyWkt); /*ok*/
1495
0
    CPLFree(pszPrettyWkt);
1496
0
}
1497
1498
/************************************************************************/
1499
/*                         exportToPrettyWkt()                          */
1500
/************************************************************************/
1501
1502
/**
1503
 * Convert this SRS into a nicely formatted WKT 1 string for display to a
1504
 * person.
1505
 *
1506
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1507
 * Issues</a> page for implementation details of WKT 1 in OGR.
1508
 *
1509
 * Note that the returned WKT string should be freed with
1510
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1511
 *
1512
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1513
 * option. Valid values are the one of the FORMAT option of
1514
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1515
 *
1516
 * This method is the same as the C function OSRExportToPrettyWkt().
1517
 *
1518
 * @param ppszResult the resulting string is returned in this pointer.
1519
 * @param bSimplify TRUE if the AXIS, AUTHORITY and EXTENSION nodes should be
1520
 *   stripped off.
1521
 *
1522
 * @return OGRERR_NONE if successful.
1523
 */
1524
1525
OGRErr OGRSpatialReference::exportToPrettyWkt(char **ppszResult,
1526
                                              int bSimplify) const
1527
1528
0
{
1529
0
    CPLStringList aosOptions;
1530
0
    aosOptions.SetNameValue("MULTILINE", "YES");
1531
0
    if (bSimplify)
1532
0
    {
1533
0
        aosOptions.SetNameValue("FORMAT", "WKT1_SIMPLE");
1534
0
    }
1535
0
    return exportToWkt(ppszResult, aosOptions.List());
1536
0
}
1537
1538
/************************************************************************/
1539
/*                        OSRExportToPrettyWkt()                        */
1540
/************************************************************************/
1541
1542
/**
1543
 * \brief Convert this SRS into a nicely formatted WKT 1 string for display to a
1544
 * person.
1545
 *
1546
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1547
 * option. Valid values are the one of the FORMAT option of
1548
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1549
 *
1550
 * This function is the same as OGRSpatialReference::exportToPrettyWkt().
1551
 */
1552
1553
OGRErr CPL_STDCALL OSRExportToPrettyWkt(OGRSpatialReferenceH hSRS,
1554
                                        char **ppszReturn, int bSimplify)
1555
1556
0
{
1557
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPrettyWkt", OGRERR_FAILURE);
1558
1559
0
    *ppszReturn = nullptr;
1560
1561
0
    return ToPointer(hSRS)->exportToPrettyWkt(ppszReturn, bSimplify);
1562
0
}
1563
1564
/************************************************************************/
1565
/*                            exportToWkt()                             */
1566
/************************************************************************/
1567
1568
/**
1569
 * \brief Convert this SRS into WKT 1 format.
1570
 *
1571
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1572
 * Issues</a> page for implementation details of WKT 1 in OGR.
1573
 *
1574
 * Note that the returned WKT string should be freed with
1575
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1576
 *
1577
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1578
 * option. Valid values are the one of the FORMAT option of
1579
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1580
 *
1581
 * This method is the same as the C function OSRExportToWkt().
1582
 *
1583
 * @param ppszResult the resulting string is returned in this pointer.
1584
 *
1585
 * @return OGRERR_NONE if successful.
1586
 */
1587
1588
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult) const
1589
1590
0
{
1591
0
    return exportToWkt(ppszResult, nullptr);
1592
0
}
1593
1594
/************************************************************************/
1595
/*                GDAL_proj_crs_create_bound_crs_to_WGS84()             */
1596
/************************************************************************/
1597
1598
static PJ *GDAL_proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, PJ *pj,
1599
                                                   bool onlyIfEPSGCode,
1600
                                                   bool canModifyHorizPart)
1601
0
{
1602
0
    PJ *ret = nullptr;
1603
0
    if (proj_get_type(pj) == PJ_TYPE_COMPOUND_CRS)
1604
0
    {
1605
0
        auto horizCRS = proj_crs_get_sub_crs(ctx, pj, 0);
1606
0
        auto vertCRS = proj_crs_get_sub_crs(ctx, pj, 1);
1607
0
        if (horizCRS && proj_get_type(horizCRS) != PJ_TYPE_BOUND_CRS &&
1608
0
            vertCRS &&
1609
0
            (!onlyIfEPSGCode || proj_get_id_auth_name(horizCRS, 0) != nullptr))
1610
0
        {
1611
0
            auto boundHoriz =
1612
0
                canModifyHorizPart
1613
0
                    ? proj_crs_create_bound_crs_to_WGS84(ctx, horizCRS, nullptr)
1614
0
                    : proj_clone(ctx, horizCRS);
1615
0
            auto boundVert =
1616
0
                proj_crs_create_bound_crs_to_WGS84(ctx, vertCRS, nullptr);
1617
0
            if (boundHoriz && boundVert)
1618
0
            {
1619
0
                ret = proj_create_compound_crs(ctx, proj_get_name(pj),
1620
0
                                               boundHoriz, boundVert);
1621
0
            }
1622
0
            proj_destroy(boundHoriz);
1623
0
            proj_destroy(boundVert);
1624
0
        }
1625
0
        proj_destroy(horizCRS);
1626
0
        proj_destroy(vertCRS);
1627
0
    }
1628
0
    else if (proj_get_type(pj) != PJ_TYPE_BOUND_CRS &&
1629
0
             (!onlyIfEPSGCode || proj_get_id_auth_name(pj, 0) != nullptr))
1630
0
    {
1631
0
        ret = proj_crs_create_bound_crs_to_WGS84(ctx, pj, nullptr);
1632
0
    }
1633
0
    return ret;
1634
0
}
1635
1636
/************************************************************************/
1637
/*                            exportToWkt()                             */
1638
/************************************************************************/
1639
1640
/**
1641
 * Convert this SRS into a WKT string.
1642
 *
1643
 * Note that the returned WKT string should be freed with
1644
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
1645
 *
1646
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1647
 * Issues</a> page for implementation details of WKT 1 in OGR.
1648
 *
1649
 * @param ppszResult the resulting string is returned in this pointer.
1650
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1651
 * supported options are
1652
 * <ul>
1653
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1654
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1655
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1656
 *     node is returned.
1657
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1658
 *     node is returned.
1659
 *     WKT1 is an alias of WKT1_GDAL.
1660
 *     WKT2 will default to the latest revision implemented (currently
1661
 *     WKT2_2018) WKT2_2019 can be used as an alias of WKT2_2018 since GDAL 3.2
1662
 * </li>
1663
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1664
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1665
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1666
 * height (for example for use with LAS 1.4 WKT1).
1667
 * Requires PROJ 7.2.1 and GDAL 3.2.1.</li>
1668
 * </ul>
1669
 *
1670
 * Starting with GDAL 3.0.3, if the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1671
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1672
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1673
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1674
 * TOWGS84[] node may be added.
1675
 *
1676
 * @return OGRERR_NONE if successful.
1677
 * @since GDAL 3.0
1678
 */
1679
1680
OGRErr OGRSpatialReference::exportToWkt(char **ppszResult,
1681
                                        const char *const *papszOptions) const
1682
0
{
1683
    // In the past calling this method was thread-safe, even if we never
1684
    // guaranteed it. Now proj_as_wkt() will cache the result internally,
1685
    // so this is no longer thread-safe.
1686
0
    std::lock_guard oLock(d->m_mutex);
1687
1688
0
    d->refreshProjObj();
1689
0
    if (!d->m_pj_crs)
1690
0
    {
1691
0
        *ppszResult = CPLStrdup("");
1692
0
        return OGRERR_FAILURE;
1693
0
    }
1694
1695
0
    if (d->m_bHasCenterLong && d->m_poRoot && !d->m_bMorphToESRI)
1696
0
    {
1697
0
        return d->m_poRoot->exportToWkt(ppszResult);
1698
0
    }
1699
1700
0
    auto ctxt = d->getPROJContext();
1701
0
    auto wktFormat = PJ_WKT1_GDAL;
1702
0
    const char *pszFormat =
1703
0
        CSLFetchNameValueDef(papszOptions, "FORMAT",
1704
0
                             CPLGetConfigOption("OSR_WKT_FORMAT", "DEFAULT"));
1705
0
    if (EQUAL(pszFormat, "DEFAULT"))
1706
0
        pszFormat = "";
1707
1708
0
    if (EQUAL(pszFormat, "WKT1_ESRI") || d->m_bMorphToESRI)
1709
0
    {
1710
0
        wktFormat = PJ_WKT1_ESRI;
1711
0
    }
1712
0
    else if (EQUAL(pszFormat, "WKT1") || EQUAL(pszFormat, "WKT1_GDAL") ||
1713
0
             EQUAL(pszFormat, "WKT1_SIMPLE") || EQUAL(pszFormat, "SFSQL"))
1714
0
    {
1715
0
        wktFormat = PJ_WKT1_GDAL;
1716
0
    }
1717
0
    else if (EQUAL(pszFormat, "WKT2_2015"))
1718
0
    {
1719
0
        wktFormat = PJ_WKT2_2015;
1720
0
    }
1721
0
    else if (EQUAL(pszFormat, "WKT2") || EQUAL(pszFormat, "WKT2_2018") ||
1722
0
             EQUAL(pszFormat, "WKT2_2019"))
1723
0
    {
1724
0
        wktFormat = PJ_WKT2_2018;
1725
0
    }
1726
0
    else if (pszFormat[0] == '\0')
1727
0
    {
1728
        // cppcheck-suppress knownConditionTrueFalse
1729
0
        if (IsDerivedGeographic())
1730
0
        {
1731
0
            wktFormat = PJ_WKT2_2018;
1732
0
        }
1733
0
        else if ((IsGeographic() || IsProjected()) && !IsCompound() &&
1734
0
                 GetAxesCount() == 3)
1735
0
        {
1736
0
            wktFormat = PJ_WKT2_2018;
1737
0
        }
1738
0
    }
1739
0
    else
1740
0
    {
1741
0
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported value for FORMAT");
1742
0
        *ppszResult = CPLStrdup("");
1743
0
        return OGRERR_FAILURE;
1744
0
    }
1745
1746
0
    CPLStringList aosOptions;
1747
0
    if (wktFormat != PJ_WKT1_ESRI)
1748
0
    {
1749
0
        aosOptions.SetNameValue("OUTPUT_AXIS", "YES");
1750
0
    }
1751
0
    aosOptions.SetNameValue(
1752
0
        "MULTILINE", CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO"));
1753
1754
0
    const char *pszAllowEllpsHeightAsVertCS = CSLFetchNameValue(
1755
0
        papszOptions, "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS");
1756
0
    if (pszAllowEllpsHeightAsVertCS)
1757
0
    {
1758
0
        aosOptions.SetNameValue("ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS",
1759
0
                                pszAllowEllpsHeightAsVertCS);
1760
0
    }
1761
1762
0
    PJ *boundCRS = nullptr;
1763
0
    if (wktFormat == PJ_WKT1_GDAL &&
1764
0
        CPLTestBool(CSLFetchNameValueDef(
1765
0
            papszOptions, "ADD_TOWGS84_ON_EXPORT_TO_WKT1",
1766
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1", "NO"))))
1767
0
    {
1768
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
1769
0
            d->getPROJContext(), d->m_pj_crs, true, true);
1770
0
    }
1771
1772
0
    CPLErrorAccumulator oErrorAccumulator;
1773
0
    const char *pszWKT;
1774
0
    {
1775
0
        auto oAccumulator = oErrorAccumulator.InstallForCurrentScope();
1776
0
        CPL_IGNORE_RET_VAL(oAccumulator);
1777
0
        pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs, wktFormat,
1778
0
                             aosOptions.List());
1779
0
    }
1780
0
    for (const auto &oError : oErrorAccumulator.GetErrors())
1781
0
    {
1782
0
        if (pszFormat[0] == '\0' &&
1783
0
            (oError.msg.find("Unsupported conversion method") !=
1784
0
                 std::string::npos ||
1785
0
             oError.msg.find("can only be exported to WKT2") !=
1786
0
                 std::string::npos ||
1787
0
             oError.msg.find("can only be exported since WKT2:2019") !=
1788
0
                 std::string::npos))
1789
0
        {
1790
0
            CPLErrorReset();
1791
            // If we cannot export in the default mode (WKT1), retry with WKT2
1792
0
            pszWKT = proj_as_wkt(ctxt, boundCRS ? boundCRS : d->m_pj_crs,
1793
0
                                 PJ_WKT2_2018, aosOptions.List());
1794
0
            break;
1795
0
        }
1796
0
        CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
1797
0
    }
1798
1799
0
    if (!pszWKT)
1800
0
    {
1801
0
        *ppszResult = CPLStrdup("");
1802
0
        proj_destroy(boundCRS);
1803
0
        return OGRERR_FAILURE;
1804
0
    }
1805
1806
0
    if (EQUAL(pszFormat, "SFSQL") || EQUAL(pszFormat, "WKT1_SIMPLE"))
1807
0
    {
1808
0
        OGR_SRSNode oRoot;
1809
0
        oRoot.importFromWkt(&pszWKT);
1810
0
        oRoot.StripNodes("AXIS");
1811
0
        if (EQUAL(pszFormat, "SFSQL"))
1812
0
        {
1813
0
            oRoot.StripNodes("TOWGS84");
1814
0
        }
1815
0
        oRoot.StripNodes("AUTHORITY");
1816
0
        oRoot.StripNodes("EXTENSION");
1817
0
        OGRErr eErr;
1818
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "MULTILINE", "NO")))
1819
0
            eErr = oRoot.exportToPrettyWkt(ppszResult, 1);
1820
0
        else
1821
0
            eErr = oRoot.exportToWkt(ppszResult);
1822
0
        proj_destroy(boundCRS);
1823
0
        return eErr;
1824
0
    }
1825
1826
0
    *ppszResult = CPLStrdup(pszWKT);
1827
1828
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
1829
    if (wktFormat == PJ_WKT2_2018)
1830
    {
1831
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
1832
        // related to a wrong EPSG code assigned to UTM South conversions
1833
        char *pszPtr = strstr(*ppszResult, "CONVERSION[\"UTM zone ");
1834
        if (pszPtr)
1835
        {
1836
            pszPtr += strlen("CONVERSION[\"UTM zone ");
1837
            const int nZone = atoi(pszPtr);
1838
            while (*pszPtr >= '0' && *pszPtr <= '9')
1839
                ++pszPtr;
1840
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' &&
1841
                pszPtr[1] == '"' && pszPtr[2] == ',')
1842
            {
1843
                pszPtr += 3;
1844
                int nLevel = 0;
1845
                bool bInString = false;
1846
                // Find the ID node corresponding to this CONVERSION node
1847
                while (*pszPtr)
1848
                {
1849
                    if (bInString)
1850
                    {
1851
                        if (*pszPtr == '"' && pszPtr[1] == '"')
1852
                        {
1853
                            ++pszPtr;
1854
                        }
1855
                        else if (*pszPtr == '"')
1856
                        {
1857
                            bInString = false;
1858
                        }
1859
                    }
1860
                    else if (nLevel == 0 && STARTS_WITH_CI(pszPtr, "ID["))
1861
                    {
1862
                        if (STARTS_WITH_CI(pszPtr, CPLSPrintf("ID[\"EPSG\",%d]",
1863
                                                              17000 + nZone)))
1864
                        {
1865
                            CPLAssert(pszPtr[11] == '7');
1866
                            CPLAssert(pszPtr[12] == '0');
1867
                            pszPtr[11] = '6';
1868
                            pszPtr[12] = '1';
1869
                        }
1870
                        break;
1871
                    }
1872
                    else if (*pszPtr == '"')
1873
                    {
1874
                        bInString = true;
1875
                    }
1876
                    else if (*pszPtr == '[')
1877
                    {
1878
                        ++nLevel;
1879
                    }
1880
                    else if (*pszPtr == ']')
1881
                    {
1882
                        --nLevel;
1883
                    }
1884
1885
                    ++pszPtr;
1886
                }
1887
            }
1888
        }
1889
    }
1890
#endif
1891
1892
0
    proj_destroy(boundCRS);
1893
0
    return OGRERR_NONE;
1894
0
}
1895
1896
/************************************************************************/
1897
/*                            exportToWkt()                             */
1898
/************************************************************************/
1899
1900
/**
1901
 * Convert this SRS into a WKT string.
1902
 *
1903
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1904
 * Issues</a> page for implementation details of WKT 1 in OGR.
1905
 *
1906
 * @param papszOptions NULL terminated list of options, or NULL. Currently
1907
 * supported options are
1908
 * <ul>
1909
 * <li>MULTILINE=YES/NO. Defaults to NO.</li>
1910
 * <li>FORMAT=SFSQL/WKT1_SIMPLE/WKT1/WKT1_GDAL/WKT1_ESRI/WKT2_2015/WKT2_2018/WKT2/DEFAULT.
1911
 *     If SFSQL, a WKT1 string without AXIS, TOWGS84, AUTHORITY or EXTENSION
1912
 *     node is returned.
1913
 *     If WKT1_SIMPLE, a WKT1 string without AXIS, AUTHORITY or EXTENSION
1914
 *     node is returned.
1915
 *     WKT1 is an alias of WKT1_GDAL.
1916
 *     WKT2 will default to the latest revision implemented (currently
1917
 *     WKT2_2019)
1918
 * </li>
1919
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1920
 * to YES and FORMAT=WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS will
1921
 * be exported as a compound CRS whose vertical part represents an ellipsoidal
1922
 * height (for example for use with LAS 1.4 WKT1).
1923
 * Requires PROJ 7.2.1.</li>
1924
 * </ul>
1925
 *
1926
 * If the OSR_ADD_TOWGS84_ON_EXPORT_TO_WKT1
1927
 * configuration option is set to YES, when exporting to WKT1_GDAL, this method
1928
 * will try to add a TOWGS84[] node, if there's none attached yet to the SRS and
1929
 * if the SRS has a EPSG code. See the AddGuessedTOWGS84() method for how this
1930
 * TOWGS84[] node may be added.
1931
 *
1932
 * @return a non-empty string if successful.
1933
 * @since GDAL 3.9
1934
 */
1935
1936
std::string
1937
OGRSpatialReference::exportToWkt(const char *const *papszOptions) const
1938
0
{
1939
0
    std::string osWKT;
1940
0
    char *pszWKT = nullptr;
1941
0
    if (exportToWkt(&pszWKT, papszOptions) == OGRERR_NONE)
1942
0
        osWKT = pszWKT;
1943
0
    CPLFree(pszWKT);
1944
0
    return osWKT;
1945
0
}
1946
1947
/************************************************************************/
1948
/*                           OSRExportToWkt()                           */
1949
/************************************************************************/
1950
1951
/**
1952
 * \brief Convert this SRS into WKT 1 format.
1953
 *
1954
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
1955
 * Issues</a> page for implementation details of WKT in OGR.
1956
 *
1957
 * The WKT version can be overridden by using the OSR_WKT_FORMAT configuration
1958
 * option. Valid values are the one of the FORMAT option of
1959
 * exportToWkt( char ** ppszResult, const char* const* papszOptions ) const
1960
 *
1961
 * This function is the same as OGRSpatialReference::exportToWkt().
1962
 */
1963
1964
OGRErr CPL_STDCALL OSRExportToWkt(OGRSpatialReferenceH hSRS, char **ppszReturn)
1965
1966
0
{
1967
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWkt", OGRERR_FAILURE);
1968
1969
0
    *ppszReturn = nullptr;
1970
1971
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn);
1972
0
}
1973
1974
/************************************************************************/
1975
/*                          OSRExportToWktEx()                          */
1976
/************************************************************************/
1977
1978
/**
1979
 * \brief Convert this SRS into WKT format.
1980
 *
1981
 * This function is the same as OGRSpatialReference::exportToWkt(char **
1982
 * ppszResult,const char* const* papszOptions ) const
1983
 *
1984
 * @since GDAL 3.0
1985
 */
1986
1987
OGRErr OSRExportToWktEx(OGRSpatialReferenceH hSRS, char **ppszReturn,
1988
                        const char *const *papszOptions)
1989
0
{
1990
0
    VALIDATE_POINTER1(hSRS, "OSRExportToWktEx", OGRERR_FAILURE);
1991
1992
0
    *ppszReturn = nullptr;
1993
1994
0
    return ToPointer(hSRS)->exportToWkt(ppszReturn, papszOptions);
1995
0
}
1996
1997
/************************************************************************/
1998
/*                       exportToPROJJSON()                             */
1999
/************************************************************************/
2000
2001
/**
2002
 * Convert this SRS into a PROJJSON string.
2003
 *
2004
 * Note that the returned JSON string should be freed with
2005
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
2006
 *
2007
 * @param ppszResult the resulting string is returned in this pointer.
2008
 * @param papszOptions NULL terminated list of options, or NULL. Currently
2009
 * supported options are
2010
 * <ul>
2011
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
2012
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
2013
 * on).</li>
2014
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
2015
 * disable it.</li>
2016
 * </ul>
2017
 *
2018
 * @return OGRERR_NONE if successful.
2019
 * @since GDAL 3.1 and PROJ 6.2
2020
 */
2021
2022
OGRErr OGRSpatialReference::exportToPROJJSON(
2023
    char **ppszResult, CPL_UNUSED const char *const *papszOptions) const
2024
0
{
2025
0
    TAKE_OPTIONAL_LOCK();
2026
2027
0
    d->refreshProjObj();
2028
0
    if (!d->m_pj_crs)
2029
0
    {
2030
0
        *ppszResult = nullptr;
2031
0
        return OGRERR_FAILURE;
2032
0
    }
2033
2034
0
    const char *pszPROJJSON =
2035
0
        proj_as_projjson(d->getPROJContext(), d->m_pj_crs, papszOptions);
2036
2037
0
    if (!pszPROJJSON)
2038
0
    {
2039
0
        *ppszResult = CPLStrdup("");
2040
0
        return OGRERR_FAILURE;
2041
0
    }
2042
2043
0
    *ppszResult = CPLStrdup(pszPROJJSON);
2044
2045
#if !(PROJ_AT_LEAST_VERSION(9, 5, 0))
2046
    {
2047
        // Works around bug fixed per https://github.com/OSGeo/PROJ/pull/4166
2048
        // related to a wrong EPSG code assigned to UTM South conversions
2049
        char *pszPtr = strstr(*ppszResult, "\"name\": \"UTM zone ");
2050
        if (pszPtr)
2051
        {
2052
            pszPtr += strlen("\"name\": \"UTM zone ");
2053
            const int nZone = atoi(pszPtr);
2054
            while (*pszPtr >= '0' && *pszPtr <= '9')
2055
                ++pszPtr;
2056
            if (nZone >= 1 && nZone <= 60 && *pszPtr == 'S' && pszPtr[1] == '"')
2057
            {
2058
                pszPtr += 2;
2059
                int nLevel = 0;
2060
                bool bInString = false;
2061
                // Find the id node corresponding to this conversion node
2062
                while (*pszPtr)
2063
                {
2064
                    if (bInString)
2065
                    {
2066
                        if (*pszPtr == '\\')
2067
                        {
2068
                            ++pszPtr;
2069
                        }
2070
                        else if (*pszPtr == '"')
2071
                        {
2072
                            bInString = false;
2073
                        }
2074
                    }
2075
                    else if (nLevel == 0 && STARTS_WITH(pszPtr, "\"id\": {"))
2076
                    {
2077
                        const char *pszNextEndCurl = strchr(pszPtr, '}');
2078
                        const char *pszAuthEPSG =
2079
                            strstr(pszPtr, "\"authority\": \"EPSG\"");
2080
                        char *pszCode = strstr(
2081
                            pszPtr, CPLSPrintf("\"code\": %d", 17000 + nZone));
2082
                        if (pszAuthEPSG && pszCode && pszNextEndCurl &&
2083
                            pszNextEndCurl - pszAuthEPSG > 0 &&
2084
                            pszNextEndCurl - pszCode > 0)
2085
                        {
2086
                            CPLAssert(pszCode[9] == '7');
2087
                            CPLAssert(pszCode[10] == '0');
2088
                            pszCode[9] = '6';
2089
                            pszCode[10] = '1';
2090
                        }
2091
                        break;
2092
                    }
2093
                    else if (*pszPtr == '"')
2094
                    {
2095
                        bInString = true;
2096
                    }
2097
                    else if (*pszPtr == '{' || *pszPtr == '[')
2098
                    {
2099
                        ++nLevel;
2100
                    }
2101
                    else if (*pszPtr == '}' || *pszPtr == ']')
2102
                    {
2103
                        --nLevel;
2104
                    }
2105
2106
                    ++pszPtr;
2107
                }
2108
            }
2109
        }
2110
    }
2111
#endif
2112
2113
0
    return OGRERR_NONE;
2114
0
}
2115
2116
/************************************************************************/
2117
/*                          OSRExportToPROJJSON()                       */
2118
/************************************************************************/
2119
2120
/**
2121
 * \brief Convert this SRS into PROJJSON format.
2122
 *
2123
 * This function is the same as OGRSpatialReference::exportToPROJJSON() const
2124
 *
2125
 * @since GDAL 3.1 and PROJ 6.2
2126
 */
2127
2128
OGRErr OSRExportToPROJJSON(OGRSpatialReferenceH hSRS, char **ppszReturn,
2129
                           const char *const *papszOptions)
2130
0
{
2131
0
    VALIDATE_POINTER1(hSRS, "OSRExportToPROJJSON", OGRERR_FAILURE);
2132
2133
0
    *ppszReturn = nullptr;
2134
2135
0
    return ToPointer(hSRS)->exportToPROJJSON(ppszReturn, papszOptions);
2136
0
}
2137
2138
/************************************************************************/
2139
/*                           importFromWkt()                            */
2140
/************************************************************************/
2141
2142
/**
2143
 * \brief Import from WKT string.
2144
 *
2145
 * This method will wipe the existing SRS definition, and
2146
 * reassign it based on the contents of the passed WKT string.  Only as
2147
 * much of the input string as needed to construct this SRS is consumed from
2148
 * the input string, and the input string pointer
2149
 * is then updated to point to the remaining (unused) input.
2150
 *
2151
 * Starting with PROJ 9.2, if invoked on a COORDINATEMETADATA[] construct,
2152
 * the CRS contained in it will be used to fill the OGRSpatialReference object,
2153
 * and the coordinate epoch potentially present used as the coordinate epoch
2154
 * property of the OGRSpatialReference object.
2155
 *
2156
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2157
 * Issues</a> page for implementation details of WKT in OGR.
2158
 *
2159
 * This method is the same as the C function OSRImportFromWkt().
2160
 *
2161
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2162
 * point to remaining unused input text.
2163
 *
2164
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2165
 * fails for any reason.
2166
 * @since GDAL 2.3
2167
 */
2168
2169
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput)
2170
2171
0
{
2172
0
    return importFromWkt(ppszInput, nullptr);
2173
0
}
2174
2175
/************************************************************************/
2176
/*                           importFromWkt()                            */
2177
/************************************************************************/
2178
2179
/*! @cond Doxygen_Suppress */
2180
2181
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput,
2182
                                          CSLConstList papszOptions)
2183
2184
0
{
2185
0
    return importFromWkt(&pszInput, papszOptions);
2186
0
}
2187
2188
OGRErr OGRSpatialReference::importFromWkt(const char **ppszInput,
2189
                                          CSLConstList papszOptions)
2190
2191
0
{
2192
0
    TAKE_OPTIONAL_LOCK();
2193
2194
0
    if (!ppszInput || !*ppszInput)
2195
0
        return OGRERR_FAILURE;
2196
2197
0
    if (strlen(*ppszInput) > 100 * 1000 &&
2198
0
        CPLTestBool(CPLGetConfigOption("OSR_IMPORT_FROM_WKT_LIMIT", "YES")))
2199
0
    {
2200
0
        CPLError(CE_Failure, CPLE_NotSupported,
2201
0
                 "Suspiciously large input for importFromWkt(). Rejecting it. "
2202
0
                 "You can remove this limitation by definition the "
2203
0
                 "OSR_IMPORT_FROM_WKT_LIMIT configuration option to NO.");
2204
0
        return OGRERR_FAILURE;
2205
0
    }
2206
2207
0
    Clear();
2208
2209
0
    bool canCache = false;
2210
0
    auto tlsCache = OSRGetProjTLSCache();
2211
0
    std::string osWkt;
2212
0
    if (**ppszInput)
2213
0
    {
2214
0
        osWkt = *ppszInput;
2215
0
        auto cachedObj = tlsCache->GetPJForWKT(osWkt);
2216
0
        if (cachedObj)
2217
0
        {
2218
0
            d->setPjCRS(cachedObj);
2219
0
        }
2220
0
        else
2221
0
        {
2222
0
            CPLStringList aosOptions(papszOptions);
2223
0
            if (aosOptions.FetchNameValue("STRICT") == nullptr)
2224
0
                aosOptions.SetNameValue("STRICT", "NO");
2225
0
            PROJ_STRING_LIST warnings = nullptr;
2226
0
            PROJ_STRING_LIST errors = nullptr;
2227
0
            auto ctxt = d->getPROJContext();
2228
0
            auto pj = proj_create_from_wkt(ctxt, *ppszInput, aosOptions.List(),
2229
0
                                           &warnings, &errors);
2230
0
            d->setPjCRS(pj);
2231
2232
0
            for (auto iter = warnings; iter && *iter; ++iter)
2233
0
            {
2234
0
                d->m_wktImportWarnings.push_back(*iter);
2235
0
            }
2236
0
            for (auto iter = errors; iter && *iter; ++iter)
2237
0
            {
2238
0
                d->m_wktImportErrors.push_back(*iter);
2239
0
                if (!d->m_pj_crs)
2240
0
                {
2241
0
                    CPLError(CE_Failure, CPLE_AppDefined, "%s", *iter);
2242
0
                }
2243
0
            }
2244
0
            if (warnings == nullptr && errors == nullptr)
2245
0
            {
2246
0
                canCache = true;
2247
0
            }
2248
0
            proj_string_list_destroy(warnings);
2249
0
            proj_string_list_destroy(errors);
2250
0
        }
2251
0
    }
2252
0
    if (!d->m_pj_crs)
2253
0
        return OGRERR_CORRUPT_DATA;
2254
2255
    // Only accept CRS objects
2256
0
    if (!proj_is_crs(d->m_pj_crs))
2257
0
    {
2258
0
        Clear();
2259
0
        return OGRERR_CORRUPT_DATA;
2260
0
    }
2261
2262
0
    if (canCache)
2263
0
    {
2264
0
        tlsCache->CachePJForWKT(osWkt, d->m_pj_crs);
2265
0
    }
2266
2267
0
    if (strstr(*ppszInput, "CENTER_LONG"))
2268
0
    {
2269
0
        auto poRoot = new OGR_SRSNode();
2270
0
        d->setRoot(poRoot);
2271
0
        const char *pszTmp = *ppszInput;
2272
0
        poRoot->importFromWkt(&pszTmp);
2273
0
        d->m_bHasCenterLong = true;
2274
0
    }
2275
2276
    // TODO? we don't really update correctly since we assume that the
2277
    // passed string is only WKT.
2278
0
    *ppszInput += strlen(*ppszInput);
2279
0
    return OGRERR_NONE;
2280
2281
#if no_longer_implemented_for_now
2282
    /* -------------------------------------------------------------------- */
2283
    /*      The following seems to try and detect and unconsumed            */
2284
    /*      VERTCS[] coordinate system definition (ESRI style) and to       */
2285
    /*      import and attach it to the existing root.  Likely we will      */
2286
    /*      need to extend this somewhat to bring it into an acceptable     */
2287
    /*      OGRSpatialReference organization at some point.                 */
2288
    /* -------------------------------------------------------------------- */
2289
    if (strlen(*ppszInput) > 0 && strstr(*ppszInput, "VERTCS"))
2290
    {
2291
        if (((*ppszInput)[0]) == ',')
2292
            (*ppszInput)++;
2293
        OGR_SRSNode *poNewChild = new OGR_SRSNode();
2294
        poRoot->AddChild(poNewChild);
2295
        return poNewChild->importFromWkt(ppszInput);
2296
    }
2297
#endif
2298
0
}
2299
2300
/*! @endcond */
2301
2302
/**
2303
 * \brief Import from WKT string.
2304
 *
2305
 * This method will wipe the existing SRS definition, and
2306
 * reassign it based on the contents of the passed WKT string.  Only as
2307
 * much of the input string as needed to construct this SRS is consumed from
2308
 * the input string, and the input string pointer
2309
 * is then updated to point to the remaining (unused) input.
2310
 *
2311
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2312
 * Issues</a> page for implementation details of WKT in OGR.
2313
 *
2314
 * This method is the same as the C function OSRImportFromWkt().
2315
 *
2316
 * @param ppszInput Pointer to pointer to input.  The pointer is updated to
2317
 * point to remaining unused input text.
2318
 *
2319
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2320
 * fails for any reason.
2321
 * @deprecated GDAL 2.3. Use importFromWkt(const char**) or importFromWkt(const
2322
 * char*)
2323
 */
2324
2325
OGRErr OGRSpatialReference::importFromWkt(char **ppszInput)
2326
2327
0
{
2328
0
    return importFromWkt(const_cast<const char **>(ppszInput));
2329
0
}
2330
2331
/**
2332
 * \brief Import from WKT string.
2333
 *
2334
 * This method will wipe the existing SRS definition, and
2335
 * reassign it based on the contents of the passed WKT string.  Only as
2336
 * much of the input string as needed to construct this SRS is consumed from
2337
 * the input string, and the input string pointer
2338
 * is then updated to point to the remaining (unused) input.
2339
 *
2340
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2341
 * Issues</a> page for implementation details of WKT in OGR.
2342
 *
2343
 * @param pszInput Input WKT
2344
 *
2345
 * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
2346
 * fails for any reason.
2347
 * @since GDAL 2.3
2348
 */
2349
2350
OGRErr OGRSpatialReference::importFromWkt(const char *pszInput)
2351
0
{
2352
0
    return importFromWkt(&pszInput);
2353
0
}
2354
2355
/************************************************************************/
2356
/*                              Validate()                              */
2357
/************************************************************************/
2358
2359
/**
2360
 * \brief Validate CRS imported with importFromWkt() or with modified with
2361
 * direct node manipulations. Otherwise the CRS should be always valid.
2362
 *
2363
 * This method attempts to verify that the spatial reference system is
2364
 * well formed, and consists of known tokens.  The validation is not
2365
 * comprehensive.
2366
 *
2367
 * This method is the same as the C function OSRValidate().
2368
 *
2369
 * @return OGRERR_NONE if all is fine, OGRERR_CORRUPT_DATA if the SRS is
2370
 * not well formed, and OGRERR_UNSUPPORTED_SRS if the SRS is well formed,
2371
 * but contains non-standard PROJECTION[] values.
2372
 */
2373
2374
OGRErr OGRSpatialReference::Validate() const
2375
2376
0
{
2377
0
    TAKE_OPTIONAL_LOCK();
2378
2379
0
    for (const auto &str : d->m_wktImportErrors)
2380
0
    {
2381
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2382
0
    }
2383
0
    for (const auto &str : d->m_wktImportWarnings)
2384
0
    {
2385
0
        CPLDebug("OGRSpatialReference::Validate", "%s", str.c_str());
2386
0
    }
2387
0
    if (!d->m_pj_crs || !d->m_wktImportErrors.empty())
2388
0
    {
2389
0
        return OGRERR_CORRUPT_DATA;
2390
0
    }
2391
0
    if (!d->m_wktImportWarnings.empty())
2392
0
    {
2393
0
        return OGRERR_UNSUPPORTED_SRS;
2394
0
    }
2395
0
    return OGRERR_NONE;
2396
0
}
2397
2398
/************************************************************************/
2399
/*                            OSRValidate()                             */
2400
/************************************************************************/
2401
/**
2402
 * \brief Validate SRS tokens.
2403
 *
2404
 * This function is the same as the C++ method OGRSpatialReference::Validate().
2405
 */
2406
OGRErr OSRValidate(OGRSpatialReferenceH hSRS)
2407
2408
0
{
2409
0
    VALIDATE_POINTER1(hSRS, "OSRValidate", OGRERR_FAILURE);
2410
2411
0
    return OGRSpatialReference::FromHandle(hSRS)->Validate();
2412
0
}
2413
2414
/************************************************************************/
2415
/*                          OSRImportFromWkt()                          */
2416
/************************************************************************/
2417
2418
/**
2419
 * \brief Import from WKT string.
2420
 *
2421
 * Consult also the <a href="wktproblems.html">OGC WKT Coordinate System
2422
 * Issues</a> page for implementation details of WKT in OGR.
2423
 *
2424
 * This function is the same as OGRSpatialReference::importFromWkt().
2425
 */
2426
2427
OGRErr OSRImportFromWkt(OGRSpatialReferenceH hSRS, char **ppszInput)
2428
2429
0
{
2430
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromWkt", OGRERR_FAILURE);
2431
2432
0
    return ToPointer(hSRS)->importFromWkt(const_cast<const char **>(ppszInput));
2433
0
}
2434
2435
/************************************************************************/
2436
/*                              SetNode()                               */
2437
/************************************************************************/
2438
2439
/**
2440
 * \brief Set attribute value in spatial reference.
2441
 *
2442
 * Missing intermediate nodes in the path will be created if not already
2443
 * in existence.  If the attribute has no children one will be created and
2444
 * assigned the value otherwise the zeroth child will be assigned the value.
2445
 *
2446
 * This method does the same as the C function OSRSetAttrValue().
2447
 *
2448
 * @param pszNodePath full path to attribute to be set.  For instance
2449
 * "PROJCS|GEOGCS|UNIT".
2450
 *
2451
 * @param pszNewNodeValue value to be assigned to node, such as "meter".
2452
 * This may be NULL if you just want to force creation of the intermediate
2453
 * path.
2454
 *
2455
 * @return OGRERR_NONE on success.
2456
 */
2457
2458
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath,
2459
                                    const char *pszNewNodeValue)
2460
2461
0
{
2462
0
    TAKE_OPTIONAL_LOCK();
2463
2464
0
    char **papszPathTokens =
2465
0
        CSLTokenizeStringComplex(pszNodePath, "|", TRUE, FALSE);
2466
2467
0
    if (CSLCount(papszPathTokens) < 1)
2468
0
    {
2469
0
        CSLDestroy(papszPathTokens);
2470
0
        return OGRERR_FAILURE;
2471
0
    }
2472
2473
0
    if (GetRoot() == nullptr ||
2474
0
        !EQUAL(papszPathTokens[0], GetRoot()->GetValue()))
2475
0
    {
2476
0
        if (EQUAL(papszPathTokens[0], "PROJCS") &&
2477
0
            CSLCount(papszPathTokens) == 1)
2478
0
        {
2479
0
            CSLDestroy(papszPathTokens);
2480
0
            return SetProjCS(pszNewNodeValue);
2481
0
        }
2482
0
        else
2483
0
        {
2484
0
            SetRoot(new OGR_SRSNode(papszPathTokens[0]));
2485
0
        }
2486
0
    }
2487
2488
0
    OGR_SRSNode *poNode = GetRoot();
2489
0
    for (int i = 1; papszPathTokens[i] != nullptr; i++)
2490
0
    {
2491
0
        int j = 0;  // Used after for.
2492
2493
0
        for (; j < poNode->GetChildCount(); j++)
2494
0
        {
2495
0
            if (EQUAL(poNode->GetChild(j)->GetValue(), papszPathTokens[i]))
2496
0
            {
2497
0
                poNode = poNode->GetChild(j);
2498
0
                j = -1;
2499
0
                break;
2500
0
            }
2501
0
        }
2502
2503
0
        if (j != -1)
2504
0
        {
2505
0
            OGR_SRSNode *poNewNode = new OGR_SRSNode(papszPathTokens[i]);
2506
0
            poNode->AddChild(poNewNode);
2507
0
            poNode = poNewNode;
2508
0
        }
2509
0
    }
2510
2511
0
    CSLDestroy(papszPathTokens);
2512
2513
0
    if (pszNewNodeValue != nullptr)
2514
0
    {
2515
0
        if (poNode->GetChildCount() > 0)
2516
0
            poNode->GetChild(0)->SetValue(pszNewNodeValue);
2517
0
        else
2518
0
            poNode->AddChild(new OGR_SRSNode(pszNewNodeValue));
2519
0
    };
2520
0
    return OGRERR_NONE;
2521
0
}
2522
2523
/************************************************************************/
2524
/*                          OSRSetAttrValue()                           */
2525
/************************************************************************/
2526
2527
/**
2528
 * \brief Set attribute value in spatial reference.
2529
 *
2530
 * This function is the same as OGRSpatialReference::SetNode()
2531
 */
2532
OGRErr CPL_STDCALL OSRSetAttrValue(OGRSpatialReferenceH hSRS,
2533
                                   const char *pszPath, const char *pszValue)
2534
2535
0
{
2536
0
    VALIDATE_POINTER1(hSRS, "OSRSetAttrValue", OGRERR_FAILURE);
2537
2538
0
    return ToPointer(hSRS)->SetNode(pszPath, pszValue);
2539
0
}
2540
2541
/************************************************************************/
2542
/*                              SetNode()                               */
2543
/************************************************************************/
2544
2545
/**
2546
 * \brief Set attribute value in spatial reference.
2547
 *
2548
 * Missing intermediate nodes in the path will be created if not already
2549
 * in existence.  If the attribute has no children one will be created and
2550
 * assigned the value otherwise the zeroth child will be assigned the value.
2551
 *
2552
 * This method does the same as the C function OSRSetAttrValue().
2553
 *
2554
 * @param pszNodePath full path to attribute to be set.  For instance
2555
 * "PROJCS|GEOGCS|UNIT".
2556
 *
2557
 * @param dfValue value to be assigned to node.
2558
 *
2559
 * @return OGRERR_NONE on success.
2560
 */
2561
2562
OGRErr OGRSpatialReference::SetNode(const char *pszNodePath, double dfValue)
2563
2564
0
{
2565
0
    char szValue[64] = {'\0'};
2566
2567
0
    if (std::abs(dfValue - static_cast<int>(dfValue)) == 0.0)
2568
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfValue));
2569
0
    else
2570
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
2571
2572
0
    return SetNode(pszNodePath, szValue);
2573
0
}
2574
2575
/************************************************************************/
2576
/*                          SetAngularUnits()                           */
2577
/************************************************************************/
2578
2579
/**
2580
 * \brief Set the angular units for the geographic coordinate system.
2581
 *
2582
 * This method creates a UNIT subnode with the specified values as a
2583
 * child of the GEOGCS node.
2584
 *
2585
 * This method does the same as the C function OSRSetAngularUnits().
2586
 *
2587
 * @param pszUnitsName the units name to be used.  Some preferred units
2588
 * names can be found in ogr_srs_api.h such as SRS_UA_DEGREE.
2589
 *
2590
 * @param dfInRadians the value to multiple by an angle in the indicated
2591
 * units to transform to radians.  Some standard conversion factors can
2592
 * be found in ogr_srs_api.h.
2593
 *
2594
 * @return OGRERR_NONE on success.
2595
 */
2596
2597
OGRErr OGRSpatialReference::SetAngularUnits(const char *pszUnitsName,
2598
                                            double dfInRadians)
2599
2600
0
{
2601
0
    TAKE_OPTIONAL_LOCK();
2602
2603
0
    d->bNormInfoSet = FALSE;
2604
2605
0
    d->refreshProjObj();
2606
0
    if (!d->m_pj_crs)
2607
0
        return OGRERR_FAILURE;
2608
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2609
0
    if (!geodCRS)
2610
0
        return OGRERR_FAILURE;
2611
0
    proj_destroy(geodCRS);
2612
0
    d->demoteFromBoundCRS();
2613
0
    d->setPjCRS(proj_crs_alter_cs_angular_unit(d->getPROJContext(), d->m_pj_crs,
2614
0
                                               pszUnitsName, dfInRadians,
2615
0
                                               nullptr, nullptr));
2616
0
    d->undoDemoteFromBoundCRS();
2617
2618
0
    d->m_osAngularUnits = pszUnitsName;
2619
0
    d->m_dfAngularUnitToRadian = dfInRadians;
2620
2621
0
    return OGRERR_NONE;
2622
0
}
2623
2624
/************************************************************************/
2625
/*                         OSRSetAngularUnits()                         */
2626
/************************************************************************/
2627
2628
/**
2629
 * \brief Set the angular units for the geographic coordinate system.
2630
 *
2631
 * This function is the same as OGRSpatialReference::SetAngularUnits()
2632
 */
2633
OGRErr OSRSetAngularUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2634
                          double dfInRadians)
2635
2636
0
{
2637
0
    VALIDATE_POINTER1(hSRS, "OSRSetAngularUnits", OGRERR_FAILURE);
2638
2639
0
    return ToPointer(hSRS)->SetAngularUnits(pszUnits, dfInRadians);
2640
0
}
2641
2642
/************************************************************************/
2643
/*                          GetAngularUnits()                           */
2644
/************************************************************************/
2645
2646
/**
2647
 * \brief Fetch angular geographic coordinate system units.
2648
 *
2649
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2650
 * will be assumed.  This method only checks directly under the GEOGCS node
2651
 * for units.
2652
 *
2653
 * This method does the same thing as the C function OSRGetAngularUnits().
2654
 *
2655
 * @param ppszName a pointer to be updated with the pointer to the units name.
2656
 * The returned value remains internal to the OGRSpatialReference and should
2657
 * not be freed, or modified.  It may be invalidated on the next
2658
 * OGRSpatialReference call.
2659
 *
2660
 * @return the value to multiply by angular distances to transform them to
2661
 * radians.
2662
 * @since GDAL 2.3.0
2663
 */
2664
2665
double OGRSpatialReference::GetAngularUnits(const char **ppszName) const
2666
2667
0
{
2668
0
    TAKE_OPTIONAL_LOCK();
2669
2670
0
    d->refreshProjObj();
2671
2672
0
    if (!d->m_osAngularUnits.empty())
2673
0
    {
2674
0
        if (ppszName != nullptr)
2675
0
            *ppszName = d->m_osAngularUnits.c_str();
2676
0
        return d->m_dfAngularUnitToRadian;
2677
0
    }
2678
2679
0
    do
2680
0
    {
2681
0
        if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
2682
0
        {
2683
0
            break;
2684
0
        }
2685
2686
0
        auto geodCRS =
2687
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
2688
0
        if (!geodCRS)
2689
0
        {
2690
0
            break;
2691
0
        }
2692
0
        auto coordSys =
2693
0
            proj_crs_get_coordinate_system(d->getPROJContext(), geodCRS);
2694
0
        proj_destroy(geodCRS);
2695
0
        if (!coordSys)
2696
0
        {
2697
0
            break;
2698
0
        }
2699
0
        if (proj_cs_get_type(d->getPROJContext(), coordSys) !=
2700
0
            PJ_CS_TYPE_ELLIPSOIDAL)
2701
0
        {
2702
0
            proj_destroy(coordSys);
2703
0
            break;
2704
0
        }
2705
2706
0
        double dfConvFactor = 0.0;
2707
0
        const char *pszUnitName = nullptr;
2708
0
        if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
2709
0
                                   nullptr, nullptr, &dfConvFactor,
2710
0
                                   &pszUnitName, nullptr, nullptr))
2711
0
        {
2712
0
            proj_destroy(coordSys);
2713
0
            break;
2714
0
        }
2715
2716
0
        d->m_osAngularUnits = pszUnitName;
2717
2718
0
        proj_destroy(coordSys);
2719
0
        d->m_dfAngularUnitToRadian = dfConvFactor;
2720
0
    } while (false);
2721
2722
0
    if (d->m_osAngularUnits.empty())
2723
0
    {
2724
0
        d->m_osAngularUnits = "degree";
2725
0
        d->m_dfAngularUnitToRadian = CPLAtof(SRS_UA_DEGREE_CONV);
2726
0
    }
2727
2728
0
    if (ppszName != nullptr)
2729
0
        *ppszName = d->m_osAngularUnits.c_str();
2730
0
    return d->m_dfAngularUnitToRadian;
2731
0
}
2732
2733
/**
2734
 * \brief Fetch angular geographic coordinate system units.
2735
 *
2736
 * If no units are available, a value of "degree" and SRS_UA_DEGREE_CONV
2737
 * will be assumed.  This method only checks directly under the GEOGCS node
2738
 * for units.
2739
 *
2740
 * This method does the same thing as the C function OSRGetAngularUnits().
2741
 *
2742
 * @param ppszName a pointer to be updated with the pointer to the units name.
2743
 * The returned value remains internal to the OGRSpatialReference and should
2744
 * not be freed, or modified.  It may be invalidated on the next
2745
 * OGRSpatialReference call.
2746
 *
2747
 * @return the value to multiply by angular distances to transform them to
2748
 * radians.
2749
 * @deprecated GDAL 2.3.0. Use GetAngularUnits(const char**) const.
2750
 */
2751
2752
double OGRSpatialReference::GetAngularUnits(char **ppszName) const
2753
2754
0
{
2755
0
    return GetAngularUnits(const_cast<const char **>(ppszName));
2756
0
}
2757
2758
/************************************************************************/
2759
/*                         OSRGetAngularUnits()                         */
2760
/************************************************************************/
2761
2762
/**
2763
 * \brief Fetch angular geographic coordinate system units.
2764
 *
2765
 * This function is the same as OGRSpatialReference::GetAngularUnits()
2766
 */
2767
double OSRGetAngularUnits(OGRSpatialReferenceH hSRS, char **ppszName)
2768
2769
0
{
2770
0
    VALIDATE_POINTER1(hSRS, "OSRGetAngularUnits", 0);
2771
2772
0
    return ToPointer(hSRS)->GetAngularUnits(
2773
0
        const_cast<const char **>(ppszName));
2774
0
}
2775
2776
/************************************************************************/
2777
/*                 SetLinearUnitsAndUpdateParameters()                  */
2778
/************************************************************************/
2779
2780
/**
2781
 * \brief Set the linear units for the projection.
2782
 *
2783
 * This method creates a UNIT subnode with the specified values as a
2784
 * child of the PROJCS or LOCAL_CS node.   It works the same as the
2785
 * SetLinearUnits() method, but it also updates all existing linear
2786
 * projection parameter values from the old units to the new units.
2787
 *
2788
 * @param pszName the units name to be used.  Some preferred units
2789
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2790
 * and SRS_UL_US_FOOT.
2791
 *
2792
 * @param dfInMeters the value to multiple by a length in the indicated
2793
 * units to transform to meters.  Some standard conversion factors can
2794
 * be found in ogr_srs_api.h.
2795
 *
2796
 * @param pszUnitAuthority Unit authority name. Or nullptr
2797
 *
2798
 * @param pszUnitCode Unit code. Or nullptr
2799
 *
2800
 * @return OGRERR_NONE on success.
2801
 */
2802
2803
OGRErr OGRSpatialReference::SetLinearUnitsAndUpdateParameters(
2804
    const char *pszName, double dfInMeters, const char *pszUnitAuthority,
2805
    const char *pszUnitCode)
2806
2807
0
{
2808
0
    TAKE_OPTIONAL_LOCK();
2809
2810
0
    if (dfInMeters <= 0.0)
2811
0
        return OGRERR_FAILURE;
2812
2813
0
    d->refreshProjObj();
2814
0
    if (!d->m_pj_crs)
2815
0
        return OGRERR_FAILURE;
2816
2817
0
    d->demoteFromBoundCRS();
2818
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2819
0
    {
2820
0
        d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2821
0
            d->getPROJContext(), d->m_pj_crs, pszName, dfInMeters,
2822
0
            pszUnitAuthority, pszUnitCode, true));
2823
0
    }
2824
0
    d->setPjCRS(proj_crs_alter_cs_linear_unit(d->getPROJContext(), d->m_pj_crs,
2825
0
                                              pszName, dfInMeters,
2826
0
                                              pszUnitAuthority, pszUnitCode));
2827
0
    d->undoDemoteFromBoundCRS();
2828
2829
0
    d->m_osLinearUnits = pszName;
2830
0
    d->dfToMeter = dfInMeters;
2831
2832
0
    return OGRERR_NONE;
2833
0
}
2834
2835
/************************************************************************/
2836
/*                OSRSetLinearUnitsAndUpdateParameters()                */
2837
/************************************************************************/
2838
2839
/**
2840
 * \brief Set the linear units for the projection.
2841
 *
2842
 * This function is the same as
2843
 *   OGRSpatialReference::SetLinearUnitsAndUpdateParameters()
2844
 */
2845
OGRErr OSRSetLinearUnitsAndUpdateParameters(OGRSpatialReferenceH hSRS,
2846
                                            const char *pszUnits,
2847
                                            double dfInMeters)
2848
2849
0
{
2850
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnitsAndUpdateParameters",
2851
0
                      OGRERR_FAILURE);
2852
2853
0
    return ToPointer(hSRS)->SetLinearUnitsAndUpdateParameters(pszUnits,
2854
0
                                                              dfInMeters);
2855
0
}
2856
2857
/************************************************************************/
2858
/*                           SetLinearUnits()                           */
2859
/************************************************************************/
2860
2861
/**
2862
 * \brief Set the linear units for the projection.
2863
 *
2864
 * This method creates a UNIT subnode with the specified values as a
2865
 * child of the PROJCS, GEOCCS, GEOGCS or LOCAL_CS node. When called on a
2866
 * Geographic 3D CRS the vertical axis units will be set.
2867
 *
2868
 * This method does the same as the C function OSRSetLinearUnits().
2869
 *
2870
 * @param pszUnitsName the units name to be used.  Some preferred units
2871
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2872
 * and SRS_UL_US_FOOT.
2873
 *
2874
 * @param dfInMeters the value to multiple by a length in the indicated
2875
 * units to transform to meters.  Some standard conversion factors can
2876
 * be found in ogr_srs_api.h.
2877
 *
2878
 * @return OGRERR_NONE on success.
2879
 */
2880
2881
OGRErr OGRSpatialReference::SetLinearUnits(const char *pszUnitsName,
2882
                                           double dfInMeters)
2883
2884
0
{
2885
0
    return SetTargetLinearUnits(nullptr, pszUnitsName, dfInMeters);
2886
0
}
2887
2888
/************************************************************************/
2889
/*                         OSRSetLinearUnits()                          */
2890
/************************************************************************/
2891
2892
/**
2893
 * \brief Set the linear units for the projection.
2894
 *
2895
 * This function is the same as OGRSpatialReference::SetLinearUnits()
2896
 */
2897
OGRErr OSRSetLinearUnits(OGRSpatialReferenceH hSRS, const char *pszUnits,
2898
                         double dfInMeters)
2899
2900
0
{
2901
0
    VALIDATE_POINTER1(hSRS, "OSRSetLinearUnits", OGRERR_FAILURE);
2902
2903
0
    return ToPointer(hSRS)->SetLinearUnits(pszUnits, dfInMeters);
2904
0
}
2905
2906
/************************************************************************/
2907
/*                        SetTargetLinearUnits()                        */
2908
/************************************************************************/
2909
2910
/**
2911
 * \brief Set the linear units for the projection.
2912
 *
2913
 * This method creates a UNIT subnode with the specified values as a
2914
 * child of the target node.
2915
 *
2916
 * This method does the same as the C function OSRSetTargetLinearUnits().
2917
 *
2918
 * @param pszTargetKey the keyword to set the linear units for.
2919
 * i.e. "PROJCS" or "VERT_CS"
2920
 *
2921
 * @param pszUnitsName the units name to be used.  Some preferred units
2922
 * names can be found in ogr_srs_api.h such as SRS_UL_METER, SRS_UL_FOOT
2923
 * and SRS_UL_US_FOOT.
2924
 *
2925
 * @param dfInMeters the value to multiple by a length in the indicated
2926
 * units to transform to meters.  Some standard conversion factors can
2927
 * be found in ogr_srs_api.h.
2928
 *
2929
 * @param pszUnitAuthority Unit authority name. Or nullptr
2930
 *
2931
 * @param pszUnitCode Unit code. Or nullptr
2932
 *
2933
 * @return OGRERR_NONE on success.
2934
 *
2935
 * @since OGR 1.9.0
2936
 */
2937
2938
OGRErr OGRSpatialReference::SetTargetLinearUnits(const char *pszTargetKey,
2939
                                                 const char *pszUnitsName,
2940
                                                 double dfInMeters,
2941
                                                 const char *pszUnitAuthority,
2942
                                                 const char *pszUnitCode)
2943
2944
0
{
2945
0
    TAKE_OPTIONAL_LOCK();
2946
2947
0
    if (dfInMeters <= 0.0)
2948
0
        return OGRERR_FAILURE;
2949
2950
0
    d->refreshProjObj();
2951
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
2952
0
    if (pszTargetKey == nullptr)
2953
0
    {
2954
0
        if (!d->m_pj_crs)
2955
0
            return OGRERR_FAILURE;
2956
2957
0
        d->demoteFromBoundCRS();
2958
0
        if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
2959
0
        {
2960
0
            d->setPjCRS(proj_crs_alter_parameters_linear_unit(
2961
0
                d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2962
0
                pszUnitAuthority, pszUnitCode, false));
2963
0
        }
2964
0
        d->setPjCRS(proj_crs_alter_cs_linear_unit(
2965
0
            d->getPROJContext(), d->m_pj_crs, pszUnitsName, dfInMeters,
2966
0
            pszUnitAuthority, pszUnitCode));
2967
0
        d->undoDemoteFromBoundCRS();
2968
2969
0
        d->m_osLinearUnits = pszUnitsName;
2970
0
        d->dfToMeter = dfInMeters;
2971
2972
0
        return OGRERR_NONE;
2973
0
    }
2974
2975
0
    OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
2976
2977
0
    if (poCS == nullptr)
2978
0
        return OGRERR_FAILURE;
2979
2980
0
    char szValue[128] = {'\0'};
2981
0
    if (dfInMeters < std::numeric_limits<int>::max() &&
2982
0
        dfInMeters > std::numeric_limits<int>::min() &&
2983
0
        dfInMeters == static_cast<int>(dfInMeters))
2984
0
        snprintf(szValue, sizeof(szValue), "%d", static_cast<int>(dfInMeters));
2985
0
    else
2986
0
        OGRsnPrintDouble(szValue, sizeof(szValue), dfInMeters);
2987
2988
0
    OGR_SRSNode *poUnits = nullptr;
2989
0
    if (poCS->FindChild("UNIT") >= 0)
2990
0
    {
2991
0
        poUnits = poCS->GetChild(poCS->FindChild("UNIT"));
2992
0
        if (poUnits->GetChildCount() < 2)
2993
0
            return OGRERR_FAILURE;
2994
0
        poUnits->GetChild(0)->SetValue(pszUnitsName);
2995
0
        poUnits->GetChild(1)->SetValue(szValue);
2996
0
        if (poUnits->FindChild("AUTHORITY") != -1)
2997
0
            poUnits->DestroyChild(poUnits->FindChild("AUTHORITY"));
2998
0
    }
2999
0
    else
3000
0
    {
3001
0
        poUnits = new OGR_SRSNode("UNIT");
3002
0
        poUnits->AddChild(new OGR_SRSNode(pszUnitsName));
3003
0
        poUnits->AddChild(new OGR_SRSNode(szValue));
3004
3005
0
        poCS->AddChild(poUnits);
3006
0
    }
3007
3008
0
    return OGRERR_NONE;
3009
0
}
3010
3011
/************************************************************************/
3012
/*                         OSRSetLinearUnits()                          */
3013
/************************************************************************/
3014
3015
/**
3016
 * \brief Set the linear units for the target node.
3017
 *
3018
 * This function is the same as OGRSpatialReference::SetTargetLinearUnits()
3019
 *
3020
 * @since OGR 1.9.0
3021
 */
3022
OGRErr OSRSetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3023
                               const char *pszTargetKey, const char *pszUnits,
3024
                               double dfInMeters)
3025
3026
0
{
3027
0
    VALIDATE_POINTER1(hSRS, "OSRSetTargetLinearUnits", OGRERR_FAILURE);
3028
3029
0
    return ToPointer(hSRS)->SetTargetLinearUnits(pszTargetKey, pszUnits,
3030
0
                                                 dfInMeters);
3031
0
}
3032
3033
/************************************************************************/
3034
/*                           GetLinearUnits()                           */
3035
/************************************************************************/
3036
3037
/**
3038
 * \brief Fetch linear projection units.
3039
 *
3040
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3041
 * This method only checks directly under the PROJCS, GEOCCS, GEOGCS or
3042
 * LOCAL_CS node for units. When called on a Geographic 3D CRS the vertical
3043
 * axis units will be returned.
3044
 *
3045
 * This method does the same thing as the C function OSRGetLinearUnits()
3046
 *
3047
 * @param ppszName a pointer to be updated with the pointer to the units name.
3048
 * The returned value remains internal to the OGRSpatialReference and should
3049
 * not be freed, or modified.  It may be invalidated on the next
3050
 * OGRSpatialReference call.
3051
 *
3052
 * @return the value to multiply by linear distances to transform them to
3053
 * meters.
3054
 * @deprecated GDAL 2.3.0. Use GetLinearUnits(const char**) const.
3055
 */
3056
3057
double OGRSpatialReference::GetLinearUnits(char **ppszName) const
3058
3059
0
{
3060
0
    return GetTargetLinearUnits(nullptr, const_cast<const char **>(ppszName));
3061
0
}
3062
3063
/**
3064
 * \brief Fetch linear projection units.
3065
 *
3066
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3067
 * This method only checks directly under the PROJCS, GEOCCS or LOCAL_CS node
3068
 * for units.
3069
 *
3070
 * This method does the same thing as the C function OSRGetLinearUnits()
3071
 *
3072
 * @param ppszName a pointer to be updated with the pointer to the units name.
3073
 * The returned value remains internal to the OGRSpatialReference and should
3074
 * not be freed, or modified.  It may be invalidated on the next
3075
 * OGRSpatialReference call.
3076
 *
3077
 * @return the value to multiply by linear distances to transform them to
3078
 * meters.
3079
 * @since GDAL 2.3.0
3080
 */
3081
3082
double OGRSpatialReference::GetLinearUnits(const char **ppszName) const
3083
3084
0
{
3085
0
    return GetTargetLinearUnits(nullptr, ppszName);
3086
0
}
3087
3088
/************************************************************************/
3089
/*                         OSRGetLinearUnits()                          */
3090
/************************************************************************/
3091
3092
/**
3093
 * \brief Fetch linear projection units.
3094
 *
3095
 * This function is the same as OGRSpatialReference::GetLinearUnits()
3096
 */
3097
double OSRGetLinearUnits(OGRSpatialReferenceH hSRS, char **ppszName)
3098
3099
0
{
3100
0
    VALIDATE_POINTER1(hSRS, "OSRGetLinearUnits", 0);
3101
3102
0
    return ToPointer(hSRS)->GetLinearUnits(const_cast<const char **>(ppszName));
3103
0
}
3104
3105
/************************************************************************/
3106
/*                        GetTargetLinearUnits()                        */
3107
/************************************************************************/
3108
3109
/**
3110
 * \brief Fetch linear units for target.
3111
 *
3112
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3113
 *
3114
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3115
 *
3116
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3117
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3118
 * GEOCCS, GEOGCS and VERT_CS are looked up)
3119
 * @param ppszName a pointer to be updated with the pointer to the units name.
3120
 * The returned value remains internal to the OGRSpatialReference and should not
3121
 * be freed, or modified.  It may be invalidated on the next
3122
 * OGRSpatialReference call. ppszName can be set to NULL.
3123
 *
3124
 * @return the value to multiply by linear distances to transform them to
3125
 * meters.
3126
 *
3127
 * @since OGR 1.9.0
3128
 * @deprecated GDAL 2.3.0. Use GetTargetLinearUnits(const char*, const char**)
3129
 * const.
3130
 */
3131
3132
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3133
                                                 const char **ppszName) const
3134
3135
0
{
3136
0
    TAKE_OPTIONAL_LOCK();
3137
3138
0
    d->refreshProjObj();
3139
3140
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
3141
0
    if (pszTargetKey == nullptr)
3142
0
    {
3143
        // Use cached result if available
3144
0
        if (!d->m_osLinearUnits.empty())
3145
0
        {
3146
0
            if (ppszName)
3147
0
                *ppszName = d->m_osLinearUnits.c_str();
3148
0
            return d->dfToMeter;
3149
0
        }
3150
3151
0
        while (true)
3152
0
        {
3153
0
            if (d->m_pj_crs == nullptr)
3154
0
            {
3155
0
                break;
3156
0
            }
3157
3158
0
            d->demoteFromBoundCRS();
3159
0
            PJ *coordSys = nullptr;
3160
0
            if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
3161
0
            {
3162
0
                for (int iComponent = 0; iComponent < 2; iComponent++)
3163
0
                {
3164
0
                    auto subCRS = proj_crs_get_sub_crs(d->getPROJContext(),
3165
0
                                                       d->m_pj_crs, iComponent);
3166
0
                    if (subCRS && proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
3167
0
                    {
3168
0
                        auto temp =
3169
0
                            proj_get_source_crs(d->getPROJContext(), subCRS);
3170
0
                        proj_destroy(subCRS);
3171
0
                        subCRS = temp;
3172
0
                    }
3173
0
                    if (subCRS &&
3174
0
                        (proj_get_type(subCRS) == PJ_TYPE_PROJECTED_CRS ||
3175
0
                         proj_get_type(subCRS) == PJ_TYPE_ENGINEERING_CRS ||
3176
0
                         proj_get_type(subCRS) == PJ_TYPE_VERTICAL_CRS))
3177
0
                    {
3178
0
                        coordSys = proj_crs_get_coordinate_system(
3179
0
                            d->getPROJContext(), subCRS);
3180
0
                        proj_destroy(subCRS);
3181
0
                        break;
3182
0
                    }
3183
0
                    else if (subCRS)
3184
0
                    {
3185
0
                        proj_destroy(subCRS);
3186
0
                    }
3187
0
                }
3188
0
                if (coordSys == nullptr)
3189
0
                {
3190
0
                    d->undoDemoteFromBoundCRS();
3191
0
                    break;
3192
0
                }
3193
0
            }
3194
0
            else
3195
0
            {
3196
0
                coordSys = proj_crs_get_coordinate_system(d->getPROJContext(),
3197
0
                                                          d->m_pj_crs);
3198
0
            }
3199
3200
0
            d->undoDemoteFromBoundCRS();
3201
0
            if (!coordSys)
3202
0
            {
3203
0
                break;
3204
0
            }
3205
0
            auto csType = proj_cs_get_type(d->getPROJContext(), coordSys);
3206
3207
0
            if (csType != PJ_CS_TYPE_CARTESIAN &&
3208
0
                csType != PJ_CS_TYPE_VERTICAL &&
3209
0
                csType != PJ_CS_TYPE_ELLIPSOIDAL &&
3210
0
                csType != PJ_CS_TYPE_SPHERICAL)
3211
0
            {
3212
0
                proj_destroy(coordSys);
3213
0
                break;
3214
0
            }
3215
3216
0
            int axis = 0;
3217
3218
0
            if (csType == PJ_CS_TYPE_ELLIPSOIDAL ||
3219
0
                csType == PJ_CS_TYPE_SPHERICAL)
3220
0
            {
3221
0
                const int axisCount =
3222
0
                    proj_cs_get_axis_count(d->getPROJContext(), coordSys);
3223
3224
0
                if (axisCount == 3)
3225
0
                {
3226
0
                    axis = 2;
3227
0
                }
3228
0
                else
3229
0
                {
3230
0
                    proj_destroy(coordSys);
3231
0
                    break;
3232
0
                }
3233
0
            }
3234
3235
0
            double dfConvFactor = 0.0;
3236
0
            const char *pszUnitName = nullptr;
3237
0
            if (!proj_cs_get_axis_info(d->getPROJContext(), coordSys, axis,
3238
0
                                       nullptr, nullptr, nullptr, &dfConvFactor,
3239
0
                                       &pszUnitName, nullptr, nullptr))
3240
0
            {
3241
0
                proj_destroy(coordSys);
3242
0
                break;
3243
0
            }
3244
3245
0
            d->m_osLinearUnits = pszUnitName;
3246
0
            d->dfToMeter = dfConvFactor;
3247
0
            if (ppszName)
3248
0
                *ppszName = d->m_osLinearUnits.c_str();
3249
3250
0
            proj_destroy(coordSys);
3251
0
            return dfConvFactor;
3252
0
        }
3253
3254
0
        d->m_osLinearUnits = "unknown";
3255
0
        d->dfToMeter = 1.0;
3256
3257
0
        if (ppszName != nullptr)
3258
0
            *ppszName = d->m_osLinearUnits.c_str();
3259
0
        return 1.0;
3260
0
    }
3261
3262
0
    const OGR_SRSNode *poCS = GetAttrNode(pszTargetKey);
3263
3264
0
    if (ppszName != nullptr)
3265
0
        *ppszName = "unknown";
3266
3267
0
    if (poCS == nullptr)
3268
0
        return 1.0;
3269
3270
0
    for (int iChild = 0; iChild < poCS->GetChildCount(); iChild++)
3271
0
    {
3272
0
        const OGR_SRSNode *poChild = poCS->GetChild(iChild);
3273
3274
0
        if (EQUAL(poChild->GetValue(), "UNIT") && poChild->GetChildCount() >= 2)
3275
0
        {
3276
0
            if (ppszName != nullptr)
3277
0
                *ppszName = poChild->GetChild(0)->GetValue();
3278
3279
0
            return CPLAtof(poChild->GetChild(1)->GetValue());
3280
0
        }
3281
0
    }
3282
3283
0
    return 1.0;
3284
0
}
3285
3286
/**
3287
 * \brief Fetch linear units for target.
3288
 *
3289
 * If no units are available, a value of "Meters" and 1.0 will be assumed.
3290
 *
3291
 * This method does the same thing as the C function OSRGetTargetLinearUnits()
3292
 *
3293
 * @param pszTargetKey the key to look on. i.e. "PROJCS" or "VERT_CS". Might be
3294
 * NULL, in which case PROJCS will be implied (and if not found, LOCAL_CS,
3295
 * GEOCCS and VERT_CS are looked up)
3296
 * @param ppszName a pointer to be updated with the pointer to the units name.
3297
 * The returned value remains internal to the OGRSpatialReference and should not
3298
 * be freed, or modified.  It may be invalidated on the next
3299
 * OGRSpatialReference call. ppszName can be set to NULL.
3300
 *
3301
 * @return the value to multiply by linear distances to transform them to
3302
 * meters.
3303
 *
3304
 * @since GDAL 2.3.0
3305
 */
3306
3307
double OGRSpatialReference::GetTargetLinearUnits(const char *pszTargetKey,
3308
                                                 char **ppszName) const
3309
3310
0
{
3311
0
    return GetTargetLinearUnits(pszTargetKey,
3312
0
                                const_cast<const char **>(ppszName));
3313
0
}
3314
3315
/************************************************************************/
3316
/*                      OSRGetTargetLinearUnits()                       */
3317
/************************************************************************/
3318
3319
/**
3320
 * \brief Fetch linear projection units.
3321
 *
3322
 * This function is the same as OGRSpatialReference::GetTargetLinearUnits()
3323
 *
3324
 * @since OGR 1.9.0
3325
 */
3326
double OSRGetTargetLinearUnits(OGRSpatialReferenceH hSRS,
3327
                               const char *pszTargetKey, char **ppszName)
3328
3329
0
{
3330
0
    VALIDATE_POINTER1(hSRS, "OSRGetTargetLinearUnits", 0);
3331
3332
0
    return ToPointer(hSRS)->GetTargetLinearUnits(
3333
0
        pszTargetKey, const_cast<const char **>(ppszName));
3334
0
}
3335
3336
/************************************************************************/
3337
/*                          GetPrimeMeridian()                          */
3338
/************************************************************************/
3339
3340
/**
3341
 * \brief Fetch prime meridian info.
3342
 *
3343
 * Returns the offset of the prime meridian from greenwich in degrees,
3344
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3345
 * in the coordinate system definition a value of "Greenwich" and an
3346
 * offset of 0.0 is assumed.
3347
 *
3348
 * If the prime meridian name is returned, the pointer is to an internal
3349
 * copy of the name. It should not be freed, altered or depended on after
3350
 * the next OGR call.
3351
 *
3352
 * This method is the same as the C function OSRGetPrimeMeridian().
3353
 *
3354
 * @param ppszName return location for prime meridian name.  If NULL, name
3355
 * is not returned.
3356
 *
3357
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3358
 * degrees.
3359
 * @deprecated GDAL 2.3.0. Use GetPrimeMeridian(const char**) const.
3360
 */
3361
3362
double OGRSpatialReference::GetPrimeMeridian(const char **ppszName) const
3363
3364
0
{
3365
0
    TAKE_OPTIONAL_LOCK();
3366
3367
0
    d->refreshProjObj();
3368
3369
0
    if (!d->m_osPrimeMeridianName.empty())
3370
0
    {
3371
0
        if (ppszName != nullptr)
3372
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3373
0
        return d->dfFromGreenwich;
3374
0
    }
3375
3376
0
    while (true)
3377
0
    {
3378
0
        if (!d->m_pj_crs)
3379
0
            break;
3380
3381
0
        auto pm = proj_get_prime_meridian(d->getPROJContext(), d->m_pj_crs);
3382
0
        if (!pm)
3383
0
            break;
3384
3385
0
        d->m_osPrimeMeridianName = proj_get_name(pm);
3386
0
        if (ppszName)
3387
0
            *ppszName = d->m_osPrimeMeridianName.c_str();
3388
0
        double dfLongitude = 0.0;
3389
0
        double dfConvFactor = 0.0;
3390
0
        proj_prime_meridian_get_parameters(
3391
0
            d->getPROJContext(), pm, &dfLongitude, &dfConvFactor, nullptr);
3392
0
        proj_destroy(pm);
3393
0
        d->dfFromGreenwich =
3394
0
            dfLongitude * dfConvFactor / CPLAtof(SRS_UA_DEGREE_CONV);
3395
0
        return d->dfFromGreenwich;
3396
0
    }
3397
3398
0
    d->m_osPrimeMeridianName = SRS_PM_GREENWICH;
3399
0
    d->dfFromGreenwich = 0.0;
3400
0
    if (ppszName != nullptr)
3401
0
        *ppszName = d->m_osPrimeMeridianName.c_str();
3402
0
    return d->dfFromGreenwich;
3403
0
}
3404
3405
/**
3406
 * \brief Fetch prime meridian info.
3407
 *
3408
 * Returns the offset of the prime meridian from greenwich in degrees,
3409
 * and the prime meridian name (if requested).   If no PRIMEM value exists
3410
 * in the coordinate system definition a value of "Greenwich" and an
3411
 * offset of 0.0 is assumed.
3412
 *
3413
 * If the prime meridian name is returned, the pointer is to an internal
3414
 * copy of the name. It should not be freed, altered or depended on after
3415
 * the next OGR call.
3416
 *
3417
 * This method is the same as the C function OSRGetPrimeMeridian().
3418
 *
3419
 * @param ppszName return location for prime meridian name.  If NULL, name
3420
 * is not returned.
3421
 *
3422
 * @return the offset to the GEOGCS prime meridian from greenwich in decimal
3423
 * degrees.
3424
 * @since GDAL 2.3.0
3425
 */
3426
3427
double OGRSpatialReference::GetPrimeMeridian(char **ppszName) const
3428
3429
0
{
3430
0
    return GetPrimeMeridian(const_cast<const char **>(ppszName));
3431
0
}
3432
3433
/************************************************************************/
3434
/*                        OSRGetPrimeMeridian()                         */
3435
/************************************************************************/
3436
3437
/**
3438
 * \brief Fetch prime meridian info.
3439
 *
3440
 * This function is the same as OGRSpatialReference::GetPrimeMeridian()
3441
 */
3442
double OSRGetPrimeMeridian(OGRSpatialReferenceH hSRS, char **ppszName)
3443
3444
0
{
3445
0
    VALIDATE_POINTER1(hSRS, "OSRGetPrimeMeridian", 0);
3446
3447
0
    return ToPointer(hSRS)->GetPrimeMeridian(
3448
0
        const_cast<const char **>(ppszName));
3449
0
}
3450
3451
/************************************************************************/
3452
/*                             SetGeogCS()                              */
3453
/************************************************************************/
3454
3455
/**
3456
 * \brief Set geographic coordinate system.
3457
 *
3458
 * This method is used to set the datum, ellipsoid, prime meridian and
3459
 * angular units for a geographic coordinate system.  It can be used on its
3460
 * own to establish a geographic spatial reference, or applied to a
3461
 * projected coordinate system to establish the underlying geographic
3462
 * coordinate system.
3463
 *
3464
 * This method does the same as the C function OSRSetGeogCS().
3465
 *
3466
 * @param pszGeogName user visible name for the geographic coordinate system
3467
 * (not to serve as a key).
3468
 *
3469
 * @param pszDatumName key name for this datum.  The OpenGIS specification
3470
 * lists some known values, and otherwise EPSG datum names with a standard
3471
 * transformation are considered legal keys.
3472
 *
3473
 * @param pszSpheroidName user visible spheroid name (not to serve as a key)
3474
 *
3475
 * @param dfSemiMajor the semi major axis of the spheroid.
3476
 *
3477
 * @param dfInvFlattening the inverse flattening for the spheroid.
3478
 * This can be computed from the semi minor axis as
3479
 * 1/f = 1.0 / (1.0 - semiminor/semimajor).
3480
 *
3481
 * @param pszPMName the name of the prime meridian (not to serve as a key)
3482
 * If this is NULL a default value of "Greenwich" will be used.
3483
 *
3484
 * @param dfPMOffset the longitude of Greenwich relative to this prime
3485
 * meridian. Always in Degrees
3486
 *
3487
 * @param pszAngularUnits the angular units name (see ogr_srs_api.h for some
3488
 * standard names).  If NULL a value of "degrees" will be assumed.
3489
 *
3490
 * @param dfConvertToRadians value to multiply angular units by to transform
3491
 * them to radians.  A value of SRS_UA_DEGREE_CONV will be used if
3492
 * pszAngularUnits is NULL.
3493
 *
3494
 * @return OGRERR_NONE on success.
3495
 */
3496
3497
OGRErr OGRSpatialReference::SetGeogCS(
3498
    const char *pszGeogName, const char *pszDatumName,
3499
    const char *pszSpheroidName, double dfSemiMajor, double dfInvFlattening,
3500
    const char *pszPMName, double dfPMOffset, const char *pszAngularUnits,
3501
    double dfConvertToRadians)
3502
3503
0
{
3504
0
    TAKE_OPTIONAL_LOCK();
3505
3506
0
    d->bNormInfoSet = FALSE;
3507
0
    d->m_osAngularUnits.clear();
3508
0
    d->m_dfAngularUnitToRadian = 0.0;
3509
0
    d->m_osPrimeMeridianName.clear();
3510
0
    d->dfFromGreenwich = 0.0;
3511
3512
    /* -------------------------------------------------------------------- */
3513
    /*      For a geocentric coordinate system we want to set the datum     */
3514
    /*      and ellipsoid based on the GEOGCS.  Create the GEOGCS in a      */
3515
    /*      temporary srs and use the copy method which has special         */
3516
    /*      handling for GEOCCS.                                            */
3517
    /* -------------------------------------------------------------------- */
3518
0
    if (IsGeocentric())
3519
0
    {
3520
0
        OGRSpatialReference oGCS;
3521
3522
0
        oGCS.SetGeogCS(pszGeogName, pszDatumName, pszSpheroidName, dfSemiMajor,
3523
0
                       dfInvFlattening, pszPMName, dfPMOffset, pszAngularUnits,
3524
0
                       dfConvertToRadians);
3525
0
        return CopyGeogCSFrom(&oGCS);
3526
0
    }
3527
3528
0
    auto cs = proj_create_ellipsoidal_2D_cs(
3529
0
        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE, pszAngularUnits,
3530
0
        dfConvertToRadians);
3531
    // Prime meridian expressed in Degree
3532
0
    auto obj = proj_create_geographic_crs(
3533
0
        d->getPROJContext(), pszGeogName, pszDatumName, pszSpheroidName,
3534
0
        dfSemiMajor, dfInvFlattening, pszPMName, dfPMOffset, nullptr, 0.0, cs);
3535
0
    proj_destroy(cs);
3536
3537
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3538
0
        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
3539
0
    {
3540
0
        d->setPjCRS(obj);
3541
0
    }
3542
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3543
0
    {
3544
0
        d->setPjCRS(
3545
0
            proj_crs_alter_geodetic_crs(d->getPROJContext(), d->m_pj_crs, obj));
3546
0
        proj_destroy(obj);
3547
0
    }
3548
0
    else
3549
0
    {
3550
0
        proj_destroy(obj);
3551
0
    }
3552
3553
0
    return OGRERR_NONE;
3554
0
}
3555
3556
/************************************************************************/
3557
/*                            OSRSetGeogCS()                            */
3558
/************************************************************************/
3559
3560
/**
3561
 * \brief Set geographic coordinate system.
3562
 *
3563
 * This function is the same as OGRSpatialReference::SetGeogCS()
3564
 */
3565
OGRErr OSRSetGeogCS(OGRSpatialReferenceH hSRS, const char *pszGeogName,
3566
                    const char *pszDatumName, const char *pszSpheroidName,
3567
                    double dfSemiMajor, double dfInvFlattening,
3568
                    const char *pszPMName, double dfPMOffset,
3569
                    const char *pszAngularUnits, double dfConvertToRadians)
3570
3571
0
{
3572
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeogCS", OGRERR_FAILURE);
3573
3574
0
    return ToPointer(hSRS)->SetGeogCS(pszGeogName, pszDatumName,
3575
0
                                      pszSpheroidName, dfSemiMajor,
3576
0
                                      dfInvFlattening, pszPMName, dfPMOffset,
3577
0
                                      pszAngularUnits, dfConvertToRadians);
3578
0
}
3579
3580
/************************************************************************/
3581
/*                         SetWellKnownGeogCS()                         */
3582
/************************************************************************/
3583
3584
/**
3585
 * \brief Set a GeogCS based on well known name.
3586
 *
3587
 * This may be called on an empty OGRSpatialReference to make a geographic
3588
 * coordinate system, or on something with an existing PROJCS node to
3589
 * set the underlying geographic coordinate system of a projected coordinate
3590
 * system.
3591
 *
3592
 * The following well known text values are currently supported,
3593
 * Except for "EPSG:n", the others are without dependency on EPSG data files:
3594
 * <ul>
3595
 * <li> "EPSG:n": where n is the code a Geographic coordinate reference system.
3596
 * <li> "WGS84": same as "EPSG:4326" (axis order lat/long).
3597
 * <li> "WGS72": same as "EPSG:4322" (axis order lat/long).
3598
 * <li> "NAD83": same as "EPSG:4269" (axis order lat/long).
3599
 * <li> "NAD27": same as "EPSG:4267" (axis order lat/long).
3600
 * <li> "CRS84", "CRS:84": same as "WGS84" but with axis order long/lat.
3601
 * <li> "CRS72", "CRS:72": same as "WGS72" but with axis order long/lat.
3602
 * <li> "CRS27", "CRS:27": same as "NAD27" but with axis order long/lat.
3603
 * </ul>
3604
 *
3605
 * @param pszName name of well known geographic coordinate system.
3606
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if the name isn't
3607
 * recognised, the target object is already initialized, or an EPSG value
3608
 * can't be successfully looked up.
3609
 */
3610
3611
OGRErr OGRSpatialReference::SetWellKnownGeogCS(const char *pszName)
3612
3613
0
{
3614
0
    TAKE_OPTIONAL_LOCK();
3615
3616
    /* -------------------------------------------------------------------- */
3617
    /*      Check for EPSG authority numbers.                               */
3618
    /* -------------------------------------------------------------------- */
3619
0
    if (STARTS_WITH_CI(pszName, "EPSG:") || STARTS_WITH_CI(pszName, "EPSGA:"))
3620
0
    {
3621
0
        OGRSpatialReference oSRS2;
3622
0
        const OGRErr eErr = oSRS2.importFromEPSG(atoi(pszName + 5));
3623
0
        if (eErr != OGRERR_NONE)
3624
0
            return eErr;
3625
3626
0
        if (!oSRS2.IsGeographic())
3627
0
            return OGRERR_FAILURE;
3628
3629
0
        return CopyGeogCSFrom(&oSRS2);
3630
0
    }
3631
3632
    /* -------------------------------------------------------------------- */
3633
    /*      Check for simple names.                                         */
3634
    /* -------------------------------------------------------------------- */
3635
0
    const char *pszWKT = nullptr;
3636
3637
0
    if (EQUAL(pszName, "WGS84"))
3638
0
    {
3639
0
        pszWKT = SRS_WKT_WGS84_LAT_LONG;
3640
0
    }
3641
0
    else if (EQUAL(pszName, "CRS84") || EQUAL(pszName, "CRS:84"))
3642
0
    {
3643
0
        pszWKT =
3644
0
            "GEOGCRS[\"WGS 84 (CRS84)\",DATUM[\"World Geodetic System 1984\","
3645
0
            "ELLIPSOID[\"WGS "
3646
0
            "84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],"
3647
0
            "PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],"
3648
0
            "CS[ellipsoidal,2],AXIS[\"geodetic longitude (Lon)\",east,ORDER[1],"
3649
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3650
0
            "AXIS[\"geodetic latitude (Lat)\",north,ORDER[2],"
3651
0
            "ANGLEUNIT[\"degree\",0.0174532925199433]],"
3652
0
            "USAGE[SCOPE[\"unknown\"],AREA[\"World\"],BBOX[-90,-180,90,180]],"
3653
0
            "ID[\"OGC\",\"CRS84\"]]";
3654
0
    }
3655
0
    else if (EQUAL(pszName, "WGS72"))
3656
0
        pszWKT =
3657
0
            "GEOGCS[\"WGS 72\",DATUM[\"WGS_1972\","
3658
0
            "SPHEROID[\"WGS 72\",6378135,298.26,AUTHORITY[\"EPSG\",\"7043\"]],"
3659
0
            "AUTHORITY[\"EPSG\",\"6322\"]],"
3660
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3661
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3662
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3663
0
            "AUTHORITY[\"EPSG\",\"4322\"]]";
3664
3665
0
    else if (EQUAL(pszName, "NAD27"))
3666
0
        pszWKT =
3667
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3668
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3669
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3670
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3671
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3672
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],"
3673
0
            "AUTHORITY[\"EPSG\",\"4267\"]]";
3674
3675
0
    else if (EQUAL(pszName, "CRS27") || EQUAL(pszName, "CRS:27"))
3676
0
        pszWKT =
3677
0
            "GEOGCS[\"NAD27\",DATUM[\"North_American_Datum_1927\","
3678
0
            "SPHEROID[\"Clarke 1866\",6378206.4,294.9786982138982,"
3679
0
            "AUTHORITY[\"EPSG\",\"7008\"]],AUTHORITY[\"EPSG\",\"6267\"]],"
3680
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3681
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3682
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3683
3684
0
    else if (EQUAL(pszName, "NAD83"))
3685
0
        pszWKT =
3686
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3687
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3688
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3689
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3690
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3691
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3692
0
            "AXIS[\"Latitude\",NORTH],AXIS[\"Longitude\",EAST],AUTHORITY["
3693
0
            "\"EPSG\",\"4269\"]]";
3694
3695
0
    else if (EQUAL(pszName, "CRS83") || EQUAL(pszName, "CRS:83"))
3696
0
        pszWKT =
3697
0
            "GEOGCS[\"NAD83\",DATUM[\"North_American_Datum_1983\","
3698
0
            "SPHEROID[\"GRS 1980\",6378137,298.257222101,"
3699
0
            "AUTHORITY[\"EPSG\",\"7019\"]],"
3700
0
            "AUTHORITY[\"EPSG\",\"6269\"]],"
3701
0
            "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
3702
0
            "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\",\"9122\"]],"
3703
0
            "AXIS[\"Longitude\",EAST],AXIS[\"Latitude\",NORTH]]";
3704
3705
0
    else
3706
0
        return OGRERR_FAILURE;
3707
3708
    /* -------------------------------------------------------------------- */
3709
    /*      Import the WKT                                                  */
3710
    /* -------------------------------------------------------------------- */
3711
0
    OGRSpatialReference oSRS2;
3712
0
    const OGRErr eErr = oSRS2.importFromWkt(pszWKT);
3713
0
    if (eErr != OGRERR_NONE)
3714
0
        return eErr;
3715
3716
    /* -------------------------------------------------------------------- */
3717
    /*      Copy over.                                                      */
3718
    /* -------------------------------------------------------------------- */
3719
0
    return CopyGeogCSFrom(&oSRS2);
3720
0
}
3721
3722
/************************************************************************/
3723
/*                       OSRSetWellKnownGeogCS()                        */
3724
/************************************************************************/
3725
3726
/**
3727
 * \brief Set a GeogCS based on well known name.
3728
 *
3729
 * This function is the same as OGRSpatialReference::SetWellKnownGeogCS()
3730
 */
3731
OGRErr OSRSetWellKnownGeogCS(OGRSpatialReferenceH hSRS, const char *pszName)
3732
3733
0
{
3734
0
    VALIDATE_POINTER1(hSRS, "OSRSetWellKnownGeogCS", OGRERR_FAILURE);
3735
3736
0
    return ToPointer(hSRS)->SetWellKnownGeogCS(pszName);
3737
0
}
3738
3739
/************************************************************************/
3740
/*                           CopyGeogCSFrom()                           */
3741
/************************************************************************/
3742
3743
/**
3744
 * \brief Copy GEOGCS from another OGRSpatialReference.
3745
 *
3746
 * The GEOGCS information is copied into this OGRSpatialReference from another.
3747
 * If this object has a PROJCS root already, the GEOGCS is installed within
3748
 * it, otherwise it is installed as the root.
3749
 *
3750
 * @param poSrcSRS the spatial reference to copy the GEOGCS information from.
3751
 *
3752
 * @return OGRERR_NONE on success or an error code.
3753
 */
3754
3755
OGRErr OGRSpatialReference::CopyGeogCSFrom(const OGRSpatialReference *poSrcSRS)
3756
3757
0
{
3758
0
    TAKE_OPTIONAL_LOCK();
3759
3760
0
    d->bNormInfoSet = FALSE;
3761
0
    d->m_osAngularUnits.clear();
3762
0
    d->m_dfAngularUnitToRadian = 0.0;
3763
0
    d->m_osPrimeMeridianName.clear();
3764
0
    d->dfFromGreenwich = 0.0;
3765
3766
0
    d->refreshProjObj();
3767
0
    poSrcSRS->d->refreshProjObj();
3768
0
    if (!poSrcSRS->d->m_pj_crs)
3769
0
    {
3770
0
        return OGRERR_FAILURE;
3771
0
    }
3772
0
    auto geodCRS =
3773
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3774
0
    if (!geodCRS)
3775
0
    {
3776
0
        return OGRERR_FAILURE;
3777
0
    }
3778
3779
    /* -------------------------------------------------------------------- */
3780
    /*      Handle geocentric coordinate systems specially.  We just        */
3781
    /*      want to copy the DATUM.                                         */
3782
    /* -------------------------------------------------------------------- */
3783
0
    if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
3784
0
    {
3785
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
3786
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
3787
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
3788
0
        if (datum == nullptr)
3789
0
        {
3790
0
            datum = proj_crs_get_datum_ensemble(d->getPROJContext(), geodCRS);
3791
0
        }
3792
0
#endif
3793
0
        if (datum == nullptr)
3794
0
        {
3795
0
            proj_destroy(geodCRS);
3796
0
            return OGRERR_FAILURE;
3797
0
        }
3798
3799
0
        const char *pszUnitName = nullptr;
3800
0
        double unitConvFactor = GetLinearUnits(&pszUnitName);
3801
3802
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
3803
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, pszUnitName,
3804
0
            unitConvFactor);
3805
0
        proj_destroy(datum);
3806
3807
0
        d->setPjCRS(pj_crs);
3808
0
    }
3809
3810
0
    else if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
3811
0
    {
3812
0
        auto pj_crs = proj_crs_alter_geodetic_crs(d->getPROJContext(),
3813
0
                                                  d->m_pj_crs, geodCRS);
3814
0
        d->setPjCRS(pj_crs);
3815
0
    }
3816
3817
0
    else
3818
0
    {
3819
0
        d->setPjCRS(proj_clone(d->getPROJContext(), geodCRS));
3820
0
    }
3821
3822
    // Apply TOWGS84 of source CRS
3823
0
    if (poSrcSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
3824
0
    {
3825
0
        auto target =
3826
0
            proj_get_target_crs(d->getPROJContext(), poSrcSRS->d->m_pj_crs);
3827
0
        auto co = proj_crs_get_coordoperation(d->getPROJContext(),
3828
0
                                              poSrcSRS->d->m_pj_crs);
3829
0
        d->setPjCRS(proj_crs_create_bound_crs(d->getPROJContext(), d->m_pj_crs,
3830
0
                                              target, co));
3831
0
        proj_destroy(target);
3832
0
        proj_destroy(co);
3833
0
    }
3834
3835
0
    proj_destroy(geodCRS);
3836
3837
0
    return OGRERR_NONE;
3838
0
}
3839
3840
/************************************************************************/
3841
/*                         OSRCopyGeogCSFrom()                          */
3842
/************************************************************************/
3843
3844
/**
3845
 * \brief Copy GEOGCS from another OGRSpatialReference.
3846
 *
3847
 * This function is the same as OGRSpatialReference::CopyGeogCSFrom()
3848
 */
3849
OGRErr OSRCopyGeogCSFrom(OGRSpatialReferenceH hSRS,
3850
                         const OGRSpatialReferenceH hSrcSRS)
3851
3852
0
{
3853
0
    VALIDATE_POINTER1(hSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3854
0
    VALIDATE_POINTER1(hSrcSRS, "OSRCopyGeogCSFrom", OGRERR_FAILURE);
3855
3856
0
    return ToPointer(hSRS)->CopyGeogCSFrom(ToPointer(hSrcSRS));
3857
0
}
3858
3859
/************************************************************************/
3860
/*                   SET_FROM_USER_INPUT_LIMITATIONS_get()              */
3861
/************************************************************************/
3862
3863
/** Limitations for OGRSpatialReference::SetFromUserInput().
3864
 *
3865
 * Currently ALLOW_NETWORK_ACCESS=NO and ALLOW_FILE_ACCESS=NO.
3866
 */
3867
const char *const OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS[] = {
3868
    "ALLOW_NETWORK_ACCESS=NO", "ALLOW_FILE_ACCESS=NO", nullptr};
3869
3870
/**
3871
 * \brief Return OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS
3872
 */
3873
CSLConstList OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get()
3874
0
{
3875
0
    return SET_FROM_USER_INPUT_LIMITATIONS;
3876
0
}
3877
3878
/************************************************************************/
3879
/*                      RemoveIDFromMemberOfEnsembles()                 */
3880
/************************************************************************/
3881
3882
// cppcheck-suppress constParameterReference
3883
static void RemoveIDFromMemberOfEnsembles(CPLJSONObject &obj)
3884
0
{
3885
    // Remove "id" from members of datum ensembles for compatibility with
3886
    // older PROJ versions
3887
    // Cf https://github.com/opengeospatial/geoparquet/discussions/110
3888
    // and https://github.com/OSGeo/PROJ/pull/3221
3889
0
    if (obj.GetType() == CPLJSONObject::Type::Object)
3890
0
    {
3891
0
        for (auto &subObj : obj.GetChildren())
3892
0
        {
3893
0
            RemoveIDFromMemberOfEnsembles(subObj);
3894
0
        }
3895
0
    }
3896
0
    else if (obj.GetType() == CPLJSONObject::Type::Array &&
3897
0
             obj.GetName() == "members")
3898
0
    {
3899
0
        for (auto &subObj : obj.ToArray())
3900
0
        {
3901
0
            if (subObj.GetType() == CPLJSONObject::Type::Object)
3902
0
            {
3903
0
                subObj.Delete("id");
3904
0
            }
3905
0
        }
3906
0
    }
3907
0
}
3908
3909
/************************************************************************/
3910
/*                          SetFromUserInput()                          */
3911
/************************************************************************/
3912
3913
/**
3914
 * \brief Set spatial reference from various text formats.
3915
 *
3916
 * This method will examine the provided input, and try to deduce the
3917
 * format, and then use it to initialize the spatial reference system.  It
3918
 * may take the following forms:
3919
 *
3920
 * <ol>
3921
 * <li> Well Known Text definition - passed on to importFromWkt().
3922
 * <li> "EPSG:n" - number passed on to importFromEPSG().
3923
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3924
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3925
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3926
 * <li> PROJ.4 definitions - passed on to importFromProj4().
3927
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3928
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3929
 * WGS84 or WGS72.
3930
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3931
 * <li> PROJJSON (PROJ &gt;= 6.2)
3932
 * </ol>
3933
 *
3934
 * It is expected that this method will be extended in the future to support
3935
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3936
 * State Plane definitions.
3937
 *
3938
 * This method is intended to be flexible, but by its nature it is
3939
 * imprecise as it must guess information about the format intended.  When
3940
 * possible applications should call the specific method appropriate if the
3941
 * input is known to be in a particular format.
3942
 *
3943
 * This method does the same thing as the OSRSetFromUserInput() function.
3944
 *
3945
 * @param pszDefinition text definition to try to deduce SRS from.
3946
 *
3947
 * @return OGRERR_NONE on success, or an error code if the name isn't
3948
 * recognised, the definition is corrupt, or an EPSG value can't be
3949
 * successfully looked up.
3950
 */
3951
3952
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition)
3953
0
{
3954
0
    return SetFromUserInput(pszDefinition, nullptr);
3955
0
}
3956
3957
/**
3958
 * \brief Set spatial reference from various text formats.
3959
 *
3960
 * This method will examine the provided input, and try to deduce the
3961
 * format, and then use it to initialize the spatial reference system.  It
3962
 * may take the following forms:
3963
 *
3964
 * <ol>
3965
 * <li> Well Known Text definition - passed on to importFromWkt().
3966
 * <li> "EPSG:n" - number passed on to importFromEPSG().
3967
 * <li> "EPSGA:n" - number passed on to importFromEPSGA().
3968
 * <li> "AUTO:proj_id,unit_id,lon0,lat0" - WMS auto projections.
3969
 * <li> "urn:ogc:def:crs:EPSG::n" - ogc urns
3970
 * <li> PROJ.4 definitions - passed on to importFromProj4().
3971
 * <li> filename - file read for WKT, XML or PROJ.4 definition.
3972
 * <li> well known name accepted by SetWellKnownGeogCS(), such as NAD27, NAD83,
3973
 * WGS84 or WGS72.
3974
 * <li> "IGNF:xxxx", "ESRI:xxxx", etc. from definitions from the PROJ database;
3975
 * <li> PROJJSON (PROJ &gt;= 6.2)
3976
 * </ol>
3977
 *
3978
 * It is expected that this method will be extended in the future to support
3979
 * XML and perhaps a simplified "minilanguage" for indicating common UTM and
3980
 * State Plane definitions.
3981
 *
3982
 * This method is intended to be flexible, but by its nature it is
3983
 * imprecise as it must guess information about the format intended.  When
3984
 * possible applications should call the specific method appropriate if the
3985
 * input is known to be in a particular format.
3986
 *
3987
 * This method does the same thing as the OSRSetFromUserInput() and
3988
 * OSRSetFromUserInputEx() functions.
3989
 *
3990
 * @param pszDefinition text definition to try to deduce SRS from.
3991
 *
3992
 * @param papszOptions NULL terminated list of options, or NULL.
3993
 * <ol>
3994
 * <li> ALLOW_NETWORK_ACCESS=YES/NO.
3995
 *      Whether http:// or https:// access is allowed. Defaults to YES.
3996
 * <li> ALLOW_FILE_ACCESS=YES/NO.
3997
 *      Whether reading a file using the Virtual File System layer is allowed
3998
 *      (can also involve network access). Defaults to YES.
3999
 * </ol>
4000
 *
4001
 * @return OGRERR_NONE on success, or an error code if the name isn't
4002
 * recognised, the definition is corrupt, or an EPSG value can't be
4003
 * successfully looked up.
4004
 */
4005
4006
OGRErr OGRSpatialReference::SetFromUserInput(const char *pszDefinition,
4007
                                             CSLConstList papszOptions)
4008
0
{
4009
0
    TAKE_OPTIONAL_LOCK();
4010
4011
    // Skip leading white space
4012
0
    while (isspace(static_cast<unsigned char>(*pszDefinition)))
4013
0
        pszDefinition++;
4014
4015
0
    if (STARTS_WITH_CI(pszDefinition, "ESRI::"))
4016
0
    {
4017
0
        pszDefinition += 6;
4018
0
    }
4019
4020
    /* -------------------------------------------------------------------- */
4021
    /*      Is it a recognised syntax?                                      */
4022
    /* -------------------------------------------------------------------- */
4023
0
    const char *const wktKeywords[] = {
4024
        // WKT1
4025
0
        "GEOGCS", "GEOCCS", "PROJCS", "VERT_CS", "COMPD_CS", "LOCAL_CS",
4026
        // WKT2"
4027
0
        "GEODCRS", "GEOGCRS", "GEODETICCRS", "GEOGRAPHICCRS", "PROJCRS",
4028
0
        "PROJECTEDCRS", "VERTCRS", "VERTICALCRS", "COMPOUNDCRS", "ENGCRS",
4029
0
        "ENGINEERINGCRS", "BOUNDCRS", "DERIVEDPROJCRS", "COORDINATEMETADATA"};
4030
0
    for (const char *keyword : wktKeywords)
4031
0
    {
4032
0
        if (STARTS_WITH_CI(pszDefinition, keyword))
4033
0
        {
4034
0
            return importFromWkt(pszDefinition);
4035
0
        }
4036
0
    }
4037
4038
0
    const bool bStartsWithEPSG = STARTS_WITH_CI(pszDefinition, "EPSG:");
4039
0
    if (bStartsWithEPSG || STARTS_WITH_CI(pszDefinition, "EPSGA:"))
4040
0
    {
4041
0
        OGRErr eStatus = OGRERR_NONE;
4042
4043
0
        if (strchr(pszDefinition, '+') || strchr(pszDefinition, '@'))
4044
0
        {
4045
            // Use proj_create() as it allows things like EPSG:3157+4617
4046
            // that are not normally supported by the below code that
4047
            // builds manually a compound CRS
4048
0
            PJ *pj = proj_create(d->getPROJContext(), pszDefinition);
4049
0
            if (!pj)
4050
0
            {
4051
0
                return OGRERR_FAILURE;
4052
0
            }
4053
0
            Clear();
4054
0
            d->setPjCRS(pj);
4055
0
            return OGRERR_NONE;
4056
0
        }
4057
0
        else
4058
0
        {
4059
0
            eStatus =
4060
0
                importFromEPSG(atoi(pszDefinition + (bStartsWithEPSG ? 5 : 6)));
4061
0
        }
4062
4063
0
        return eStatus;
4064
0
    }
4065
4066
0
    if (STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs:") ||
4067
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:crs,crs:") ||
4068
0
        STARTS_WITH_CI(pszDefinition, "urn:x-ogc:def:crs:") ||
4069
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:crs:") ||
4070
0
        STARTS_WITH_CI(pszDefinition, "urn:opengis:def:crs:") ||
4071
0
        STARTS_WITH_CI(pszDefinition, "urn:ogc:def:coordinateMetadata:"))
4072
0
        return importFromURN(pszDefinition);
4073
4074
0
    if (STARTS_WITH_CI(pszDefinition, "http://opengis.net/def/crs") ||
4075
0
        STARTS_WITH_CI(pszDefinition, "https://opengis.net/def/crs") ||
4076
0
        STARTS_WITH_CI(pszDefinition, "http://www.opengis.net/def/crs") ||
4077
0
        STARTS_WITH_CI(pszDefinition, "https://www.opengis.net/def/crs") ||
4078
0
        STARTS_WITH_CI(pszDefinition, "www.opengis.net/def/crs"))
4079
0
        return importFromCRSURL(pszDefinition);
4080
4081
0
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4082
0
        return importFromWMSAUTO(pszDefinition);
4083
4084
    // WMS/WCS OGC codes like OGC:CRS84.
4085
0
    if (EQUAL(pszDefinition, "OGC:CRS84"))
4086
0
        return SetWellKnownGeogCS(pszDefinition + 4);
4087
4088
0
    if (STARTS_WITH_CI(pszDefinition, "CRS:"))
4089
0
        return SetWellKnownGeogCS(pszDefinition);
4090
4091
0
    if (STARTS_WITH_CI(pszDefinition, "DICT:") && strstr(pszDefinition, ","))
4092
0
    {
4093
0
        char *pszFile = CPLStrdup(pszDefinition + 5);
4094
0
        char *pszCode = strstr(pszFile, ",") + 1;
4095
4096
0
        pszCode[-1] = '\0';
4097
4098
0
        OGRErr err = importFromDict(pszFile, pszCode);
4099
0
        CPLFree(pszFile);
4100
4101
0
        return err;
4102
0
    }
4103
4104
0
    if (EQUAL(pszDefinition, "NAD27") || EQUAL(pszDefinition, "NAD83") ||
4105
0
        EQUAL(pszDefinition, "WGS84") || EQUAL(pszDefinition, "WGS72"))
4106
0
    {
4107
0
        Clear();
4108
0
        return SetWellKnownGeogCS(pszDefinition);
4109
0
    }
4110
4111
    // PROJJSON
4112
0
    if (pszDefinition[0] == '{' && strstr(pszDefinition, "\"type\"") &&
4113
0
        (strstr(pszDefinition, "GeodeticCRS") ||
4114
0
         strstr(pszDefinition, "GeographicCRS") ||
4115
0
         strstr(pszDefinition, "ProjectedCRS") ||
4116
0
         strstr(pszDefinition, "VerticalCRS") ||
4117
0
         strstr(pszDefinition, "BoundCRS") ||
4118
0
         strstr(pszDefinition, "CompoundCRS") ||
4119
0
         strstr(pszDefinition, "DerivedGeodeticCRS") ||
4120
0
         strstr(pszDefinition, "DerivedGeographicCRS") ||
4121
0
         strstr(pszDefinition, "DerivedProjectedCRS") ||
4122
0
         strstr(pszDefinition, "DerivedVerticalCRS") ||
4123
0
         strstr(pszDefinition, "EngineeringCRS") ||
4124
0
         strstr(pszDefinition, "DerivedEngineeringCRS") ||
4125
0
         strstr(pszDefinition, "ParametricCRS") ||
4126
0
         strstr(pszDefinition, "DerivedParametricCRS") ||
4127
0
         strstr(pszDefinition, "TemporalCRS") ||
4128
0
         strstr(pszDefinition, "DerivedTemporalCRS")))
4129
0
    {
4130
0
        PJ *pj;
4131
0
        if (strstr(pszDefinition, "datum_ensemble") != nullptr)
4132
0
        {
4133
            // PROJ < 9.0.1 doesn't like a datum_ensemble whose member have
4134
            // a unknown id.
4135
0
            CPLJSONDocument oCRSDoc;
4136
0
            if (!oCRSDoc.LoadMemory(pszDefinition))
4137
0
                return OGRERR_CORRUPT_DATA;
4138
0
            CPLJSONObject oCRSRoot = oCRSDoc.GetRoot();
4139
0
            RemoveIDFromMemberOfEnsembles(oCRSRoot);
4140
0
            pj = proj_create(d->getPROJContext(), oCRSRoot.ToString().c_str());
4141
0
        }
4142
0
        else
4143
0
        {
4144
0
            pj = proj_create(d->getPROJContext(), pszDefinition);
4145
0
        }
4146
0
        if (!pj)
4147
0
        {
4148
0
            return OGRERR_FAILURE;
4149
0
        }
4150
0
        Clear();
4151
0
        d->setPjCRS(pj);
4152
0
        return OGRERR_NONE;
4153
0
    }
4154
4155
0
    if (strstr(pszDefinition, "+proj") != nullptr ||
4156
0
        strstr(pszDefinition, "+init") != nullptr)
4157
0
        return importFromProj4(pszDefinition);
4158
4159
0
    if (STARTS_WITH_CI(pszDefinition, "http://") ||
4160
0
        STARTS_WITH_CI(pszDefinition, "https://"))
4161
0
    {
4162
0
        if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
4163
0
                                             "ALLOW_NETWORK_ACCESS", "YES")))
4164
0
            return importFromUrl(pszDefinition);
4165
4166
0
        CPLError(CE_Failure, CPLE_AppDefined,
4167
0
                 "Cannot import %s due to ALLOW_NETWORK_ACCESS=NO",
4168
0
                 pszDefinition);
4169
0
        return OGRERR_FAILURE;
4170
0
    }
4171
4172
0
    if (EQUAL(pszDefinition, "osgb:BNG"))
4173
0
    {
4174
0
        return importFromEPSG(27700);
4175
0
    }
4176
4177
    // Used by German CityGML files
4178
0
    if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH"))
4179
0
    {
4180
        // "ETRS89 / UTM Zone 32N + DHHN92 height"
4181
0
        return SetFromUserInput("EPSG:25832+5783");
4182
0
    }
4183
0
    else if (EQUAL(pszDefinition, "urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH"))
4184
0
    {
4185
        // "ETRS89 / UTM Zone 32N + DHHN2016 height"
4186
0
        return SetFromUserInput("EPSG:25832+7837");
4187
0
    }
4188
4189
    // Deal with IGNF:xxx, ESRI:xxx, etc from the PROJ database
4190
0
    const char *pszDot = strrchr(pszDefinition, ':');
4191
0
    if (pszDot)
4192
0
    {
4193
0
        CPLString osPrefix(pszDefinition, pszDot - pszDefinition);
4194
0
        auto authorities =
4195
0
            proj_get_authorities_from_database(d->getPROJContext());
4196
0
        if (authorities)
4197
0
        {
4198
0
            std::set<std::string> aosCandidateAuthorities;
4199
0
            for (auto iter = authorities; *iter; ++iter)
4200
0
            {
4201
0
                if (*iter == osPrefix)
4202
0
                {
4203
0
                    aosCandidateAuthorities.clear();
4204
0
                    aosCandidateAuthorities.insert(*iter);
4205
0
                    break;
4206
0
                }
4207
                // Deal with "IAU_2015" as authority in the list and input
4208
                // "IAU:code"
4209
0
                else if (strncmp(*iter, osPrefix.c_str(), osPrefix.size()) ==
4210
0
                             0 &&
4211
0
                         (*iter)[osPrefix.size()] == '_')
4212
0
                {
4213
0
                    aosCandidateAuthorities.insert(*iter);
4214
0
                }
4215
                // Deal with "IAU_2015" as authority in the list and input
4216
                // "IAU:2015:code"
4217
0
                else if (osPrefix.find(':') != std::string::npos &&
4218
0
                         osPrefix.size() == strlen(*iter) &&
4219
0
                         CPLString(osPrefix).replaceAll(':', '_') == *iter)
4220
0
                {
4221
0
                    aosCandidateAuthorities.clear();
4222
0
                    aosCandidateAuthorities.insert(*iter);
4223
0
                    break;
4224
0
                }
4225
0
            }
4226
4227
0
            proj_string_list_destroy(authorities);
4228
4229
0
            if (!aosCandidateAuthorities.empty())
4230
0
            {
4231
0
                auto obj = proj_create_from_database(
4232
0
                    d->getPROJContext(),
4233
0
                    aosCandidateAuthorities.rbegin()->c_str(), pszDot + 1,
4234
0
                    PJ_CATEGORY_CRS, false, nullptr);
4235
0
                if (!obj)
4236
0
                {
4237
0
                    return OGRERR_FAILURE;
4238
0
                }
4239
0
                Clear();
4240
0
                d->setPjCRS(obj);
4241
0
                return OGRERR_NONE;
4242
0
            }
4243
0
        }
4244
0
    }
4245
4246
    /* -------------------------------------------------------------------- */
4247
    /*      Try to open it as a file.                                       */
4248
    /* -------------------------------------------------------------------- */
4249
0
    if (!CPLTestBool(
4250
0
            CSLFetchNameValueDef(papszOptions, "ALLOW_FILE_ACCESS", "YES")))
4251
0
    {
4252
0
        VSIStatBufL sStat;
4253
0
        if (STARTS_WITH(pszDefinition, "/vsi") ||
4254
0
            VSIStatExL(pszDefinition, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
4255
0
        {
4256
0
            CPLError(CE_Failure, CPLE_AppDefined,
4257
0
                     "Cannot import %s due to ALLOW_FILE_ACCESS=NO",
4258
0
                     pszDefinition);
4259
0
            return OGRERR_FAILURE;
4260
0
        }
4261
        // We used to silently return an error without a CE_Failure message
4262
        // Cf https://github.com/Toblerity/Fiona/issues/1063
4263
0
        return OGRERR_CORRUPT_DATA;
4264
0
    }
4265
4266
0
    CPLConfigOptionSetter oSetter("CPL_ALLOW_VSISTDIN", "NO", true);
4267
0
    VSILFILE *const fp = VSIFOpenL(pszDefinition, "rt");
4268
0
    if (fp == nullptr)
4269
0
        return OGRERR_CORRUPT_DATA;
4270
4271
0
    const size_t nBufMax = 100000;
4272
0
    char *const pszBuffer = static_cast<char *>(CPLMalloc(nBufMax));
4273
0
    const size_t nBytes = VSIFReadL(pszBuffer, 1, nBufMax - 1, fp);
4274
0
    VSIFCloseL(fp);
4275
4276
0
    if (nBytes == nBufMax - 1)
4277
0
    {
4278
0
        CPLDebug("OGR",
4279
0
                 "OGRSpatialReference::SetFromUserInput(%s), opened file "
4280
0
                 "but it is to large for our generous buffer.  Is it really "
4281
0
                 "just a WKT definition?",
4282
0
                 pszDefinition);
4283
0
        CPLFree(pszBuffer);
4284
0
        return OGRERR_FAILURE;
4285
0
    }
4286
4287
0
    pszBuffer[nBytes] = '\0';
4288
4289
0
    char *pszBufPtr = pszBuffer;
4290
0
    while (pszBufPtr[0] == ' ' || pszBufPtr[0] == '\n')
4291
0
        pszBufPtr++;
4292
4293
0
    OGRErr err = OGRERR_NONE;
4294
0
    if (pszBufPtr[0] == '<')
4295
0
        err = importFromXML(pszBufPtr);
4296
0
    else if ((strstr(pszBuffer, "+proj") != nullptr ||
4297
0
              strstr(pszBuffer, "+init") != nullptr) &&
4298
0
             (strstr(pszBuffer, "EXTENSION") == nullptr &&
4299
0
              strstr(pszBuffer, "extension") == nullptr))
4300
0
        err = importFromProj4(pszBufPtr);
4301
0
    else
4302
0
    {
4303
0
        if (STARTS_WITH_CI(pszBufPtr, "ESRI::"))
4304
0
        {
4305
0
            pszBufPtr += 6;
4306
0
        }
4307
4308
        // coverity[tainted_data]
4309
0
        err = importFromWkt(pszBufPtr);
4310
0
    }
4311
4312
0
    CPLFree(pszBuffer);
4313
4314
0
    return err;
4315
0
}
4316
4317
/************************************************************************/
4318
/*                        OSRSetFromUserInput()                         */
4319
/************************************************************************/
4320
4321
/**
4322
 * \brief Set spatial reference from various text formats.
4323
 *
4324
 * This function is the same as OGRSpatialReference::SetFromUserInput()
4325
 *
4326
 * \see OSRSetFromUserInputEx() for a variant allowing to pass options.
4327
 */
4328
OGRErr CPL_STDCALL OSRSetFromUserInput(OGRSpatialReferenceH hSRS,
4329
                                       const char *pszDef)
4330
4331
0
{
4332
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInput", OGRERR_FAILURE);
4333
4334
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef);
4335
0
}
4336
4337
/************************************************************************/
4338
/*                       OSRSetFromUserInputEx()                        */
4339
/************************************************************************/
4340
4341
/**
4342
 * \brief Set spatial reference from various text formats.
4343
 *
4344
 * This function is the same as OGRSpatialReference::SetFromUserInput().
4345
 *
4346
 * @since GDAL 3.9
4347
 */
4348
OGRErr OSRSetFromUserInputEx(OGRSpatialReferenceH hSRS, const char *pszDef,
4349
                             CSLConstList papszOptions)
4350
4351
0
{
4352
0
    VALIDATE_POINTER1(hSRS, "OSRSetFromUserInputEx", OGRERR_FAILURE);
4353
4354
0
    return ToPointer(hSRS)->SetFromUserInput(pszDef, papszOptions);
4355
0
}
4356
4357
/************************************************************************/
4358
/*                          ImportFromUrl()                             */
4359
/************************************************************************/
4360
4361
/**
4362
 * \brief Set spatial reference from a URL.
4363
 *
4364
 * This method will download the spatial reference at a given URL and
4365
 * feed it into SetFromUserInput for you.
4366
 *
4367
 * This method does the same thing as the OSRImportFromUrl() function.
4368
 *
4369
 * @param pszUrl text definition to try to deduce SRS from.
4370
 *
4371
 * @return OGRERR_NONE on success, or an error code with the curl
4372
 * error message if it is unable to download data.
4373
 */
4374
4375
OGRErr OGRSpatialReference::importFromUrl(const char *pszUrl)
4376
4377
0
{
4378
0
    TAKE_OPTIONAL_LOCK();
4379
4380
0
    if (!STARTS_WITH_CI(pszUrl, "http://") &&
4381
0
        !STARTS_WITH_CI(pszUrl, "https://"))
4382
0
    {
4383
0
        CPLError(CE_Failure, CPLE_AppDefined,
4384
0
                 "The given string is not recognized as a URL"
4385
0
                 "starting with 'http://' -- %s",
4386
0
                 pszUrl);
4387
0
        return OGRERR_FAILURE;
4388
0
    }
4389
4390
    /* -------------------------------------------------------------------- */
4391
    /*      Fetch the result.                                               */
4392
    /* -------------------------------------------------------------------- */
4393
0
    CPLErrorReset();
4394
4395
0
    std::string osUrl(pszUrl);
4396
    // We have historically supported "http://spatialreference.org/ref/AUTHNAME/CODE/"
4397
    // as a valid URL since we used a "Accept: application/x-ogcwkt" header
4398
    // to query WKT. To allow a static server to be used, rather append a
4399
    // "ogcwkt/" suffix.
4400
0
    for (const char *pszPrefix : {"https://spatialreference.org/ref/",
4401
0
                                  "http://spatialreference.org/ref/"})
4402
0
    {
4403
0
        if (STARTS_WITH(pszUrl, pszPrefix))
4404
0
        {
4405
0
            const CPLStringList aosTokens(
4406
0
                CSLTokenizeString2(pszUrl + strlen(pszPrefix), "/", 0));
4407
0
            if (aosTokens.size() == 2)
4408
0
            {
4409
0
                osUrl = "https://spatialreference.org/ref/";
4410
0
                osUrl += aosTokens[0];  // authority
4411
0
                osUrl += '/';
4412
0
                osUrl += aosTokens[1];  // code
4413
0
                osUrl += "/ogcwkt/";
4414
0
            }
4415
0
            break;
4416
0
        }
4417
0
    }
4418
4419
0
    const char *pszTimeout = "TIMEOUT=10";
4420
0
    char *apszOptions[] = {const_cast<char *>(pszTimeout), nullptr};
4421
4422
0
    CPLHTTPResult *psResult = CPLHTTPFetch(osUrl.c_str(), apszOptions);
4423
4424
    /* -------------------------------------------------------------------- */
4425
    /*      Try to handle errors.                                           */
4426
    /* -------------------------------------------------------------------- */
4427
4428
0
    if (psResult == nullptr)
4429
0
        return OGRERR_FAILURE;
4430
0
    if (psResult->nDataLen == 0 || CPLGetLastErrorNo() != 0 ||
4431
0
        psResult->pabyData == nullptr)
4432
0
    {
4433
0
        if (CPLGetLastErrorNo() == 0)
4434
0
        {
4435
0
            CPLError(CE_Failure, CPLE_AppDefined,
4436
0
                     "No data was returned from the given URL");
4437
0
        }
4438
0
        CPLHTTPDestroyResult(psResult);
4439
0
        return OGRERR_FAILURE;
4440
0
    }
4441
4442
0
    if (psResult->nStatus != 0)
4443
0
    {
4444
0
        CPLError(CE_Failure, CPLE_AppDefined, "Curl reports error: %d: %s",
4445
0
                 psResult->nStatus, psResult->pszErrBuf);
4446
0
        CPLHTTPDestroyResult(psResult);
4447
0
        return OGRERR_FAILURE;
4448
0
    }
4449
4450
0
    const char *pszData = reinterpret_cast<const char *>(psResult->pabyData);
4451
0
    if (STARTS_WITH_CI(pszData, "http://") ||
4452
0
        STARTS_WITH_CI(pszData, "https://"))
4453
0
    {
4454
0
        CPLError(CE_Failure, CPLE_AppDefined,
4455
0
                 "The data that was downloaded also starts with 'http://' "
4456
0
                 "and cannot be passed into SetFromUserInput.  Is this "
4457
0
                 "really a spatial reference definition? ");
4458
0
        CPLHTTPDestroyResult(psResult);
4459
0
        return OGRERR_FAILURE;
4460
0
    }
4461
0
    if (OGRERR_NONE != SetFromUserInput(pszData))
4462
0
    {
4463
0
        CPLHTTPDestroyResult(psResult);
4464
0
        return OGRERR_FAILURE;
4465
0
    }
4466
4467
0
    CPLHTTPDestroyResult(psResult);
4468
0
    return OGRERR_NONE;
4469
0
}
4470
4471
/************************************************************************/
4472
/*                        OSRimportFromUrl()                            */
4473
/************************************************************************/
4474
4475
/**
4476
 * \brief Set spatial reference from a URL.
4477
 *
4478
 * This function is the same as OGRSpatialReference::importFromUrl()
4479
 */
4480
OGRErr OSRImportFromUrl(OGRSpatialReferenceH hSRS, const char *pszUrl)
4481
4482
0
{
4483
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromUrl", OGRERR_FAILURE);
4484
4485
0
    return ToPointer(hSRS)->importFromUrl(pszUrl);
4486
0
}
4487
4488
/************************************************************************/
4489
/*                         importFromURNPart()                          */
4490
/************************************************************************/
4491
OGRErr OGRSpatialReference::importFromURNPart(const char *pszAuthority,
4492
                                              const char *pszCode,
4493
                                              const char *pszURN)
4494
0
{
4495
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4496
0
    (void)this;
4497
0
    (void)pszAuthority;
4498
0
    (void)pszCode;
4499
0
    (void)pszURN;
4500
0
    return OGRERR_FAILURE;
4501
#else
4502
    /* -------------------------------------------------------------------- */
4503
    /*      Is this an EPSG code? Note that we import it with EPSG          */
4504
    /*      preferred axis ordering for geographic coordinate systems.      */
4505
    /* -------------------------------------------------------------------- */
4506
    if (STARTS_WITH_CI(pszAuthority, "EPSG"))
4507
        return importFromEPSGA(atoi(pszCode));
4508
4509
    /* -------------------------------------------------------------------- */
4510
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4511
    /* -------------------------------------------------------------------- */
4512
    if (STARTS_WITH_CI(pszAuthority, "IAU"))
4513
        return importFromDict("IAU2000.wkt", pszCode);
4514
4515
    /* -------------------------------------------------------------------- */
4516
    /*      Is this an OGC code?                                            */
4517
    /* -------------------------------------------------------------------- */
4518
    if (!STARTS_WITH_CI(pszAuthority, "OGC"))
4519
    {
4520
        CPLError(CE_Failure, CPLE_AppDefined,
4521
                 "URN %s has unrecognized authority.", pszURN);
4522
        return OGRERR_FAILURE;
4523
    }
4524
4525
    if (STARTS_WITH_CI(pszCode, "CRS84"))
4526
        return SetWellKnownGeogCS(pszCode);
4527
    else if (STARTS_WITH_CI(pszCode, "CRS83"))
4528
        return SetWellKnownGeogCS(pszCode);
4529
    else if (STARTS_WITH_CI(pszCode, "CRS27"))
4530
        return SetWellKnownGeogCS(pszCode);
4531
    else if (STARTS_WITH_CI(pszCode, "84"))  // urn:ogc:def:crs:OGC:2:84
4532
        return SetWellKnownGeogCS("CRS84");
4533
4534
    /* -------------------------------------------------------------------- */
4535
    /*      Handle auto codes.  We need to convert from format              */
4536
    /*      AUTO42001:99:8888 to format AUTO:42001,99,8888.                 */
4537
    /* -------------------------------------------------------------------- */
4538
    else if (STARTS_WITH_CI(pszCode, "AUTO"))
4539
    {
4540
        char szWMSAuto[100] = {'\0'};
4541
4542
        if (strlen(pszCode) > sizeof(szWMSAuto) - 2)
4543
            return OGRERR_FAILURE;
4544
4545
        snprintf(szWMSAuto, sizeof(szWMSAuto), "AUTO:%s", pszCode + 4);
4546
        for (int i = 5; szWMSAuto[i] != '\0'; i++)
4547
        {
4548
            if (szWMSAuto[i] == ':')
4549
                szWMSAuto[i] = ',';
4550
        }
4551
4552
        return importFromWMSAUTO(szWMSAuto);
4553
    }
4554
4555
    /* -------------------------------------------------------------------- */
4556
    /*      Not a recognise OGC item.                                       */
4557
    /* -------------------------------------------------------------------- */
4558
    CPLError(CE_Failure, CPLE_AppDefined, "URN %s value not supported.",
4559
             pszURN);
4560
4561
    return OGRERR_FAILURE;
4562
#endif
4563
0
}
4564
4565
/************************************************************************/
4566
/*                           importFromURN()                            */
4567
/*                                                                      */
4568
/*      See OGC recommendation paper 06-023r1 or later for details.     */
4569
/************************************************************************/
4570
4571
/**
4572
 * \brief Initialize from OGC URN.
4573
 *
4574
 * Initializes this spatial reference from a coordinate system defined
4575
 * by an OGC URN prefixed with "urn:ogc:def:crs:" per recommendation
4576
 * paper 06-023r1.  Currently EPSG and OGC authority values are supported,
4577
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4578
 *
4579
 * This method is also support through SetFromUserInput() which can
4580
 * normally be used for URNs.
4581
 *
4582
 * @param pszURN the urn string.
4583
 *
4584
 * @return OGRERR_NONE on success or an error code.
4585
 */
4586
4587
OGRErr OGRSpatialReference::importFromURN(const char *pszURN)
4588
4589
0
{
4590
0
    constexpr const char *EPSG_URN_CRS_PREFIX = "urn:ogc:def:crs:EPSG::";
4591
0
    if (STARTS_WITH(pszURN, EPSG_URN_CRS_PREFIX) &&
4592
0
        CPLGetValueType(pszURN + strlen(EPSG_URN_CRS_PREFIX)) ==
4593
0
            CPL_VALUE_INTEGER)
4594
0
    {
4595
0
        return importFromEPSG(atoi(pszURN + strlen(EPSG_URN_CRS_PREFIX)));
4596
0
    }
4597
4598
0
    TAKE_OPTIONAL_LOCK();
4599
4600
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4601
4602
    // PROJ 8.2.0 has support for IAU codes now.
4603
#if !PROJ_AT_LEAST_VERSION(8, 2, 0)
4604
    /* -------------------------------------------------------------------- */
4605
    /*      Is this an IAU code?  Lets try for the IAU2000 dictionary.      */
4606
    /* -------------------------------------------------------------------- */
4607
    const char *pszIAU = strstr(pszURN, "IAU");
4608
    if (pszIAU)
4609
    {
4610
        const char *pszCode = strchr(pszIAU, ':');
4611
        if (pszCode)
4612
        {
4613
            ++pszCode;
4614
            if (*pszCode == ':')
4615
                ++pszCode;
4616
            return importFromDict("IAU2000.wkt", pszCode);
4617
        }
4618
    }
4619
#endif
4620
4621
0
    if (strlen(pszURN) >= 1000)
4622
0
    {
4623
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4624
0
        return OGRERR_CORRUPT_DATA;
4625
0
    }
4626
0
    auto obj = proj_create(d->getPROJContext(), pszURN);
4627
0
    if (!obj)
4628
0
    {
4629
0
        return OGRERR_FAILURE;
4630
0
    }
4631
0
    Clear();
4632
0
    d->setPjCRS(obj);
4633
0
    return OGRERR_NONE;
4634
#else
4635
    const char *pszCur = nullptr;
4636
4637
    if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs:"))
4638
        pszCur = pszURN + 16;
4639
    else if (STARTS_WITH_CI(pszURN, "urn:ogc:def:crs,crs:"))
4640
        pszCur = pszURN + 20;
4641
    else if (STARTS_WITH_CI(pszURN, "urn:x-ogc:def:crs:"))
4642
        pszCur = pszURN + 18;
4643
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:crs:"))
4644
        pszCur = pszURN + 16;
4645
    else if (STARTS_WITH_CI(pszURN, "urn:opengis:def:crs:"))
4646
        pszCur = pszURN + 20;
4647
    else
4648
    {
4649
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4650
                 pszURN);
4651
        return OGRERR_FAILURE;
4652
    }
4653
4654
    /* -------------------------------------------------------------------- */
4655
    /*      Clear any existing definition.                                  */
4656
    /* -------------------------------------------------------------------- */
4657
    Clear();
4658
4659
    /* -------------------------------------------------------------------- */
4660
    /*      Find code (ignoring version) out of string like:                */
4661
    /*                                                                      */
4662
    /*      authority:[version]:code                                        */
4663
    /* -------------------------------------------------------------------- */
4664
    const char *pszAuthority = pszCur;
4665
4666
    // skip authority
4667
    while (*pszCur != ':' && *pszCur)
4668
        pszCur++;
4669
    if (*pszCur == ':')
4670
        pszCur++;
4671
4672
    // skip version
4673
    const char *pszBeforeVersion = pszCur;
4674
    while (*pszCur != ':' && *pszCur)
4675
        pszCur++;
4676
    if (*pszCur == ':')
4677
        pszCur++;
4678
    else
4679
        // We come here in the case, the content to parse is authority:code
4680
        // (instead of authority::code) which is probably illegal according to
4681
        // http://www.opengeospatial.org/ogcUrnPolicy but such content is found
4682
        // for example in what is returned by GeoServer.
4683
        pszCur = pszBeforeVersion;
4684
4685
    const char *pszCode = pszCur;
4686
4687
    const char *pszComma = strchr(pszCur, ',');
4688
    if (pszComma == nullptr)
4689
        return importFromURNPart(pszAuthority, pszCode, pszURN);
4690
4691
    // There's a second part with the vertical SRS.
4692
    pszCur = pszComma + 1;
4693
    if (!STARTS_WITH(pszCur, "crs:"))
4694
    {
4695
        CPLError(CE_Failure, CPLE_AppDefined, "URN %s not a supported format.",
4696
                 pszURN);
4697
        return OGRERR_FAILURE;
4698
    }
4699
4700
    pszCur += 4;
4701
4702
    char *pszFirstCode = CPLStrdup(pszCode);
4703
    pszFirstCode[pszComma - pszCode] = '\0';
4704
    OGRErr eStatus = importFromURNPart(pszAuthority, pszFirstCode, pszURN);
4705
    CPLFree(pszFirstCode);
4706
4707
    // Do we want to turn this into a compound definition
4708
    // with a vertical datum?
4709
    if (eStatus != OGRERR_NONE)
4710
        return eStatus;
4711
4712
    /* -------------------------------------------------------------------- */
4713
    /*      Find code (ignoring version) out of string like:                */
4714
    /*                                                                      */
4715
    /*      authority:[version]:code                                        */
4716
    /* -------------------------------------------------------------------- */
4717
    pszAuthority = pszCur;
4718
4719
    // skip authority
4720
    while (*pszCur != ':' && *pszCur)
4721
        pszCur++;
4722
    if (*pszCur == ':')
4723
        pszCur++;
4724
4725
    // skip version
4726
    pszBeforeVersion = pszCur;
4727
    while (*pszCur != ':' && *pszCur)
4728
        pszCur++;
4729
    if (*pszCur == ':')
4730
        pszCur++;
4731
    else
4732
        pszCur = pszBeforeVersion;
4733
4734
    pszCode = pszCur;
4735
4736
    OGRSpatialReference oVertSRS;
4737
    eStatus = oVertSRS.importFromURNPart(pszAuthority, pszCode, pszURN);
4738
    if (eStatus == OGRERR_NONE)
4739
    {
4740
        OGRSpatialReference oHorizSRS(*this);
4741
4742
        Clear();
4743
4744
        oHorizSRS.d->refreshProjObj();
4745
        oVertSRS.d->refreshProjObj();
4746
        if (!oHorizSRS.d->m_pj_crs || !oVertSRS.d->m_pj_crs)
4747
            return OGRERR_FAILURE;
4748
4749
        const char *pszHorizName = proj_get_name(oHorizSRS.d->m_pj_crs);
4750
        const char *pszVertName = proj_get_name(oVertSRS.d->m_pj_crs);
4751
4752
        CPLString osName = pszHorizName ? pszHorizName : "";
4753
        osName += " + ";
4754
        osName += pszVertName ? pszVertName : "";
4755
4756
        SetCompoundCS(osName, &oHorizSRS, &oVertSRS);
4757
    }
4758
4759
    return eStatus;
4760
#endif
4761
0
}
4762
4763
/************************************************************************/
4764
/*                           importFromCRSURL()                         */
4765
/*                                                                      */
4766
/*      See OGC Best Practice document 11-135 for details.              */
4767
/************************************************************************/
4768
4769
/**
4770
 * \brief Initialize from OGC URL.
4771
 *
4772
 * Initializes this spatial reference from a coordinate system defined
4773
 * by an OGC URL prefixed with "http://opengis.net/def/crs" per best practice
4774
 * paper 11-135.  Currently EPSG and OGC authority values are supported,
4775
 * including OGC auto codes, but not including CRS1 or CRS88 (NAVD88).
4776
 *
4777
 * This method is also supported through SetFromUserInput() which can
4778
 * normally be used for URLs.
4779
 *
4780
 * @param pszURL the URL string.
4781
 *
4782
 * @return OGRERR_NONE on success or an error code.
4783
 */
4784
4785
OGRErr OGRSpatialReference::importFromCRSURL(const char *pszURL)
4786
4787
0
{
4788
0
    TAKE_OPTIONAL_LOCK();
4789
4790
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4791
0
    if (strlen(pszURL) >= 10000)
4792
0
    {
4793
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4794
0
        return OGRERR_CORRUPT_DATA;
4795
0
    }
4796
4797
0
    PJ *obj;
4798
#if !PROJ_AT_LEAST_VERSION(9, 2, 0)
4799
    if (STARTS_WITH(pszURL, "http://www.opengis.net/def/crs/IAU/0/"))
4800
    {
4801
        obj = proj_create(
4802
            d->getPROJContext(),
4803
            CPLSPrintf("IAU:%s",
4804
                       pszURL +
4805
                           strlen("http://www.opengis.net/def/crs/IAU/0/")));
4806
    }
4807
    else
4808
#endif
4809
0
    {
4810
0
        obj = proj_create(d->getPROJContext(), pszURL);
4811
0
    }
4812
0
    if (!obj)
4813
0
    {
4814
0
        return OGRERR_FAILURE;
4815
0
    }
4816
0
    Clear();
4817
0
    d->setPjCRS(obj);
4818
0
    return OGRERR_NONE;
4819
#else
4820
    const char *pszCur = nullptr;
4821
4822
    if (STARTS_WITH_CI(pszURL, "http://opengis.net/def/crs"))
4823
        pszCur = pszURL + 26;
4824
    else if (STARTS_WITH_CI(pszURL, "https://opengis.net/def/crs"))
4825
        pszCur = pszURL + 27;
4826
    else if (STARTS_WITH_CI(pszURL, "http://www.opengis.net/def/crs"))
4827
        pszCur = pszURL + 30;
4828
    else if (STARTS_WITH_CI(pszURL, "https://www.opengis.net/def/crs"))
4829
        pszCur = pszURL + 31;
4830
    else if (STARTS_WITH_CI(pszURL, "www.opengis.net/def/crs"))
4831
        pszCur = pszURL + 23;
4832
    else
4833
    {
4834
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s not a supported format.",
4835
                 pszURL);
4836
        return OGRERR_FAILURE;
4837
    }
4838
4839
    if (*pszCur == '\0')
4840
    {
4841
        CPLError(CE_Failure, CPLE_AppDefined, "URL %s malformed.", pszURL);
4842
        return OGRERR_FAILURE;
4843
    }
4844
4845
    /* -------------------------------------------------------------------- */
4846
    /*      Clear any existing definition.                                  */
4847
    /* -------------------------------------------------------------------- */
4848
    Clear();
4849
4850
    if (STARTS_WITH_CI(pszCur, "-compound?1="))
4851
    {
4852
        /* --------------------------------------------------------------------
4853
         */
4854
        /*      It's a compound CRS, of the form: */
4855
        /*                                                                      */
4856
        /*      http://opengis.net/def/crs-compound?1=URL1&2=URL2&3=URL3&.. */
4857
        /* --------------------------------------------------------------------
4858
         */
4859
        pszCur += 12;
4860
4861
        // Extract each component CRS URL.
4862
        int iComponentUrl = 2;
4863
4864
        CPLString osName = "";
4865
        Clear();
4866
4867
        while (iComponentUrl != -1)
4868
        {
4869
            char searchStr[15] = {};
4870
            snprintf(searchStr, sizeof(searchStr), "&%d=", iComponentUrl);
4871
4872
            const char *pszUrlEnd = strstr(pszCur, searchStr);
4873
4874
            // Figure out the next component URL.
4875
            char *pszComponentUrl = nullptr;
4876
4877
            if (pszUrlEnd)
4878
            {
4879
                size_t nLen = pszUrlEnd - pszCur;
4880
                pszComponentUrl = static_cast<char *>(CPLMalloc(nLen + 1));
4881
                strncpy(pszComponentUrl, pszCur, nLen);
4882
                pszComponentUrl[nLen] = '\0';
4883
4884
                ++iComponentUrl;
4885
                pszCur += nLen + strlen(searchStr);
4886
            }
4887
            else
4888
            {
4889
                if (iComponentUrl == 2)
4890
                {
4891
                    CPLError(CE_Failure, CPLE_AppDefined,
4892
                             "Compound CRS URLs must have at least two "
4893
                             "component CRSs.");
4894
                    return OGRERR_FAILURE;
4895
                }
4896
                else
4897
                {
4898
                    pszComponentUrl = CPLStrdup(pszCur);
4899
                    // no more components
4900
                    iComponentUrl = -1;
4901
                }
4902
            }
4903
4904
            OGRSpatialReference oComponentSRS;
4905
            OGRErr eStatus = oComponentSRS.importFromCRSURL(pszComponentUrl);
4906
4907
            CPLFree(pszComponentUrl);
4908
            pszComponentUrl = nullptr;
4909
4910
            if (eStatus == OGRERR_NONE)
4911
            {
4912
                if (osName.length() != 0)
4913
                {
4914
                    osName += " + ";
4915
                }
4916
                osName += oComponentSRS.GetRoot()->GetValue();
4917
                SetNode("COMPD_CS", osName);
4918
                GetRoot()->AddChild(oComponentSRS.GetRoot()->Clone());
4919
            }
4920
            else
4921
                return eStatus;
4922
        }
4923
4924
        return OGRERR_NONE;
4925
    }
4926
4927
    /* -------------------------------------------------------------------- */
4928
    /*      It's a normal CRS URL, of the form:                             */
4929
    /*                                                                      */
4930
    /*      http://opengis.net/def/crs/AUTHORITY/VERSION/CODE               */
4931
    /* -------------------------------------------------------------------- */
4932
    ++pszCur;
4933
    const char *pszAuthority = pszCur;
4934
4935
    // skip authority
4936
    while (*pszCur != '/' && *pszCur)
4937
        pszCur++;
4938
    if (*pszCur == '/')
4939
        pszCur++;
4940
4941
    // skip version
4942
    while (*pszCur != '/' && *pszCur)
4943
        pszCur++;
4944
    if (*pszCur == '/')
4945
        pszCur++;
4946
4947
    const char *pszCode = pszCur;
4948
4949
    return importFromURNPart(pszAuthority, pszCode, pszURL);
4950
#endif
4951
0
}
4952
4953
/************************************************************************/
4954
/*                         importFromWMSAUTO()                          */
4955
/************************************************************************/
4956
4957
/**
4958
 * \brief Initialize from WMSAUTO string.
4959
 *
4960
 * Note that the WMS 1.3 specification does not include the
4961
 * units code, while apparently earlier specs do.  We try to
4962
 * guess around this.
4963
 *
4964
 * @param pszDefinition the WMSAUTO string
4965
 *
4966
 * @return OGRERR_NONE on success or an error code.
4967
 */
4968
OGRErr OGRSpatialReference::importFromWMSAUTO(const char *pszDefinition)
4969
4970
0
{
4971
0
    TAKE_OPTIONAL_LOCK();
4972
4973
0
#if PROJ_AT_LEAST_VERSION(8, 1, 0)
4974
0
    if (strlen(pszDefinition) >= 10000)
4975
0
    {
4976
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long input string");
4977
0
        return OGRERR_CORRUPT_DATA;
4978
0
    }
4979
4980
0
    auto obj = proj_create(d->getPROJContext(), pszDefinition);
4981
0
    if (!obj)
4982
0
    {
4983
0
        return OGRERR_FAILURE;
4984
0
    }
4985
0
    Clear();
4986
0
    d->setPjCRS(obj);
4987
0
    return OGRERR_NONE;
4988
#else
4989
    int nProjId, nUnitsId;
4990
    double dfRefLong, dfRefLat = 0.0;
4991
4992
    /* -------------------------------------------------------------------- */
4993
    /*      Tokenize                                                        */
4994
    /* -------------------------------------------------------------------- */
4995
    if (STARTS_WITH_CI(pszDefinition, "AUTO:"))
4996
        pszDefinition += 5;
4997
4998
    char **papszTokens =
4999
        CSLTokenizeStringComplex(pszDefinition, ",", FALSE, TRUE);
5000
5001
    if (CSLCount(papszTokens) == 4)
5002
    {
5003
        nProjId = atoi(papszTokens[0]);
5004
        nUnitsId = atoi(papszTokens[1]);
5005
        dfRefLong = CPLAtof(papszTokens[2]);
5006
        dfRefLat = CPLAtof(papszTokens[3]);
5007
    }
5008
    else if (CSLCount(papszTokens) == 3 && atoi(papszTokens[0]) == 42005)
5009
    {
5010
        nProjId = atoi(papszTokens[0]);
5011
        nUnitsId = atoi(papszTokens[1]);
5012
        dfRefLong = CPLAtof(papszTokens[2]);
5013
        dfRefLat = 0.0;
5014
    }
5015
    else if (CSLCount(papszTokens) == 3)
5016
    {
5017
        nProjId = atoi(papszTokens[0]);
5018
        nUnitsId = 9001;
5019
        dfRefLong = CPLAtof(papszTokens[1]);
5020
        dfRefLat = CPLAtof(papszTokens[2]);
5021
    }
5022
    else if (CSLCount(papszTokens) == 2 && atoi(papszTokens[0]) == 42005)
5023
    {
5024
        nProjId = atoi(papszTokens[0]);
5025
        nUnitsId = 9001;
5026
        dfRefLong = CPLAtof(papszTokens[1]);
5027
    }
5028
    else
5029
    {
5030
        CSLDestroy(papszTokens);
5031
        CPLError(CE_Failure, CPLE_AppDefined,
5032
                 "AUTO projection has wrong number of arguments, expected\n"
5033
                 "AUTO:proj_id,units_id,ref_long,ref_lat or"
5034
                 "AUTO:proj_id,ref_long,ref_lat");
5035
        return OGRERR_FAILURE;
5036
    }
5037
5038
    CSLDestroy(papszTokens);
5039
    papszTokens = nullptr;
5040
5041
    /* -------------------------------------------------------------------- */
5042
    /*      Build coordsys.                                                 */
5043
    /* -------------------------------------------------------------------- */
5044
    Clear();
5045
5046
    /* -------------------------------------------------------------------- */
5047
    /*      Set WGS84.                                                      */
5048
    /* -------------------------------------------------------------------- */
5049
    SetWellKnownGeogCS("WGS84");
5050
5051
    switch (nProjId)
5052
    {
5053
        case 42001:  // Auto UTM
5054
            SetUTM(static_cast<int>(floor((dfRefLong + 180.0) / 6.0)) + 1,
5055
                   dfRefLat >= 0.0);
5056
            break;
5057
5058
        case 42002:  // Auto TM (strangely very UTM-like).
5059
            SetTM(0, dfRefLong, 0.9996, 500000.0,
5060
                  (dfRefLat >= 0.0) ? 0.0 : 10000000.0);
5061
            break;
5062
5063
        case 42003:  // Auto Orthographic.
5064
            SetOrthographic(dfRefLat, dfRefLong, 0.0, 0.0);
5065
            break;
5066
5067
        case 42004:  // Auto Equirectangular
5068
            SetEquirectangular(dfRefLat, dfRefLong, 0.0, 0.0);
5069
            break;
5070
5071
        case 42005:
5072
            SetMollweide(dfRefLong, 0.0, 0.0);
5073
            break;
5074
5075
        default:
5076
            CPLError(CE_Failure, CPLE_AppDefined,
5077
                     "Unsupported projection id in importFromWMSAUTO(): %d",
5078
                     nProjId);
5079
            return OGRERR_FAILURE;
5080
    }
5081
5082
    /* -------------------------------------------------------------------- */
5083
    /*      Set units.                                                      */
5084
    /* -------------------------------------------------------------------- */
5085
5086
    switch (nUnitsId)
5087
    {
5088
        case 9001:
5089
            SetTargetLinearUnits(nullptr, SRS_UL_METER, 1.0, "EPSG", "9001");
5090
            break;
5091
5092
        case 9002:
5093
            SetTargetLinearUnits(nullptr, "Foot", 0.3048, "EPSG", "9002");
5094
            break;
5095
5096
        case 9003:
5097
            SetTargetLinearUnits(nullptr, "US survey foot",
5098
                                 CPLAtof(SRS_UL_US_FOOT_CONV), "EPSG", "9003");
5099
            break;
5100
5101
        default:
5102
            CPLError(CE_Failure, CPLE_AppDefined,
5103
                     "Unsupported units code (%d).", nUnitsId);
5104
            return OGRERR_FAILURE;
5105
            break;
5106
    }
5107
5108
    return OGRERR_NONE;
5109
#endif
5110
0
}
5111
5112
/************************************************************************/
5113
/*                            GetSemiMajor()                            */
5114
/************************************************************************/
5115
5116
/**
5117
 * \brief Get spheroid semi major axis (in metres starting with GDAL 3.0)
5118
 *
5119
 * This method does the same thing as the C function OSRGetSemiMajor().
5120
 *
5121
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi major axis
5122
 * can be found.
5123
 *
5124
 * @return semi-major axis, or SRS_WGS84_SEMIMAJOR if it can't be found.
5125
 */
5126
5127
double OGRSpatialReference::GetSemiMajor(OGRErr *pnErr) const
5128
5129
0
{
5130
0
    TAKE_OPTIONAL_LOCK();
5131
5132
0
    if (pnErr != nullptr)
5133
0
        *pnErr = OGRERR_FAILURE;
5134
5135
0
    d->refreshProjObj();
5136
0
    if (!d->m_pj_crs)
5137
0
        return SRS_WGS84_SEMIMAJOR;
5138
5139
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5140
0
    if (!ellps)
5141
0
        return SRS_WGS84_SEMIMAJOR;
5142
5143
0
    double dfSemiMajor = 0.0;
5144
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, &dfSemiMajor,
5145
0
                                  nullptr, nullptr, nullptr);
5146
0
    proj_destroy(ellps);
5147
5148
0
    if (dfSemiMajor > 0)
5149
0
    {
5150
0
        if (pnErr != nullptr)
5151
0
            *pnErr = OGRERR_NONE;
5152
0
        return dfSemiMajor;
5153
0
    }
5154
5155
0
    return SRS_WGS84_SEMIMAJOR;
5156
0
}
5157
5158
/************************************************************************/
5159
/*                          OSRGetSemiMajor()                           */
5160
/************************************************************************/
5161
5162
/**
5163
 * \brief Get spheroid semi major axis.
5164
 *
5165
 * This function is the same as OGRSpatialReference::GetSemiMajor()
5166
 */
5167
double OSRGetSemiMajor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5168
5169
0
{
5170
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMajor", 0);
5171
5172
0
    return ToPointer(hSRS)->GetSemiMajor(pnErr);
5173
0
}
5174
5175
/************************************************************************/
5176
/*                          GetInvFlattening()                          */
5177
/************************************************************************/
5178
5179
/**
5180
 * \brief Get spheroid inverse flattening.
5181
 *
5182
 * This method does the same thing as the C function OSRGetInvFlattening().
5183
 *
5184
 * @param pnErr if non-NULL set to OGRERR_FAILURE if no inverse flattening
5185
 * can be found.
5186
 *
5187
 * @return inverse flattening, or SRS_WGS84_INVFLATTENING if it can't be found.
5188
 */
5189
5190
double OGRSpatialReference::GetInvFlattening(OGRErr *pnErr) const
5191
5192
0
{
5193
0
    TAKE_OPTIONAL_LOCK();
5194
5195
0
    if (pnErr != nullptr)
5196
0
        *pnErr = OGRERR_FAILURE;
5197
5198
0
    d->refreshProjObj();
5199
0
    if (!d->m_pj_crs)
5200
0
        return SRS_WGS84_INVFLATTENING;
5201
5202
0
    auto ellps = proj_get_ellipsoid(d->getPROJContext(), d->m_pj_crs);
5203
0
    if (!ellps)
5204
0
        return SRS_WGS84_INVFLATTENING;
5205
5206
0
    double dfInvFlattening = -1.0;
5207
0
    proj_ellipsoid_get_parameters(d->getPROJContext(), ellps, nullptr, nullptr,
5208
0
                                  nullptr, &dfInvFlattening);
5209
0
    proj_destroy(ellps);
5210
5211
0
    if (dfInvFlattening >= 0.0)
5212
0
    {
5213
0
        if (pnErr != nullptr)
5214
0
            *pnErr = OGRERR_NONE;
5215
0
        return dfInvFlattening;
5216
0
    }
5217
5218
0
    return SRS_WGS84_INVFLATTENING;
5219
0
}
5220
5221
/************************************************************************/
5222
/*                        OSRGetInvFlattening()                         */
5223
/************************************************************************/
5224
5225
/**
5226
 * \brief Get spheroid inverse flattening.
5227
 *
5228
 * This function is the same as OGRSpatialReference::GetInvFlattening()
5229
 */
5230
double OSRGetInvFlattening(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5231
5232
0
{
5233
0
    VALIDATE_POINTER1(hSRS, "OSRGetInvFlattening", 0);
5234
5235
0
    return ToPointer(hSRS)->GetInvFlattening(pnErr);
5236
0
}
5237
5238
/************************************************************************/
5239
/*                           GetEccentricity()                          */
5240
/************************************************************************/
5241
5242
/**
5243
 * \brief Get spheroid eccentricity
5244
 *
5245
 * @return eccentricity (or -1 in case of error)
5246
 * @since GDAL 2.3
5247
 */
5248
5249
double OGRSpatialReference::GetEccentricity() const
5250
5251
0
{
5252
0
    OGRErr eErr = OGRERR_NONE;
5253
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5254
0
    if (eErr != OGRERR_NONE)
5255
0
    {
5256
0
        return -1.0;
5257
0
    }
5258
0
    if (dfInvFlattening == 0.0)
5259
0
        return 0.0;
5260
0
    if (dfInvFlattening < 0.5)
5261
0
        return -1.0;
5262
0
    return sqrt(2.0 / dfInvFlattening -
5263
0
                1.0 / (dfInvFlattening * dfInvFlattening));
5264
0
}
5265
5266
/************************************************************************/
5267
/*                      GetSquaredEccentricity()                        */
5268
/************************************************************************/
5269
5270
/**
5271
 * \brief Get spheroid squared eccentricity
5272
 *
5273
 * @return squared eccentricity (or -1 in case of error)
5274
 * @since GDAL 2.3
5275
 */
5276
5277
double OGRSpatialReference::GetSquaredEccentricity() const
5278
5279
0
{
5280
0
    OGRErr eErr = OGRERR_NONE;
5281
0
    const double dfInvFlattening = GetInvFlattening(&eErr);
5282
0
    if (eErr != OGRERR_NONE)
5283
0
    {
5284
0
        return -1.0;
5285
0
    }
5286
0
    if (dfInvFlattening == 0.0)
5287
0
        return 0.0;
5288
0
    if (dfInvFlattening < 0.5)
5289
0
        return -1.0;
5290
0
    return 2.0 / dfInvFlattening - 1.0 / (dfInvFlattening * dfInvFlattening);
5291
0
}
5292
5293
/************************************************************************/
5294
/*                            GetSemiMinor()                            */
5295
/************************************************************************/
5296
5297
/**
5298
 * \brief Get spheroid semi minor axis.
5299
 *
5300
 * This method does the same thing as the C function OSRGetSemiMinor().
5301
 *
5302
 * @param pnErr if non-NULL set to OGRERR_FAILURE if semi minor axis
5303
 * can be found.
5304
 *
5305
 * @return semi-minor axis, or WGS84 semi minor if it can't be found.
5306
 */
5307
5308
double OGRSpatialReference::GetSemiMinor(OGRErr *pnErr) const
5309
5310
0
{
5311
0
    const double dfSemiMajor = GetSemiMajor(pnErr);
5312
0
    const double dfInvFlattening = GetInvFlattening(pnErr);
5313
5314
0
    return OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
5315
0
}
5316
5317
/************************************************************************/
5318
/*                          OSRGetSemiMinor()                           */
5319
/************************************************************************/
5320
5321
/**
5322
 * \brief Get spheroid semi minor axis.
5323
 *
5324
 * This function is the same as OGRSpatialReference::GetSemiMinor()
5325
 */
5326
double OSRGetSemiMinor(OGRSpatialReferenceH hSRS, OGRErr *pnErr)
5327
5328
0
{
5329
0
    VALIDATE_POINTER1(hSRS, "OSRGetSemiMinor", 0);
5330
5331
0
    return ToPointer(hSRS)->GetSemiMinor(pnErr);
5332
0
}
5333
5334
/************************************************************************/
5335
/*                             SetLocalCS()                             */
5336
/************************************************************************/
5337
5338
/**
5339
 * \brief Set the user visible LOCAL_CS name.
5340
 *
5341
 * This method is the same as the C function OSRSetLocalCS().
5342
 *
5343
 * This method will ensure a LOCAL_CS node is created as the root,
5344
 * and set the provided name on it.  It must be used before SetLinearUnits().
5345
 *
5346
 * @param pszName the user visible name to assign.  Not used as a key.
5347
 *
5348
 * @return OGRERR_NONE on success.
5349
 */
5350
5351
OGRErr OGRSpatialReference::SetLocalCS(const char *pszName)
5352
5353
0
{
5354
0
    TAKE_OPTIONAL_LOCK();
5355
5356
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN ||
5357
0
        d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
5358
0
    {
5359
0
        d->setPjCRS(proj_create_engineering_crs(d->getPROJContext(), pszName));
5360
0
    }
5361
0
    else
5362
0
    {
5363
0
        CPLDebug("OGR",
5364
0
                 "OGRSpatialReference::SetLocalCS(%s) failed.  "
5365
0
                 "It appears an incompatible object already exists.",
5366
0
                 pszName);
5367
0
        return OGRERR_FAILURE;
5368
0
    }
5369
5370
0
    return OGRERR_NONE;
5371
0
}
5372
5373
/************************************************************************/
5374
/*                           OSRSetLocalCS()                            */
5375
/************************************************************************/
5376
5377
/**
5378
 * \brief Set the user visible LOCAL_CS name.
5379
 *
5380
 * This function is the same as OGRSpatialReference::SetLocalCS()
5381
 */
5382
OGRErr OSRSetLocalCS(OGRSpatialReferenceH hSRS, const char *pszName)
5383
5384
0
{
5385
0
    VALIDATE_POINTER1(hSRS, "OSRSetLocalCS", OGRERR_FAILURE);
5386
5387
0
    return ToPointer(hSRS)->SetLocalCS(pszName);
5388
0
}
5389
5390
/************************************************************************/
5391
/*                             SetGeocCS()                              */
5392
/************************************************************************/
5393
5394
/**
5395
 * \brief Set the user visible GEOCCS name.
5396
 *
5397
 * This method is the same as the C function OSRSetGeocCS().
5398
5399
 * This method will ensure a GEOCCS node is created as the root,
5400
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5401
 * the DATUM and PRIMEM nodes from the GEOGCS will be transferred over to
5402
 * the GEOGCS.
5403
 *
5404
 * @param pszName the user visible name to assign.  Not used as a key.
5405
 *
5406
 * @return OGRERR_NONE on success.
5407
 *
5408
 * @since OGR 1.9.0
5409
 */
5410
5411
OGRErr OGRSpatialReference::SetGeocCS(const char *pszName)
5412
5413
0
{
5414
0
    TAKE_OPTIONAL_LOCK();
5415
5416
0
    OGRErr eErr = OGRERR_NONE;
5417
0
    d->refreshProjObj();
5418
0
    d->demoteFromBoundCRS();
5419
0
    if (d->m_pjType == PJ_TYPE_UNKNOWN)
5420
0
    {
5421
0
        d->setPjCRS(proj_create_geocentric_crs(
5422
0
            d->getPROJContext(), pszName, "World Geodetic System 1984",
5423
0
            "WGS 84", SRS_WGS84_SEMIMAJOR, SRS_WGS84_INVFLATTENING,
5424
0
            SRS_PM_GREENWICH, 0.0, SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV),
5425
0
            "Metre", 1.0));
5426
0
    }
5427
0
    else if (d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS)
5428
0
    {
5429
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5430
0
    }
5431
0
    else if (d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
5432
0
             d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS)
5433
0
    {
5434
0
        auto datum = proj_crs_get_datum(d->getPROJContext(), d->m_pj_crs);
5435
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
5436
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
5437
0
        if (datum == nullptr)
5438
0
        {
5439
0
            datum =
5440
0
                proj_crs_get_datum_ensemble(d->getPROJContext(), d->m_pj_crs);
5441
0
        }
5442
0
#endif
5443
0
        if (datum == nullptr)
5444
0
        {
5445
0
            d->undoDemoteFromBoundCRS();
5446
0
            return OGRERR_FAILURE;
5447
0
        }
5448
5449
0
        auto pj_crs = proj_create_geocentric_crs_from_datum(
5450
0
            d->getPROJContext(), proj_get_name(d->m_pj_crs), datum, nullptr,
5451
0
            0.0);
5452
0
        d->setPjCRS(pj_crs);
5453
5454
0
        proj_destroy(datum);
5455
0
    }
5456
0
    else
5457
0
    {
5458
0
        CPLDebug("OGR",
5459
0
                 "OGRSpatialReference::SetGeocCS(%s) failed.  "
5460
0
                 "It appears an incompatible object already exists.",
5461
0
                 pszName);
5462
0
        eErr = OGRERR_FAILURE;
5463
0
    }
5464
0
    d->undoDemoteFromBoundCRS();
5465
5466
0
    return eErr;
5467
0
}
5468
5469
/************************************************************************/
5470
/*                            OSRSetGeocCS()                            */
5471
/************************************************************************/
5472
5473
/**
5474
 * \brief Set the user visible PROJCS name.
5475
 *
5476
 * This function is the same as OGRSpatialReference::SetGeocCS()
5477
 *
5478
 * @since OGR 1.9.0
5479
 */
5480
OGRErr OSRSetGeocCS(OGRSpatialReferenceH hSRS, const char *pszName)
5481
5482
0
{
5483
0
    VALIDATE_POINTER1(hSRS, "OSRSetGeocCS", OGRERR_FAILURE);
5484
5485
0
    return ToPointer(hSRS)->SetGeocCS(pszName);
5486
0
}
5487
5488
/************************************************************************/
5489
/*                             SetVertCS()                              */
5490
/************************************************************************/
5491
5492
/**
5493
 * \brief Set the user visible VERT_CS name.
5494
 *
5495
 * This method is the same as the C function OSRSetVertCS().
5496
5497
 * This method will ensure a VERT_CS node is created if needed.  If the
5498
 * existing coordinate system is GEOGCS or PROJCS rooted, then it will be
5499
 * turned into a COMPD_CS.
5500
 *
5501
 * @param pszVertCSName the user visible name of the vertical coordinate
5502
 * system. Not used as a key.
5503
 *
5504
 * @param pszVertDatumName the user visible name of the vertical datum.  It
5505
 * is helpful if this matches the EPSG name.
5506
 *
5507
 * @param nVertDatumType the OGC vertical datum type. Ignored
5508
 *
5509
 * @return OGRERR_NONE on success.
5510
 *
5511
 * @since OGR 1.9.0
5512
 */
5513
5514
OGRErr OGRSpatialReference::SetVertCS(const char *pszVertCSName,
5515
                                      const char *pszVertDatumName,
5516
                                      int nVertDatumType)
5517
5518
0
{
5519
0
    TAKE_OPTIONAL_LOCK();
5520
5521
0
    CPL_IGNORE_RET_VAL(nVertDatumType);
5522
5523
0
    d->refreshProjObj();
5524
5525
0
    auto vertCRS = proj_create_vertical_crs(d->getPROJContext(), pszVertCSName,
5526
0
                                            pszVertDatumName, nullptr, 0.0);
5527
5528
    /* -------------------------------------------------------------------- */
5529
    /*      Handle the case where we want to make a compound coordinate     */
5530
    /*      system.                                                         */
5531
    /* -------------------------------------------------------------------- */
5532
0
    if (IsProjected() || IsGeographic())
5533
0
    {
5534
0
        auto compoundCRS = proj_create_compound_crs(
5535
0
            d->getPROJContext(), nullptr, d->m_pj_crs, vertCRS);
5536
0
        proj_destroy(vertCRS);
5537
0
        d->setPjCRS(compoundCRS);
5538
0
    }
5539
0
    else
5540
0
    {
5541
0
        d->setPjCRS(vertCRS);
5542
0
    }
5543
0
    return OGRERR_NONE;
5544
0
}
5545
5546
/************************************************************************/
5547
/*                            OSRSetVertCS()                            */
5548
/************************************************************************/
5549
5550
/**
5551
 * \brief Setup the vertical coordinate system.
5552
 *
5553
 * This function is the same as OGRSpatialReference::SetVertCS()
5554
 *
5555
 * @since OGR 1.9.0
5556
 */
5557
OGRErr OSRSetVertCS(OGRSpatialReferenceH hSRS, const char *pszVertCSName,
5558
                    const char *pszVertDatumName, int nVertDatumType)
5559
5560
0
{
5561
0
    VALIDATE_POINTER1(hSRS, "OSRSetVertCS", OGRERR_FAILURE);
5562
5563
0
    return ToPointer(hSRS)->SetVertCS(pszVertCSName, pszVertDatumName,
5564
0
                                      nVertDatumType);
5565
0
}
5566
5567
/************************************************************************/
5568
/*                           SetCompoundCS()                            */
5569
/************************************************************************/
5570
5571
/**
5572
 * \brief Setup a compound coordinate system.
5573
 *
5574
 * This method is the same as the C function OSRSetCompoundCS().
5575
5576
 * This method is replace the current SRS with a COMPD_CS coordinate system
5577
 * consisting of the passed in horizontal and vertical coordinate systems.
5578
 *
5579
 * @param pszName the name of the compound coordinate system.
5580
 *
5581
 * @param poHorizSRS the horizontal SRS (PROJCS or GEOGCS).
5582
 *
5583
 * @param poVertSRS the vertical SRS (VERT_CS).
5584
 *
5585
 * @return OGRERR_NONE on success.
5586
 */
5587
5588
OGRErr OGRSpatialReference::SetCompoundCS(const char *pszName,
5589
                                          const OGRSpatialReference *poHorizSRS,
5590
                                          const OGRSpatialReference *poVertSRS)
5591
5592
0
{
5593
0
    TAKE_OPTIONAL_LOCK();
5594
5595
    /* -------------------------------------------------------------------- */
5596
    /*      Verify these are legal horizontal and vertical coordinate       */
5597
    /*      systems.                                                        */
5598
    /* -------------------------------------------------------------------- */
5599
0
    if (!poVertSRS->IsVertical())
5600
0
    {
5601
0
        CPLError(CE_Failure, CPLE_AppDefined,
5602
0
                 "SetCompoundCS() fails, vertical component is not VERT_CS.");
5603
0
        return OGRERR_FAILURE;
5604
0
    }
5605
0
    if (!poHorizSRS->IsProjected() && !poHorizSRS->IsGeographic())
5606
0
    {
5607
0
        CPLError(CE_Failure, CPLE_AppDefined,
5608
0
                 "SetCompoundCS() fails, horizontal component is not PROJCS or "
5609
0
                 "GEOGCS.");
5610
0
        return OGRERR_FAILURE;
5611
0
    }
5612
5613
    /* -------------------------------------------------------------------- */
5614
    /*      Replace with compound srs.                                      */
5615
    /* -------------------------------------------------------------------- */
5616
0
    Clear();
5617
5618
0
    auto compoundCRS = proj_create_compound_crs(d->getPROJContext(), pszName,
5619
0
                                                poHorizSRS->d->m_pj_crs,
5620
0
                                                poVertSRS->d->m_pj_crs);
5621
0
    d->setPjCRS(compoundCRS);
5622
5623
0
    return OGRERR_NONE;
5624
0
}
5625
5626
/************************************************************************/
5627
/*                          OSRSetCompoundCS()                          */
5628
/************************************************************************/
5629
5630
/**
5631
 * \brief Setup a compound coordinate system.
5632
 *
5633
 * This function is the same as OGRSpatialReference::SetCompoundCS()
5634
 */
5635
OGRErr OSRSetCompoundCS(OGRSpatialReferenceH hSRS, const char *pszName,
5636
                        OGRSpatialReferenceH hHorizSRS,
5637
                        OGRSpatialReferenceH hVertSRS)
5638
5639
0
{
5640
0
    VALIDATE_POINTER1(hSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5641
0
    VALIDATE_POINTER1(hHorizSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5642
0
    VALIDATE_POINTER1(hVertSRS, "OSRSetCompoundCS", OGRERR_FAILURE);
5643
5644
0
    return ToPointer(hSRS)->SetCompoundCS(pszName, ToPointer(hHorizSRS),
5645
0
                                          ToPointer(hVertSRS));
5646
0
}
5647
5648
/************************************************************************/
5649
/*                             SetProjCS()                              */
5650
/************************************************************************/
5651
5652
/**
5653
 * \brief Set the user visible PROJCS name.
5654
 *
5655
 * This method is the same as the C function OSRSetProjCS().
5656
 *
5657
 * This method will ensure a PROJCS node is created as the root,
5658
 * and set the provided name on it.  If used on a GEOGCS coordinate system,
5659
 * the GEOGCS node will be demoted to be a child of the new PROJCS root.
5660
 *
5661
 * @param pszName the user visible name to assign.  Not used as a key.
5662
 *
5663
 * @return OGRERR_NONE on success.
5664
 */
5665
5666
OGRErr OGRSpatialReference::SetProjCS(const char *pszName)
5667
5668
0
{
5669
0
    TAKE_OPTIONAL_LOCK();
5670
5671
0
    d->refreshProjObj();
5672
0
    d->demoteFromBoundCRS();
5673
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
5674
0
    {
5675
0
        d->setPjCRS(proj_alter_name(d->getPROJContext(), d->m_pj_crs, pszName));
5676
0
    }
5677
0
    else
5678
0
    {
5679
0
        auto dummyConv = proj_create_conversion(d->getPROJContext(), nullptr,
5680
0
                                                nullptr, nullptr, nullptr,
5681
0
                                                nullptr, nullptr, 0, nullptr);
5682
0
        auto cs = proj_create_cartesian_2D_cs(
5683
0
            d->getPROJContext(), PJ_CART2D_EASTING_NORTHING, nullptr, 0);
5684
5685
0
        auto projCRS = proj_create_projected_crs(
5686
0
            d->getPROJContext(), pszName, d->getGeodBaseCRS(), dummyConv, cs);
5687
0
        proj_destroy(dummyConv);
5688
0
        proj_destroy(cs);
5689
5690
0
        d->setPjCRS(projCRS);
5691
0
    }
5692
0
    d->undoDemoteFromBoundCRS();
5693
0
    return OGRERR_NONE;
5694
0
}
5695
5696
/************************************************************************/
5697
/*                            OSRSetProjCS()                            */
5698
/************************************************************************/
5699
5700
/**
5701
 * \brief Set the user visible PROJCS name.
5702
 *
5703
 * This function is the same as OGRSpatialReference::SetProjCS()
5704
 */
5705
OGRErr OSRSetProjCS(OGRSpatialReferenceH hSRS, const char *pszName)
5706
5707
0
{
5708
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjCS", OGRERR_FAILURE);
5709
5710
0
    return ToPointer(hSRS)->SetProjCS(pszName);
5711
0
}
5712
5713
/************************************************************************/
5714
/*                           SetProjection()                            */
5715
/************************************************************************/
5716
5717
/**
5718
 * \brief Set a projection name.
5719
 *
5720
 * This method is the same as the C function OSRSetProjection().
5721
 *
5722
 * @param pszProjection the projection name, which should be selected from
5723
 * the macros in ogr_srs_api.h, such as SRS_PT_TRANSVERSE_MERCATOR.
5724
 *
5725
 * @return OGRERR_NONE on success.
5726
 */
5727
5728
OGRErr OGRSpatialReference::SetProjection(const char *pszProjection)
5729
5730
0
{
5731
0
    TAKE_OPTIONAL_LOCK();
5732
5733
0
    OGR_SRSNode *poGeogCS = nullptr;
5734
5735
0
    if (GetRoot() != nullptr && EQUAL(d->m_poRoot->GetValue(), "GEOGCS"))
5736
0
    {
5737
0
        poGeogCS = d->m_poRoot;
5738
0
        d->m_poRoot = nullptr;
5739
0
    }
5740
5741
0
    if (!GetAttrNode("PROJCS"))
5742
0
    {
5743
0
        SetNode("PROJCS", "unnamed");
5744
0
    }
5745
5746
0
    const OGRErr eErr = SetNode("PROJCS|PROJECTION", pszProjection);
5747
0
    if (eErr != OGRERR_NONE)
5748
0
        return eErr;
5749
5750
0
    if (poGeogCS != nullptr)
5751
0
        d->m_poRoot->InsertChild(poGeogCS, 1);
5752
5753
0
    return OGRERR_NONE;
5754
0
}
5755
5756
/************************************************************************/
5757
/*                            OSRSetProjection()                        */
5758
/************************************************************************/
5759
5760
/**
5761
 * \brief Set a projection name.
5762
 *
5763
 * This function is the same as OGRSpatialReference::SetProjection()
5764
 */
5765
OGRErr OSRSetProjection(OGRSpatialReferenceH hSRS, const char *pszProjection)
5766
5767
0
{
5768
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjection", OGRERR_FAILURE);
5769
5770
0
    return ToPointer(hSRS)->SetProjection(pszProjection);
5771
0
}
5772
5773
/************************************************************************/
5774
/*                      GetWKT2ProjectionMethod()                       */
5775
/************************************************************************/
5776
5777
/**
5778
 * \brief Returns info on the projection method, based on WKT2 naming
5779
 * conventions.
5780
 *
5781
 * The returned strings are short lived and should be considered to be
5782
 * invalidated by any further call to the GDAL API.
5783
 *
5784
 * @param[out] ppszMethodName Pointer to a string that will receive the
5785
 * projection method name.
5786
 * @param[out] ppszMethodAuthName null pointer, or pointer to a string that will
5787
 * receive the name of the authority that defines the projection method.
5788
 * *ppszMethodAuthName may be nullptr if the projection method is not linked to
5789
 * an authority.
5790
 * @param[out] ppszMethodCode null pointer, or pointer to a string that will
5791
 * receive the code that defines the projection method.
5792
 * *ppszMethodCode may be nullptr if the projection method is not linked to
5793
 * an authority.
5794
 *
5795
 * @return OGRERR_NONE on success.
5796
 */
5797
OGRErr
5798
OGRSpatialReference::GetWKT2ProjectionMethod(const char **ppszMethodName,
5799
                                             const char **ppszMethodAuthName,
5800
                                             const char **ppszMethodCode) const
5801
0
{
5802
0
    TAKE_OPTIONAL_LOCK();
5803
5804
0
    auto conv = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
5805
0
    if (!conv)
5806
0
        return OGRERR_FAILURE;
5807
0
    const char *pszTmpMethodName = "";
5808
0
    const char *pszTmpMethodAuthName = "";
5809
0
    const char *pszTmpMethodCode = "";
5810
0
    int ret = proj_coordoperation_get_method_info(
5811
0
        d->getPROJContext(), conv, &pszTmpMethodName, &pszTmpMethodAuthName,
5812
0
        &pszTmpMethodCode);
5813
    // "Internalize" temporary strings returned by PROJ
5814
0
    CPLAssert(pszTmpMethodName);
5815
0
    if (ppszMethodName)
5816
0
        *ppszMethodName = CPLSPrintf("%s", pszTmpMethodName);
5817
0
    if (ppszMethodAuthName)
5818
0
        *ppszMethodAuthName = pszTmpMethodAuthName
5819
0
                                  ? CPLSPrintf("%s", pszTmpMethodAuthName)
5820
0
                                  : nullptr;
5821
0
    if (ppszMethodCode)
5822
0
        *ppszMethodCode =
5823
0
            pszTmpMethodCode ? CPLSPrintf("%s", pszTmpMethodCode) : nullptr;
5824
0
    proj_destroy(conv);
5825
0
    return ret ? OGRERR_NONE : OGRERR_FAILURE;
5826
0
}
5827
5828
/************************************************************************/
5829
/*                            SetProjParm()                             */
5830
/************************************************************************/
5831
5832
/**
5833
 * \brief Set a projection parameter value.
5834
 *
5835
 * Adds a new PARAMETER under the PROJCS with the indicated name and value.
5836
 *
5837
 * This method is the same as the C function OSRSetProjParm().
5838
 *
5839
 * Please check https://gdal.org/proj_list pages for
5840
 * legal parameter names for specific projections.
5841
 *
5842
 *
5843
 * @param pszParamName the parameter name, which should be selected from
5844
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
5845
 *
5846
 * @param dfValue value to assign.
5847
 *
5848
 * @return OGRERR_NONE on success.
5849
 */
5850
5851
OGRErr OGRSpatialReference::SetProjParm(const char *pszParamName,
5852
                                        double dfValue)
5853
5854
0
{
5855
0
    TAKE_OPTIONAL_LOCK();
5856
5857
0
    OGR_SRSNode *poPROJCS = GetAttrNode("PROJCS");
5858
5859
0
    if (poPROJCS == nullptr)
5860
0
        return OGRERR_FAILURE;
5861
5862
0
    char szValue[64] = {'\0'};
5863
0
    OGRsnPrintDouble(szValue, sizeof(szValue), dfValue);
5864
5865
    /* -------------------------------------------------------------------- */
5866
    /*      Try to find existing parameter with this name.                  */
5867
    /* -------------------------------------------------------------------- */
5868
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5869
0
    {
5870
0
        OGR_SRSNode *poParam = poPROJCS->GetChild(iChild);
5871
5872
0
        if (EQUAL(poParam->GetValue(), "PARAMETER") &&
5873
0
            poParam->GetChildCount() == 2 &&
5874
0
            EQUAL(poParam->GetChild(0)->GetValue(), pszParamName))
5875
0
        {
5876
0
            poParam->GetChild(1)->SetValue(szValue);
5877
0
            return OGRERR_NONE;
5878
0
        }
5879
0
    }
5880
5881
    /* -------------------------------------------------------------------- */
5882
    /*      Otherwise create a new parameter and append.                    */
5883
    /* -------------------------------------------------------------------- */
5884
0
    OGR_SRSNode *poParam = new OGR_SRSNode("PARAMETER");
5885
0
    poParam->AddChild(new OGR_SRSNode(pszParamName));
5886
0
    poParam->AddChild(new OGR_SRSNode(szValue));
5887
5888
0
    poPROJCS->AddChild(poParam);
5889
5890
0
    return OGRERR_NONE;
5891
0
}
5892
5893
/************************************************************************/
5894
/*                           OSRSetProjParm()                           */
5895
/************************************************************************/
5896
5897
/**
5898
 * \brief Set a projection parameter value.
5899
 *
5900
 * This function is the same as OGRSpatialReference::SetProjParm()
5901
 */
5902
OGRErr OSRSetProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
5903
                      double dfValue)
5904
5905
0
{
5906
0
    VALIDATE_POINTER1(hSRS, "OSRSetProjParm", OGRERR_FAILURE);
5907
5908
0
    return ToPointer(hSRS)->SetProjParm(pszParamName, dfValue);
5909
0
}
5910
5911
/************************************************************************/
5912
/*                            FindProjParm()                            */
5913
/************************************************************************/
5914
5915
/**
5916
 * \brief Return the child index of the named projection parameter on
5917
 * its parent PROJCS node.
5918
 *
5919
 * @param pszParameter projection parameter to look for
5920
 * @param poPROJCS projection CS node to look in. If NULL is passed,
5921
 *        the PROJCS node of the SpatialReference object will be searched.
5922
 *
5923
 * @return the child index of the named projection parameter. -1 on failure
5924
 */
5925
int OGRSpatialReference::FindProjParm(const char *pszParameter,
5926
                                      const OGR_SRSNode *poPROJCS) const
5927
5928
0
{
5929
0
    TAKE_OPTIONAL_LOCK();
5930
5931
0
    if (poPROJCS == nullptr)
5932
0
        poPROJCS = GetAttrNode("PROJCS");
5933
5934
0
    if (poPROJCS == nullptr)
5935
0
        return -1;
5936
5937
    /* -------------------------------------------------------------------- */
5938
    /*      Search for requested parameter.                                 */
5939
    /* -------------------------------------------------------------------- */
5940
0
    bool bIsWKT2 = false;
5941
0
    for (int iChild = 0; iChild < poPROJCS->GetChildCount(); iChild++)
5942
0
    {
5943
0
        const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
5944
5945
0
        if (poParameter->GetChildCount() >= 2)
5946
0
        {
5947
0
            const char *pszValue = poParameter->GetValue();
5948
0
            if (EQUAL(pszValue, "PARAMETER") &&
5949
0
                EQUAL(poPROJCS->GetChild(iChild)->GetChild(0)->GetValue(),
5950
0
                      pszParameter))
5951
0
            {
5952
0
                return iChild;
5953
0
            }
5954
0
            else if (EQUAL(pszValue, "METHOD"))
5955
0
            {
5956
0
                bIsWKT2 = true;
5957
0
            }
5958
0
        }
5959
0
    }
5960
5961
    /* -------------------------------------------------------------------- */
5962
    /*      Try similar names, for selected parameters.                     */
5963
    /* -------------------------------------------------------------------- */
5964
0
    if (EQUAL(pszParameter, SRS_PP_LATITUDE_OF_ORIGIN))
5965
0
    {
5966
0
        if (bIsWKT2)
5967
0
        {
5968
0
            int iChild = FindProjParm(
5969
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5970
0
            if (iChild == -1)
5971
0
                iChild = FindProjParm(
5972
0
                    EPSG_NAME_PARAMETER_LATITUDE_PROJECTION_CENTRE, poPROJCS);
5973
0
            return iChild;
5974
0
        }
5975
0
        return FindProjParm(SRS_PP_LATITUDE_OF_CENTER, poPROJCS);
5976
0
    }
5977
5978
0
    if (EQUAL(pszParameter, SRS_PP_CENTRAL_MERIDIAN))
5979
0
    {
5980
0
        if (bIsWKT2)
5981
0
        {
5982
0
            int iChild = FindProjParm(
5983
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN, poPROJCS);
5984
0
            if (iChild == -1)
5985
0
                iChild = FindProjParm(
5986
0
                    EPSG_NAME_PARAMETER_LONGITUDE_PROJECTION_CENTRE, poPROJCS);
5987
0
            return iChild;
5988
0
        }
5989
0
        int iChild = FindProjParm(SRS_PP_LONGITUDE_OF_CENTER, poPROJCS);
5990
0
        if (iChild == -1)
5991
0
            iChild = FindProjParm(SRS_PP_LONGITUDE_OF_ORIGIN, poPROJCS);
5992
0
        return iChild;
5993
0
    }
5994
5995
0
    return -1;
5996
0
}
5997
5998
/************************************************************************/
5999
/*                            GetProjParm()                             */
6000
/************************************************************************/
6001
6002
/**
6003
 * \brief Fetch a projection parameter value.
6004
 *
6005
 * NOTE: This code should be modified to translate non degree angles into
6006
 * degrees based on the GEOGCS unit.  This has not yet been done.
6007
 *
6008
 * This method is the same as the C function OSRGetProjParm().
6009
 *
6010
 * @param pszName the name of the parameter to fetch, from the set of
6011
 * SRS_PP codes in ogr_srs_api.h.
6012
 *
6013
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6014
 *
6015
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6016
 *
6017
 * @return value of parameter.
6018
 */
6019
6020
double OGRSpatialReference::GetProjParm(const char *pszName,
6021
                                        double dfDefaultValue,
6022
                                        OGRErr *pnErr) const
6023
6024
0
{
6025
0
    TAKE_OPTIONAL_LOCK();
6026
6027
0
    d->refreshProjObj();
6028
0
    GetRoot();  // force update of d->m_bNodesWKT2
6029
6030
0
    if (pnErr != nullptr)
6031
0
        *pnErr = OGRERR_NONE;
6032
6033
    /* -------------------------------------------------------------------- */
6034
    /*      Find the desired parameter.                                     */
6035
    /* -------------------------------------------------------------------- */
6036
0
    const OGR_SRSNode *poPROJCS =
6037
0
        GetAttrNode(d->m_bNodesWKT2 ? "CONVERSION" : "PROJCS");
6038
0
    if (poPROJCS == nullptr)
6039
0
    {
6040
0
        if (pnErr != nullptr)
6041
0
            *pnErr = OGRERR_FAILURE;
6042
0
        return dfDefaultValue;
6043
0
    }
6044
6045
0
    const int iChild = FindProjParm(pszName, poPROJCS);
6046
0
    if (iChild == -1)
6047
0
    {
6048
0
        if (IsProjected() && GetAxesCount() == 3)
6049
0
        {
6050
0
            OGRSpatialReference *poSRSTmp = Clone();
6051
0
            poSRSTmp->DemoteTo2D(nullptr);
6052
0
            const double dfRet =
6053
0
                poSRSTmp->GetProjParm(pszName, dfDefaultValue, pnErr);
6054
0
            delete poSRSTmp;
6055
0
            return dfRet;
6056
0
        }
6057
6058
0
        if (pnErr != nullptr)
6059
0
            *pnErr = OGRERR_FAILURE;
6060
0
        return dfDefaultValue;
6061
0
    }
6062
6063
0
    const OGR_SRSNode *poParameter = poPROJCS->GetChild(iChild);
6064
0
    return CPLAtof(poParameter->GetChild(1)->GetValue());
6065
0
}
6066
6067
/************************************************************************/
6068
/*                           OSRGetProjParm()                           */
6069
/************************************************************************/
6070
6071
/**
6072
 * \brief Fetch a projection parameter value.
6073
 *
6074
 * This function is the same as OGRSpatialReference::GetProjParm()
6075
 */
6076
double OSRGetProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6077
                      double dfDefaultValue, OGRErr *pnErr)
6078
6079
0
{
6080
0
    VALIDATE_POINTER1(hSRS, "OSRGetProjParm", 0);
6081
6082
0
    return ToPointer(hSRS)->GetProjParm(pszName, dfDefaultValue, pnErr);
6083
0
}
6084
6085
/************************************************************************/
6086
/*                          GetNormProjParm()                           */
6087
/************************************************************************/
6088
6089
/**
6090
 * \brief Fetch a normalized projection parameter value.
6091
 *
6092
 * This method is the same as GetProjParm() except that the value of
6093
 * the parameter is "normalized" into degrees or meters depending on
6094
 * whether it is linear or angular.
6095
 *
6096
 * This method is the same as the C function OSRGetNormProjParm().
6097
 *
6098
 * @param pszName the name of the parameter to fetch, from the set of
6099
 * SRS_PP codes in ogr_srs_api.h.
6100
 *
6101
 * @param dfDefaultValue the value to return if this parameter doesn't exist.
6102
 *
6103
 * @param pnErr place to put error code on failure.  Ignored if NULL.
6104
 *
6105
 * @return value of parameter.
6106
 */
6107
6108
double OGRSpatialReference::GetNormProjParm(const char *pszName,
6109
                                            double dfDefaultValue,
6110
                                            OGRErr *pnErr) const
6111
6112
0
{
6113
0
    TAKE_OPTIONAL_LOCK();
6114
6115
0
    GetNormInfo();
6116
6117
0
    OGRErr nError = OGRERR_NONE;
6118
0
    double dfRawResult = GetProjParm(pszName, dfDefaultValue, &nError);
6119
0
    if (pnErr != nullptr)
6120
0
        *pnErr = nError;
6121
6122
    // If we got the default just return it unadjusted.
6123
0
    if (nError != OGRERR_NONE)
6124
0
        return dfRawResult;
6125
6126
0
    if (d->dfToDegrees != 1.0 && IsAngularParameter(pszName))
6127
0
        dfRawResult *= d->dfToDegrees;
6128
6129
0
    if (d->dfToMeter != 1.0 && IsLinearParameter(pszName))
6130
0
        return dfRawResult * d->dfToMeter;
6131
6132
0
    return dfRawResult;
6133
0
}
6134
6135
/************************************************************************/
6136
/*                         OSRGetNormProjParm()                         */
6137
/************************************************************************/
6138
6139
/**
6140
 * \brief This function is the same as OGRSpatialReference::
6141
 *
6142
 * This function is the same as OGRSpatialReference::GetNormProjParm()
6143
 */
6144
double OSRGetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszName,
6145
                          double dfDefaultValue, OGRErr *pnErr)
6146
6147
0
{
6148
0
    VALIDATE_POINTER1(hSRS, "OSRGetNormProjParm", 0);
6149
6150
0
    return ToPointer(hSRS)->GetNormProjParm(pszName, dfDefaultValue, pnErr);
6151
0
}
6152
6153
/************************************************************************/
6154
/*                          SetNormProjParm()                           */
6155
/************************************************************************/
6156
6157
/**
6158
 * \brief Set a projection parameter with a normalized value.
6159
 *
6160
 * This method is the same as SetProjParm() except that the value of
6161
 * the parameter passed in is assumed to be in "normalized" form (decimal
6162
 * degrees for angular values, meters for linear values.  The values are
6163
 * converted in a form suitable for the GEOGCS and linear units in effect.
6164
 *
6165
 * This method is the same as the C function OSRSetNormProjParm().
6166
 *
6167
 * @param pszName the parameter name, which should be selected from
6168
 * the macros in ogr_srs_api.h, such as SRS_PP_CENTRAL_MERIDIAN.
6169
 *
6170
 * @param dfValue value to assign.
6171
 *
6172
 * @return OGRERR_NONE on success.
6173
 */
6174
6175
OGRErr OGRSpatialReference::SetNormProjParm(const char *pszName, double dfValue)
6176
6177
0
{
6178
0
    TAKE_OPTIONAL_LOCK();
6179
6180
0
    GetNormInfo();
6181
6182
0
    if (d->dfToDegrees != 0.0 &&
6183
0
        (d->dfToDegrees != 1.0 || d->dfFromGreenwich != 0.0) &&
6184
0
        IsAngularParameter(pszName))
6185
0
    {
6186
0
        dfValue /= d->dfToDegrees;
6187
0
    }
6188
0
    else if (d->dfToMeter != 1.0 && d->dfToMeter != 0.0 &&
6189
0
             IsLinearParameter(pszName))
6190
0
        dfValue /= d->dfToMeter;
6191
6192
0
    return SetProjParm(pszName, dfValue);
6193
0
}
6194
6195
/************************************************************************/
6196
/*                         OSRSetNormProjParm()                         */
6197
/************************************************************************/
6198
6199
/**
6200
 * \brief Set a projection parameter with a normalized value.
6201
 *
6202
 * This function is the same as OGRSpatialReference::SetNormProjParm()
6203
 */
6204
OGRErr OSRSetNormProjParm(OGRSpatialReferenceH hSRS, const char *pszParamName,
6205
                          double dfValue)
6206
6207
0
{
6208
0
    VALIDATE_POINTER1(hSRS, "OSRSetNormProjParm", OGRERR_FAILURE);
6209
6210
0
    return ToPointer(hSRS)->SetNormProjParm(pszParamName, dfValue);
6211
0
}
6212
6213
/************************************************************************/
6214
/*                               SetTM()                                */
6215
/************************************************************************/
6216
6217
OGRErr OGRSpatialReference::SetTM(double dfCenterLat, double dfCenterLong,
6218
                                  double dfScale, double dfFalseEasting,
6219
                                  double dfFalseNorthing)
6220
6221
0
{
6222
0
    TAKE_OPTIONAL_LOCK();
6223
6224
0
    return d->replaceConversionAndUnref(
6225
0
        proj_create_conversion_transverse_mercator(
6226
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6227
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6228
0
}
6229
6230
/************************************************************************/
6231
/*                              OSRSetTM()                              */
6232
/************************************************************************/
6233
6234
OGRErr OSRSetTM(OGRSpatialReferenceH hSRS, double dfCenterLat,
6235
                double dfCenterLong, double dfScale, double dfFalseEasting,
6236
                double dfFalseNorthing)
6237
6238
0
{
6239
0
    VALIDATE_POINTER1(hSRS, "OSRSetTM", OGRERR_FAILURE);
6240
6241
0
    return ToPointer(hSRS)->SetTM(dfCenterLat, dfCenterLong, dfScale,
6242
0
                                  dfFalseEasting, dfFalseNorthing);
6243
0
}
6244
6245
/************************************************************************/
6246
/*                            SetTMVariant()                            */
6247
/************************************************************************/
6248
6249
OGRErr OGRSpatialReference::SetTMVariant(const char *pszVariantName,
6250
                                         double dfCenterLat,
6251
                                         double dfCenterLong, double dfScale,
6252
                                         double dfFalseEasting,
6253
                                         double dfFalseNorthing)
6254
6255
0
{
6256
0
    TAKE_OPTIONAL_LOCK();
6257
6258
0
    SetProjection(pszVariantName);
6259
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6260
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6261
0
    SetNormProjParm(SRS_PP_SCALE_FACTOR, dfScale);
6262
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6263
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6264
6265
0
    return OGRERR_NONE;
6266
0
}
6267
6268
/************************************************************************/
6269
/*                          OSRSetTMVariant()                           */
6270
/************************************************************************/
6271
6272
OGRErr OSRSetTMVariant(OGRSpatialReferenceH hSRS, const char *pszVariantName,
6273
                       double dfCenterLat, double dfCenterLong, double dfScale,
6274
                       double dfFalseEasting, double dfFalseNorthing)
6275
6276
0
{
6277
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMVariant", OGRERR_FAILURE);
6278
6279
0
    return ToPointer(hSRS)->SetTMVariant(pszVariantName, dfCenterLat,
6280
0
                                         dfCenterLong, dfScale, dfFalseEasting,
6281
0
                                         dfFalseNorthing);
6282
0
}
6283
6284
/************************************************************************/
6285
/*                              SetTMSO()                               */
6286
/************************************************************************/
6287
6288
OGRErr OGRSpatialReference::SetTMSO(double dfCenterLat, double dfCenterLong,
6289
                                    double dfScale, double dfFalseEasting,
6290
                                    double dfFalseNorthing)
6291
6292
0
{
6293
0
    TAKE_OPTIONAL_LOCK();
6294
6295
0
    auto conv = proj_create_conversion_transverse_mercator_south_oriented(
6296
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale, dfFalseEasting,
6297
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6298
6299
0
    const char *pszName = nullptr;
6300
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
6301
0
    CPLString osName = pszName ? pszName : "";
6302
6303
0
    d->refreshProjObj();
6304
6305
0
    d->demoteFromBoundCRS();
6306
6307
0
    auto cs = proj_create_cartesian_2D_cs(
6308
0
        d->getPROJContext(), PJ_CART2D_WESTING_SOUTHING,
6309
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
6310
0
    auto projCRS =
6311
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
6312
0
                                  d->getGeodBaseCRS(), conv, cs);
6313
0
    proj_destroy(conv);
6314
0
    proj_destroy(cs);
6315
6316
0
    d->setPjCRS(projCRS);
6317
6318
0
    d->undoDemoteFromBoundCRS();
6319
6320
0
    return OGRERR_NONE;
6321
0
}
6322
6323
/************************************************************************/
6324
/*                             OSRSetTMSO()                             */
6325
/************************************************************************/
6326
6327
OGRErr OSRSetTMSO(OGRSpatialReferenceH hSRS, double dfCenterLat,
6328
                  double dfCenterLong, double dfScale, double dfFalseEasting,
6329
                  double dfFalseNorthing)
6330
6331
0
{
6332
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMSO", OGRERR_FAILURE);
6333
6334
0
    return ToPointer(hSRS)->SetTMSO(dfCenterLat, dfCenterLong, dfScale,
6335
0
                                    dfFalseEasting, dfFalseNorthing);
6336
0
}
6337
6338
/************************************************************************/
6339
/*                              SetTPED()                               */
6340
/************************************************************************/
6341
6342
OGRErr OGRSpatialReference::SetTPED(double dfLat1, double dfLong1,
6343
                                    double dfLat2, double dfLong2,
6344
                                    double dfFalseEasting,
6345
                                    double dfFalseNorthing)
6346
6347
0
{
6348
0
    TAKE_OPTIONAL_LOCK();
6349
6350
0
    return d->replaceConversionAndUnref(
6351
0
        proj_create_conversion_two_point_equidistant(
6352
0
            d->getPROJContext(), dfLat1, dfLong1, dfLat2, dfLong2,
6353
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6354
0
}
6355
6356
/************************************************************************/
6357
/*                             OSRSetTPED()                             */
6358
/************************************************************************/
6359
6360
OGRErr OSRSetTPED(OGRSpatialReferenceH hSRS, double dfLat1, double dfLong1,
6361
                  double dfLat2, double dfLong2, double dfFalseEasting,
6362
                  double dfFalseNorthing)
6363
6364
0
{
6365
0
    VALIDATE_POINTER1(hSRS, "OSRSetTPED", OGRERR_FAILURE);
6366
6367
0
    return ToPointer(hSRS)->SetTPED(dfLat1, dfLong1, dfLat2, dfLong2,
6368
0
                                    dfFalseEasting, dfFalseNorthing);
6369
0
}
6370
6371
/************************************************************************/
6372
/*                               SetTMG()                               */
6373
/************************************************************************/
6374
6375
OGRErr OGRSpatialReference::SetTMG(double dfCenterLat, double dfCenterLong,
6376
                                   double dfFalseEasting,
6377
                                   double dfFalseNorthing)
6378
6379
0
{
6380
0
    TAKE_OPTIONAL_LOCK();
6381
6382
0
    return d->replaceConversionAndUnref(
6383
0
        proj_create_conversion_tunisia_mapping_grid(
6384
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6385
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6386
0
}
6387
6388
/************************************************************************/
6389
/*                             OSRSetTMG()                              */
6390
/************************************************************************/
6391
6392
OGRErr OSRSetTMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
6393
                 double dfCenterLong, double dfFalseEasting,
6394
                 double dfFalseNorthing)
6395
6396
0
{
6397
0
    VALIDATE_POINTER1(hSRS, "OSRSetTMG", OGRERR_FAILURE);
6398
6399
0
    return ToPointer(hSRS)->SetTMG(dfCenterLat, dfCenterLong, dfFalseEasting,
6400
0
                                   dfFalseNorthing);
6401
0
}
6402
6403
/************************************************************************/
6404
/*                              SetACEA()                               */
6405
/************************************************************************/
6406
6407
OGRErr OGRSpatialReference::SetACEA(double dfStdP1, double dfStdP2,
6408
                                    double dfCenterLat, double dfCenterLong,
6409
                                    double dfFalseEasting,
6410
                                    double dfFalseNorthing)
6411
6412
0
{
6413
0
    TAKE_OPTIONAL_LOCK();
6414
6415
    // Note different order of parameters. The one in PROJ is conformant with
6416
    // EPSG
6417
0
    return d->replaceConversionAndUnref(
6418
0
        proj_create_conversion_albers_equal_area(
6419
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6420
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6421
0
}
6422
6423
/************************************************************************/
6424
/*                             OSRSetACEA()                             */
6425
/************************************************************************/
6426
6427
OGRErr OSRSetACEA(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6428
                  double dfCenterLat, double dfCenterLong,
6429
                  double dfFalseEasting, double dfFalseNorthing)
6430
6431
0
{
6432
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6433
6434
0
    return ToPointer(hSRS)->SetACEA(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6435
0
                                    dfFalseEasting, dfFalseNorthing);
6436
0
}
6437
6438
/************************************************************************/
6439
/*                               SetAE()                                */
6440
/************************************************************************/
6441
6442
OGRErr OGRSpatialReference::SetAE(double dfCenterLat, double dfCenterLong,
6443
                                  double dfFalseEasting, double dfFalseNorthing)
6444
6445
0
{
6446
0
    TAKE_OPTIONAL_LOCK();
6447
6448
0
    return d->replaceConversionAndUnref(
6449
0
        proj_create_conversion_azimuthal_equidistant(
6450
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6451
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6452
0
}
6453
6454
/************************************************************************/
6455
/*                              OSRSetAE()                              */
6456
/************************************************************************/
6457
6458
OGRErr OSRSetAE(OGRSpatialReferenceH hSRS, double dfCenterLat,
6459
                double dfCenterLong, double dfFalseEasting,
6460
                double dfFalseNorthing)
6461
6462
0
{
6463
0
    VALIDATE_POINTER1(hSRS, "OSRSetACEA", OGRERR_FAILURE);
6464
6465
0
    return ToPointer(hSRS)->SetAE(dfCenterLat, dfCenterLong, dfFalseEasting,
6466
0
                                  dfFalseNorthing);
6467
0
}
6468
6469
/************************************************************************/
6470
/*                              SetBonne()                              */
6471
/************************************************************************/
6472
6473
OGRErr OGRSpatialReference::SetBonne(double dfStdP1, double dfCentralMeridian,
6474
                                     double dfFalseEasting,
6475
                                     double dfFalseNorthing)
6476
6477
0
{
6478
0
    TAKE_OPTIONAL_LOCK();
6479
6480
0
    return d->replaceConversionAndUnref(proj_create_conversion_bonne(
6481
0
        d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6482
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6483
0
}
6484
6485
/************************************************************************/
6486
/*                            OSRSetBonne()                             */
6487
/************************************************************************/
6488
6489
OGRErr OSRSetBonne(OGRSpatialReferenceH hSRS, double dfStdP1,
6490
                   double dfCentralMeridian, double dfFalseEasting,
6491
                   double dfFalseNorthing)
6492
6493
0
{
6494
0
    VALIDATE_POINTER1(hSRS, "OSRSetBonne", OGRERR_FAILURE);
6495
6496
0
    return ToPointer(hSRS)->SetBonne(dfStdP1, dfCentralMeridian, dfFalseEasting,
6497
0
                                     dfFalseNorthing);
6498
0
}
6499
6500
/************************************************************************/
6501
/*                               SetCEA()                               */
6502
/************************************************************************/
6503
6504
OGRErr OGRSpatialReference::SetCEA(double dfStdP1, double dfCentralMeridian,
6505
                                   double dfFalseEasting,
6506
                                   double dfFalseNorthing)
6507
6508
0
{
6509
0
    TAKE_OPTIONAL_LOCK();
6510
6511
0
    return d->replaceConversionAndUnref(
6512
0
        proj_create_conversion_lambert_cylindrical_equal_area(
6513
0
            d->getPROJContext(), dfStdP1, dfCentralMeridian, dfFalseEasting,
6514
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6515
0
}
6516
6517
/************************************************************************/
6518
/*                             OSRSetCEA()                              */
6519
/************************************************************************/
6520
6521
OGRErr OSRSetCEA(OGRSpatialReferenceH hSRS, double dfStdP1,
6522
                 double dfCentralMeridian, double dfFalseEasting,
6523
                 double dfFalseNorthing)
6524
6525
0
{
6526
0
    VALIDATE_POINTER1(hSRS, "OSRSetCEA", OGRERR_FAILURE);
6527
6528
0
    return ToPointer(hSRS)->SetCEA(dfStdP1, dfCentralMeridian, dfFalseEasting,
6529
0
                                   dfFalseNorthing);
6530
0
}
6531
6532
/************************************************************************/
6533
/*                               SetCS()                                */
6534
/************************************************************************/
6535
6536
OGRErr OGRSpatialReference::SetCS(double dfCenterLat, double dfCenterLong,
6537
                                  double dfFalseEasting, double dfFalseNorthing)
6538
6539
0
{
6540
0
    TAKE_OPTIONAL_LOCK();
6541
6542
0
    return d->replaceConversionAndUnref(proj_create_conversion_cassini_soldner(
6543
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6544
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6545
0
}
6546
6547
/************************************************************************/
6548
/*                              OSRSetCS()                              */
6549
/************************************************************************/
6550
6551
OGRErr OSRSetCS(OGRSpatialReferenceH hSRS, double dfCenterLat,
6552
                double dfCenterLong, double dfFalseEasting,
6553
                double dfFalseNorthing)
6554
6555
0
{
6556
0
    VALIDATE_POINTER1(hSRS, "OSRSetCS", OGRERR_FAILURE);
6557
6558
0
    return ToPointer(hSRS)->SetCS(dfCenterLat, dfCenterLong, dfFalseEasting,
6559
0
                                  dfFalseNorthing);
6560
0
}
6561
6562
/************************************************************************/
6563
/*                               SetEC()                                */
6564
/************************************************************************/
6565
6566
OGRErr OGRSpatialReference::SetEC(double dfStdP1, double dfStdP2,
6567
                                  double dfCenterLat, double dfCenterLong,
6568
                                  double dfFalseEasting, double dfFalseNorthing)
6569
6570
0
{
6571
0
    TAKE_OPTIONAL_LOCK();
6572
6573
    // Note: different order of arguments
6574
0
    return d->replaceConversionAndUnref(
6575
0
        proj_create_conversion_equidistant_conic(
6576
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
6577
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6578
0
}
6579
6580
/************************************************************************/
6581
/*                              OSRSetEC()                              */
6582
/************************************************************************/
6583
6584
OGRErr OSRSetEC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
6585
                double dfCenterLat, double dfCenterLong, double dfFalseEasting,
6586
                double dfFalseNorthing)
6587
6588
0
{
6589
0
    VALIDATE_POINTER1(hSRS, "OSRSetEC", OGRERR_FAILURE);
6590
6591
0
    return ToPointer(hSRS)->SetEC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
6592
0
                                  dfFalseEasting, dfFalseNorthing);
6593
0
}
6594
6595
/************************************************************************/
6596
/*                             SetEckert()                              */
6597
/************************************************************************/
6598
6599
OGRErr OGRSpatialReference::SetEckert(int nVariation,  // 1-6.
6600
                                      double dfCentralMeridian,
6601
                                      double dfFalseEasting,
6602
                                      double dfFalseNorthing)
6603
6604
0
{
6605
0
    TAKE_OPTIONAL_LOCK();
6606
6607
0
    PJ *conv;
6608
0
    if (nVariation == 1)
6609
0
    {
6610
0
        conv = proj_create_conversion_eckert_i(
6611
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6612
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6613
0
    }
6614
0
    else if (nVariation == 2)
6615
0
    {
6616
0
        conv = proj_create_conversion_eckert_ii(
6617
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6618
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6619
0
    }
6620
0
    else if (nVariation == 3)
6621
0
    {
6622
0
        conv = proj_create_conversion_eckert_iii(
6623
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6624
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6625
0
    }
6626
0
    else if (nVariation == 4)
6627
0
    {
6628
0
        conv = proj_create_conversion_eckert_iv(
6629
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6630
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6631
0
    }
6632
0
    else if (nVariation == 5)
6633
0
    {
6634
0
        conv = proj_create_conversion_eckert_v(
6635
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6636
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6637
0
    }
6638
0
    else if (nVariation == 6)
6639
0
    {
6640
0
        conv = proj_create_conversion_eckert_vi(
6641
0
            d->getPROJContext(), dfCentralMeridian, dfFalseEasting,
6642
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
6643
0
    }
6644
0
    else
6645
0
    {
6646
0
        CPLError(CE_Failure, CPLE_AppDefined,
6647
0
                 "Unsupported Eckert variation (%d).", nVariation);
6648
0
        return OGRERR_UNSUPPORTED_SRS;
6649
0
    }
6650
6651
0
    return d->replaceConversionAndUnref(conv);
6652
0
}
6653
6654
/************************************************************************/
6655
/*                            OSRSetEckert()                            */
6656
/************************************************************************/
6657
6658
OGRErr OSRSetEckert(OGRSpatialReferenceH hSRS, int nVariation,
6659
                    double dfCentralMeridian, double dfFalseEasting,
6660
                    double dfFalseNorthing)
6661
6662
0
{
6663
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckert", OGRERR_FAILURE);
6664
6665
0
    return ToPointer(hSRS)->SetEckert(nVariation, dfCentralMeridian,
6666
0
                                      dfFalseEasting, dfFalseNorthing);
6667
0
}
6668
6669
/************************************************************************/
6670
/*                            SetEckertIV()                             */
6671
/*                                                                      */
6672
/*      Deprecated                                                      */
6673
/************************************************************************/
6674
6675
OGRErr OGRSpatialReference::SetEckertIV(double dfCentralMeridian,
6676
                                        double dfFalseEasting,
6677
                                        double dfFalseNorthing)
6678
6679
0
{
6680
0
    return SetEckert(4, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6681
0
}
6682
6683
/************************************************************************/
6684
/*                           OSRSetEckertIV()                           */
6685
/************************************************************************/
6686
6687
OGRErr OSRSetEckertIV(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6688
                      double dfFalseEasting, double dfFalseNorthing)
6689
6690
0
{
6691
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertIV", OGRERR_FAILURE);
6692
6693
0
    return ToPointer(hSRS)->SetEckertIV(dfCentralMeridian, dfFalseEasting,
6694
0
                                        dfFalseNorthing);
6695
0
}
6696
6697
/************************************************************************/
6698
/*                            SetEckertVI()                             */
6699
/*                                                                      */
6700
/*      Deprecated                                                      */
6701
/************************************************************************/
6702
6703
OGRErr OGRSpatialReference::SetEckertVI(double dfCentralMeridian,
6704
                                        double dfFalseEasting,
6705
                                        double dfFalseNorthing)
6706
6707
0
{
6708
0
    return SetEckert(6, dfCentralMeridian, dfFalseEasting, dfFalseNorthing);
6709
0
}
6710
6711
/************************************************************************/
6712
/*                           OSRSetEckertVI()                           */
6713
/************************************************************************/
6714
6715
OGRErr OSRSetEckertVI(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6716
                      double dfFalseEasting, double dfFalseNorthing)
6717
6718
0
{
6719
0
    VALIDATE_POINTER1(hSRS, "OSRSetEckertVI", OGRERR_FAILURE);
6720
6721
0
    return ToPointer(hSRS)->SetEckertVI(dfCentralMeridian, dfFalseEasting,
6722
0
                                        dfFalseNorthing);
6723
0
}
6724
6725
/************************************************************************/
6726
/*                         SetEquirectangular()                         */
6727
/************************************************************************/
6728
6729
OGRErr OGRSpatialReference::SetEquirectangular(double dfCenterLat,
6730
                                               double dfCenterLong,
6731
                                               double dfFalseEasting,
6732
                                               double dfFalseNorthing)
6733
6734
0
{
6735
0
    TAKE_OPTIONAL_LOCK();
6736
6737
0
    if (dfCenterLat == 0.0)
6738
0
    {
6739
0
        return d->replaceConversionAndUnref(
6740
0
            proj_create_conversion_equidistant_cylindrical(
6741
0
                d->getPROJContext(), 0.0, dfCenterLong, dfFalseEasting,
6742
0
                dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6743
0
    }
6744
6745
    // Non-standard extension with non-zero latitude of origin
6746
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6747
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6748
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6749
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6750
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6751
6752
0
    return OGRERR_NONE;
6753
0
}
6754
6755
/************************************************************************/
6756
/*                       OSRSetEquirectangular()                        */
6757
/************************************************************************/
6758
6759
OGRErr OSRSetEquirectangular(OGRSpatialReferenceH hSRS, double dfCenterLat,
6760
                             double dfCenterLong, double dfFalseEasting,
6761
                             double dfFalseNorthing)
6762
6763
0
{
6764
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular", OGRERR_FAILURE);
6765
6766
0
    return ToPointer(hSRS)->SetEquirectangular(dfCenterLat, dfCenterLong,
6767
0
                                               dfFalseEasting, dfFalseNorthing);
6768
0
}
6769
6770
/************************************************************************/
6771
/*                         SetEquirectangular2()                        */
6772
/* Generalized form                                                     */
6773
/************************************************************************/
6774
6775
OGRErr OGRSpatialReference::SetEquirectangular2(double dfCenterLat,
6776
                                                double dfCenterLong,
6777
                                                double dfStdParallel1,
6778
                                                double dfFalseEasting,
6779
                                                double dfFalseNorthing)
6780
6781
0
{
6782
0
    TAKE_OPTIONAL_LOCK();
6783
6784
0
    if (dfCenterLat == 0.0)
6785
0
    {
6786
0
        return d->replaceConversionAndUnref(
6787
0
            proj_create_conversion_equidistant_cylindrical(
6788
0
                d->getPROJContext(), dfStdParallel1, dfCenterLong,
6789
0
                dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6790
0
    }
6791
6792
    // Non-standard extension with non-zero latitude of origin
6793
0
    SetProjection(SRS_PT_EQUIRECTANGULAR);
6794
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
6795
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
6796
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdParallel1);
6797
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
6798
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
6799
6800
0
    return OGRERR_NONE;
6801
0
}
6802
6803
/************************************************************************/
6804
/*                       OSRSetEquirectangular2()                       */
6805
/************************************************************************/
6806
6807
OGRErr OSRSetEquirectangular2(OGRSpatialReferenceH hSRS, double dfCenterLat,
6808
                              double dfCenterLong, double dfStdParallel1,
6809
                              double dfFalseEasting, double dfFalseNorthing)
6810
6811
0
{
6812
0
    VALIDATE_POINTER1(hSRS, "OSRSetEquirectangular2", OGRERR_FAILURE);
6813
6814
0
    return ToPointer(hSRS)->SetEquirectangular2(dfCenterLat, dfCenterLong,
6815
0
                                                dfStdParallel1, dfFalseEasting,
6816
0
                                                dfFalseNorthing);
6817
0
}
6818
6819
/************************************************************************/
6820
/*                               SetGS()                                */
6821
/************************************************************************/
6822
6823
OGRErr OGRSpatialReference::SetGS(double dfCentralMeridian,
6824
                                  double dfFalseEasting, double dfFalseNorthing)
6825
6826
0
{
6827
0
    return d->replaceConversionAndUnref(proj_create_conversion_gall(
6828
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6829
0
        nullptr, 0.0, nullptr, 0.0));
6830
0
}
6831
6832
/************************************************************************/
6833
/*                              OSRSetGS()                              */
6834
/************************************************************************/
6835
6836
OGRErr OSRSetGS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6837
                double dfFalseEasting, double dfFalseNorthing)
6838
6839
0
{
6840
0
    VALIDATE_POINTER1(hSRS, "OSRSetGS", OGRERR_FAILURE);
6841
6842
0
    return ToPointer(hSRS)->SetGS(dfCentralMeridian, dfFalseEasting,
6843
0
                                  dfFalseNorthing);
6844
0
}
6845
6846
/************************************************************************/
6847
/*                               SetGH()                                */
6848
/************************************************************************/
6849
6850
OGRErr OGRSpatialReference::SetGH(double dfCentralMeridian,
6851
                                  double dfFalseEasting, double dfFalseNorthing)
6852
6853
0
{
6854
0
    TAKE_OPTIONAL_LOCK();
6855
6856
0
    return d->replaceConversionAndUnref(proj_create_conversion_goode_homolosine(
6857
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
6858
0
        nullptr, 0.0, nullptr, 0.0));
6859
0
}
6860
6861
/************************************************************************/
6862
/*                              OSRSetGH()                              */
6863
/************************************************************************/
6864
6865
OGRErr OSRSetGH(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6866
                double dfFalseEasting, double dfFalseNorthing)
6867
6868
0
{
6869
0
    VALIDATE_POINTER1(hSRS, "OSRSetGH", OGRERR_FAILURE);
6870
6871
0
    return ToPointer(hSRS)->SetGH(dfCentralMeridian, dfFalseEasting,
6872
0
                                  dfFalseNorthing);
6873
0
}
6874
6875
/************************************************************************/
6876
/*                              SetIGH()                                */
6877
/************************************************************************/
6878
6879
OGRErr OGRSpatialReference::SetIGH()
6880
6881
0
{
6882
0
    TAKE_OPTIONAL_LOCK();
6883
6884
0
    return d->replaceConversionAndUnref(
6885
0
        proj_create_conversion_interrupted_goode_homolosine(
6886
0
            d->getPROJContext(), 0.0, 0.0, 0.0, nullptr, 0.0, nullptr, 0.0));
6887
0
}
6888
6889
/************************************************************************/
6890
/*                              OSRSetIGH()                             */
6891
/************************************************************************/
6892
6893
OGRErr OSRSetIGH(OGRSpatialReferenceH hSRS)
6894
6895
0
{
6896
0
    VALIDATE_POINTER1(hSRS, "OSRSetIGH", OGRERR_FAILURE);
6897
6898
0
    return ToPointer(hSRS)->SetIGH();
6899
0
}
6900
6901
/************************************************************************/
6902
/*                              SetGEOS()                               */
6903
/************************************************************************/
6904
6905
OGRErr OGRSpatialReference::SetGEOS(double dfCentralMeridian,
6906
                                    double dfSatelliteHeight,
6907
                                    double dfFalseEasting,
6908
                                    double dfFalseNorthing)
6909
6910
0
{
6911
0
    TAKE_OPTIONAL_LOCK();
6912
6913
0
    return d->replaceConversionAndUnref(
6914
0
        proj_create_conversion_geostationary_satellite_sweep_y(
6915
0
            d->getPROJContext(), dfCentralMeridian, dfSatelliteHeight,
6916
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6917
0
}
6918
6919
/************************************************************************/
6920
/*                              OSRSetGEOS()                             */
6921
/************************************************************************/
6922
6923
OGRErr OSRSetGEOS(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
6924
                  double dfSatelliteHeight, double dfFalseEasting,
6925
                  double dfFalseNorthing)
6926
6927
0
{
6928
0
    VALIDATE_POINTER1(hSRS, "OSRSetGEOS", OGRERR_FAILURE);
6929
6930
0
    return ToPointer(hSRS)->SetGEOS(dfCentralMeridian, dfSatelliteHeight,
6931
0
                                    dfFalseEasting, dfFalseNorthing);
6932
0
}
6933
6934
/************************************************************************/
6935
/*                       SetGaussSchreiberTMercator()                   */
6936
/************************************************************************/
6937
6938
OGRErr OGRSpatialReference::SetGaussSchreiberTMercator(double dfCenterLat,
6939
                                                       double dfCenterLong,
6940
                                                       double dfScale,
6941
                                                       double dfFalseEasting,
6942
                                                       double dfFalseNorthing)
6943
6944
0
{
6945
0
    TAKE_OPTIONAL_LOCK();
6946
6947
0
    return d->replaceConversionAndUnref(
6948
0
        proj_create_conversion_gauss_schreiber_transverse_mercator(
6949
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
6950
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6951
0
}
6952
6953
/************************************************************************/
6954
/*                     OSRSetGaussSchreiberTMercator()                  */
6955
/************************************************************************/
6956
6957
OGRErr OSRSetGaussSchreiberTMercator(OGRSpatialReferenceH hSRS,
6958
                                     double dfCenterLat, double dfCenterLong,
6959
                                     double dfScale, double dfFalseEasting,
6960
                                     double dfFalseNorthing)
6961
6962
0
{
6963
0
    VALIDATE_POINTER1(hSRS, "OSRSetGaussSchreiberTMercator", OGRERR_FAILURE);
6964
6965
0
    return ToPointer(hSRS)->SetGaussSchreiberTMercator(
6966
0
        dfCenterLat, dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing);
6967
0
}
6968
6969
/************************************************************************/
6970
/*                            SetGnomonic()                             */
6971
/************************************************************************/
6972
6973
OGRErr OGRSpatialReference::SetGnomonic(double dfCenterLat, double dfCenterLong,
6974
                                        double dfFalseEasting,
6975
                                        double dfFalseNorthing)
6976
6977
0
{
6978
0
    TAKE_OPTIONAL_LOCK();
6979
6980
0
    return d->replaceConversionAndUnref(proj_create_conversion_gnomonic(
6981
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
6982
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
6983
0
}
6984
6985
/************************************************************************/
6986
/*                           OSRSetGnomonic()                           */
6987
/************************************************************************/
6988
6989
OGRErr OSRSetGnomonic(OGRSpatialReferenceH hSRS, double dfCenterLat,
6990
                      double dfCenterLong, double dfFalseEasting,
6991
                      double dfFalseNorthing)
6992
6993
0
{
6994
0
    VALIDATE_POINTER1(hSRS, "OSRSetGnomonic", OGRERR_FAILURE);
6995
6996
0
    return ToPointer(hSRS)->SetGnomonic(dfCenterLat, dfCenterLong,
6997
0
                                        dfFalseEasting, dfFalseNorthing);
6998
0
}
6999
7000
/************************************************************************/
7001
/*                              SetHOMAC()                              */
7002
/************************************************************************/
7003
7004
/**
7005
 * \brief Set an Hotine Oblique Mercator Azimuth Center projection using
7006
 * azimuth angle.
7007
 *
7008
 * This projection corresponds to EPSG projection method 9815, also
7009
 * sometimes known as hotine oblique mercator (variant B).
7010
 *
7011
 * This method does the same thing as the C function OSRSetHOMAC().
7012
 *
7013
 * @param dfCenterLat Latitude of the projection origin.
7014
 * @param dfCenterLong Longitude of the projection origin.
7015
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7016
 * centerline.
7017
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7018
 * @param dfScale Scale factor applies to the projection origin.
7019
 * @param dfFalseEasting False easting.
7020
 * @param dfFalseNorthing False northing.
7021
 *
7022
 * @return OGRERR_NONE on success.
7023
 */
7024
7025
OGRErr OGRSpatialReference::SetHOMAC(double dfCenterLat, double dfCenterLong,
7026
                                     double dfAzimuth, double dfRectToSkew,
7027
                                     double dfScale, double dfFalseEasting,
7028
                                     double dfFalseNorthing)
7029
7030
0
{
7031
0
    TAKE_OPTIONAL_LOCK();
7032
7033
0
    return d->replaceConversionAndUnref(
7034
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7035
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7036
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7037
0
            0.0, nullptr, 0.0));
7038
0
}
7039
7040
/************************************************************************/
7041
/*                            OSRSetHOMAC()                             */
7042
/************************************************************************/
7043
7044
/**
7045
 * \brief Set an Oblique Mercator projection using azimuth angle.
7046
 *
7047
 * This is the same as the C++ method OGRSpatialReference::SetHOMAC()
7048
 */
7049
OGRErr OSRSetHOMAC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7050
                   double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7051
                   double dfScale, double dfFalseEasting,
7052
                   double dfFalseNorthing)
7053
7054
0
{
7055
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOMAC", OGRERR_FAILURE);
7056
7057
0
    return ToPointer(hSRS)->SetHOMAC(dfCenterLat, dfCenterLong, dfAzimuth,
7058
0
                                     dfRectToSkew, dfScale, dfFalseEasting,
7059
0
                                     dfFalseNorthing);
7060
0
}
7061
7062
/************************************************************************/
7063
/*                               SetHOM()                               */
7064
/************************************************************************/
7065
7066
/**
7067
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7068
 *
7069
 * This projection corresponds to EPSG projection method 9812, also
7070
 * sometimes known as hotine oblique mercator (variant A)..
7071
 *
7072
 * This method does the same thing as the C function OSRSetHOM().
7073
 *
7074
 * @param dfCenterLat Latitude of the projection origin.
7075
 * @param dfCenterLong Longitude of the projection origin.
7076
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7077
 * centerline.
7078
 * @param dfRectToSkew Angle from Rectified to Skew Grid
7079
 * @param dfScale Scale factor applies to the projection origin.
7080
 * @param dfFalseEasting False easting.
7081
 * @param dfFalseNorthing False northing.
7082
 *
7083
 * @return OGRERR_NONE on success.
7084
 */
7085
7086
OGRErr OGRSpatialReference::SetHOM(double dfCenterLat, double dfCenterLong,
7087
                                   double dfAzimuth, double dfRectToSkew,
7088
                                   double dfScale, double dfFalseEasting,
7089
                                   double dfFalseNorthing)
7090
7091
0
{
7092
0
    TAKE_OPTIONAL_LOCK();
7093
7094
0
    return d->replaceConversionAndUnref(
7095
0
        proj_create_conversion_hotine_oblique_mercator_variant_a(
7096
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7097
0
            dfRectToSkew, dfScale, dfFalseEasting, dfFalseNorthing, nullptr,
7098
0
            0.0, nullptr, 0.0));
7099
0
}
7100
7101
/************************************************************************/
7102
/*                             OSRSetHOM()                              */
7103
/************************************************************************/
7104
/**
7105
 * \brief Set a Hotine Oblique Mercator projection using azimuth angle.
7106
 *
7107
 * This is the same as the C++ method OGRSpatialReference::SetHOM()
7108
 */
7109
OGRErr OSRSetHOM(OGRSpatialReferenceH hSRS, double dfCenterLat,
7110
                 double dfCenterLong, double dfAzimuth, double dfRectToSkew,
7111
                 double dfScale, double dfFalseEasting, double dfFalseNorthing)
7112
7113
0
{
7114
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM", OGRERR_FAILURE);
7115
7116
0
    return ToPointer(hSRS)->SetHOM(dfCenterLat, dfCenterLong, dfAzimuth,
7117
0
                                   dfRectToSkew, dfScale, dfFalseEasting,
7118
0
                                   dfFalseNorthing);
7119
0
}
7120
7121
/************************************************************************/
7122
/*                             SetHOM2PNO()                             */
7123
/************************************************************************/
7124
7125
/**
7126
 * \brief Set a Hotine Oblique Mercator projection using two points on
7127
 * projection centerline.
7128
 *
7129
 * This method does the same thing as the C function OSRSetHOM2PNO().
7130
 *
7131
 * @param dfCenterLat Latitude of the projection origin.
7132
 * @param dfLat1 Latitude of the first point on center line.
7133
 * @param dfLong1 Longitude of the first point on center line.
7134
 * @param dfLat2 Latitude of the second point on center line.
7135
 * @param dfLong2 Longitude of the second point on center line.
7136
 * @param dfScale Scale factor applies to the projection origin.
7137
 * @param dfFalseEasting False easting.
7138
 * @param dfFalseNorthing False northing.
7139
 *
7140
 * @return OGRERR_NONE on success.
7141
 */
7142
7143
OGRErr OGRSpatialReference::SetHOM2PNO(double dfCenterLat, double dfLat1,
7144
                                       double dfLong1, double dfLat2,
7145
                                       double dfLong2, double dfScale,
7146
                                       double dfFalseEasting,
7147
                                       double dfFalseNorthing)
7148
7149
0
{
7150
0
    TAKE_OPTIONAL_LOCK();
7151
7152
0
    return d->replaceConversionAndUnref(
7153
0
        proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
7154
0
            d->getPROJContext(), dfCenterLat, dfLat1, dfLong1, dfLat2, dfLong2,
7155
0
            dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7156
0
            0.0));
7157
0
}
7158
7159
/************************************************************************/
7160
/*                           OSRSetHOM2PNO()                            */
7161
/************************************************************************/
7162
/**
7163
 * \brief Set a Hotine Oblique Mercator projection using two points on
7164
 *  projection centerline.
7165
 *
7166
 * This is the same as the C++ method OGRSpatialReference::SetHOM2PNO()
7167
 */
7168
OGRErr OSRSetHOM2PNO(OGRSpatialReferenceH hSRS, double dfCenterLat,
7169
                     double dfLat1, double dfLong1, double dfLat2,
7170
                     double dfLong2, double dfScale, double dfFalseEasting,
7171
                     double dfFalseNorthing)
7172
7173
0
{
7174
0
    VALIDATE_POINTER1(hSRS, "OSRSetHOM2PNO", OGRERR_FAILURE);
7175
7176
0
    return ToPointer(hSRS)->SetHOM2PNO(dfCenterLat, dfLat1, dfLong1, dfLat2,
7177
0
                                       dfLong2, dfScale, dfFalseEasting,
7178
0
                                       dfFalseNorthing);
7179
0
}
7180
7181
/************************************************************************/
7182
/*                               SetLOM()                               */
7183
/************************************************************************/
7184
7185
/**
7186
 * \brief Set a Laborde Oblique Mercator projection.
7187
 *
7188
 * @param dfCenterLat Latitude of the projection origin.
7189
 * @param dfCenterLong Longitude of the projection origin.
7190
 * @param dfAzimuth Azimuth, measured clockwise from North, of the projection
7191
 * centerline.
7192
 * @param dfScale Scale factor on the initiali line
7193
 * @param dfFalseEasting False easting.
7194
 * @param dfFalseNorthing False northing.
7195
 *
7196
 * @return OGRERR_NONE on success.
7197
 */
7198
7199
OGRErr OGRSpatialReference::SetLOM(double dfCenterLat, double dfCenterLong,
7200
                                   double dfAzimuth, double dfScale,
7201
                                   double dfFalseEasting,
7202
                                   double dfFalseNorthing)
7203
7204
0
{
7205
0
    TAKE_OPTIONAL_LOCK();
7206
7207
0
    return d->replaceConversionAndUnref(
7208
0
        proj_create_conversion_laborde_oblique_mercator(
7209
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth, dfScale,
7210
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7211
0
}
7212
7213
/************************************************************************/
7214
/*                            SetIWMPolyconic()                         */
7215
/************************************************************************/
7216
7217
OGRErr OGRSpatialReference::SetIWMPolyconic(double dfLat1, double dfLat2,
7218
                                            double dfCenterLong,
7219
                                            double dfFalseEasting,
7220
                                            double dfFalseNorthing)
7221
7222
0
{
7223
0
    TAKE_OPTIONAL_LOCK();
7224
7225
0
    return d->replaceConversionAndUnref(
7226
0
        proj_create_conversion_international_map_world_polyconic(
7227
0
            d->getPROJContext(), dfCenterLong, dfLat1, dfLat2, dfFalseEasting,
7228
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0));
7229
0
}
7230
7231
/************************************************************************/
7232
/*                          OSRSetIWMPolyconic()                        */
7233
/************************************************************************/
7234
7235
OGRErr OSRSetIWMPolyconic(OGRSpatialReferenceH hSRS, double dfLat1,
7236
                          double dfLat2, double dfCenterLong,
7237
                          double dfFalseEasting, double dfFalseNorthing)
7238
7239
0
{
7240
0
    VALIDATE_POINTER1(hSRS, "OSRSetIWMPolyconic", OGRERR_FAILURE);
7241
7242
0
    return ToPointer(hSRS)->SetIWMPolyconic(dfLat1, dfLat2, dfCenterLong,
7243
0
                                            dfFalseEasting, dfFalseNorthing);
7244
0
}
7245
7246
/************************************************************************/
7247
/*                             SetKrovak()                              */
7248
/************************************************************************/
7249
7250
/** Krovak east-north projection.
7251
 *
7252
 * Note that dfAzimuth and dfPseudoStdParallel1 are ignored when exporting
7253
 * to PROJ and should be respectively set to 30.28813972222222 and 78.5
7254
 */
7255
OGRErr OGRSpatialReference::SetKrovak(double dfCenterLat, double dfCenterLong,
7256
                                      double dfAzimuth,
7257
                                      double dfPseudoStdParallel1,
7258
                                      double dfScale, double dfFalseEasting,
7259
                                      double dfFalseNorthing)
7260
7261
0
{
7262
0
    TAKE_OPTIONAL_LOCK();
7263
7264
0
    return d->replaceConversionAndUnref(
7265
0
        proj_create_conversion_krovak_north_oriented(
7266
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfAzimuth,
7267
0
            dfPseudoStdParallel1, dfScale, dfFalseEasting, dfFalseNorthing,
7268
0
            nullptr, 0.0, nullptr, 0.0));
7269
0
}
7270
7271
/************************************************************************/
7272
/*                            OSRSetKrovak()                            */
7273
/************************************************************************/
7274
7275
OGRErr OSRSetKrovak(OGRSpatialReferenceH hSRS, double dfCenterLat,
7276
                    double dfCenterLong, double dfAzimuth,
7277
                    double dfPseudoStdParallel1, double dfScale,
7278
                    double dfFalseEasting, double dfFalseNorthing)
7279
7280
0
{
7281
0
    VALIDATE_POINTER1(hSRS, "OSRSetKrovak", OGRERR_FAILURE);
7282
7283
0
    return ToPointer(hSRS)->SetKrovak(dfCenterLat, dfCenterLong, dfAzimuth,
7284
0
                                      dfPseudoStdParallel1, dfScale,
7285
0
                                      dfFalseEasting, dfFalseNorthing);
7286
0
}
7287
7288
/************************************************************************/
7289
/*                              SetLAEA()                               */
7290
/************************************************************************/
7291
7292
OGRErr OGRSpatialReference::SetLAEA(double dfCenterLat, double dfCenterLong,
7293
                                    double dfFalseEasting,
7294
                                    double dfFalseNorthing)
7295
7296
0
{
7297
0
    TAKE_OPTIONAL_LOCK();
7298
7299
0
    auto conv = proj_create_conversion_lambert_azimuthal_equal_area(
7300
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7301
0
        dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
7302
7303
0
    const char *pszName = nullptr;
7304
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7305
0
    CPLString osName = pszName ? pszName : "";
7306
7307
0
    d->refreshProjObj();
7308
7309
0
    d->demoteFromBoundCRS();
7310
7311
0
    auto cs = proj_create_cartesian_2D_cs(
7312
0
        d->getPROJContext(),
7313
0
        std::fabs(dfCenterLat - 90) < 1e-10 && dfCenterLong == 0
7314
0
            ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7315
0
        : std::fabs(dfCenterLat - -90) < 1e-10 && dfCenterLong == 0
7316
0
            ? PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH
7317
0
            : PJ_CART2D_EASTING_NORTHING,
7318
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7319
0
    auto projCRS =
7320
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7321
0
                                  d->getGeodBaseCRS(), conv, cs);
7322
0
    proj_destroy(conv);
7323
0
    proj_destroy(cs);
7324
7325
0
    d->setPjCRS(projCRS);
7326
7327
0
    d->undoDemoteFromBoundCRS();
7328
7329
0
    return OGRERR_NONE;
7330
0
}
7331
7332
/************************************************************************/
7333
/*                             OSRSetLAEA()                             */
7334
/************************************************************************/
7335
7336
OGRErr OSRSetLAEA(OGRSpatialReferenceH hSRS, double dfCenterLat,
7337
                  double dfCenterLong, double dfFalseEasting,
7338
                  double dfFalseNorthing)
7339
7340
0
{
7341
0
    VALIDATE_POINTER1(hSRS, "OSRSetLAEA", OGRERR_FAILURE);
7342
7343
0
    return ToPointer(hSRS)->SetLAEA(dfCenterLat, dfCenterLong, dfFalseEasting,
7344
0
                                    dfFalseNorthing);
7345
0
}
7346
7347
/************************************************************************/
7348
/*                               SetLCC()                               */
7349
/************************************************************************/
7350
7351
OGRErr OGRSpatialReference::SetLCC(double dfStdP1, double dfStdP2,
7352
                                   double dfCenterLat, double dfCenterLong,
7353
                                   double dfFalseEasting,
7354
                                   double dfFalseNorthing)
7355
7356
0
{
7357
0
    TAKE_OPTIONAL_LOCK();
7358
7359
0
    return d->replaceConversionAndUnref(
7360
0
        proj_create_conversion_lambert_conic_conformal_2sp(
7361
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7362
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7363
0
}
7364
7365
/************************************************************************/
7366
/*                             OSRSetLCC()                              */
7367
/************************************************************************/
7368
7369
OGRErr OSRSetLCC(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7370
                 double dfCenterLat, double dfCenterLong, double dfFalseEasting,
7371
                 double dfFalseNorthing)
7372
7373
0
{
7374
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC", OGRERR_FAILURE);
7375
7376
0
    return ToPointer(hSRS)->SetLCC(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7377
0
                                   dfFalseEasting, dfFalseNorthing);
7378
0
}
7379
7380
/************************************************************************/
7381
/*                             SetLCC1SP()                              */
7382
/************************************************************************/
7383
7384
OGRErr OGRSpatialReference::SetLCC1SP(double dfCenterLat, double dfCenterLong,
7385
                                      double dfScale, double dfFalseEasting,
7386
                                      double dfFalseNorthing)
7387
7388
0
{
7389
0
    TAKE_OPTIONAL_LOCK();
7390
7391
0
    return d->replaceConversionAndUnref(
7392
0
        proj_create_conversion_lambert_conic_conformal_1sp(
7393
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7394
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7395
0
}
7396
7397
/************************************************************************/
7398
/*                            OSRSetLCC1SP()                            */
7399
/************************************************************************/
7400
7401
OGRErr OSRSetLCC1SP(OGRSpatialReferenceH hSRS, double dfCenterLat,
7402
                    double dfCenterLong, double dfScale, double dfFalseEasting,
7403
                    double dfFalseNorthing)
7404
7405
0
{
7406
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCC1SP", OGRERR_FAILURE);
7407
7408
0
    return ToPointer(hSRS)->SetLCC1SP(dfCenterLat, dfCenterLong, dfScale,
7409
0
                                      dfFalseEasting, dfFalseNorthing);
7410
0
}
7411
7412
/************************************************************************/
7413
/*                              SetLCCB()                               */
7414
/************************************************************************/
7415
7416
OGRErr OGRSpatialReference::SetLCCB(double dfStdP1, double dfStdP2,
7417
                                    double dfCenterLat, double dfCenterLong,
7418
                                    double dfFalseEasting,
7419
                                    double dfFalseNorthing)
7420
7421
0
{
7422
0
    TAKE_OPTIONAL_LOCK();
7423
7424
0
    return d->replaceConversionAndUnref(
7425
0
        proj_create_conversion_lambert_conic_conformal_2sp_belgium(
7426
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfStdP1, dfStdP2,
7427
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7428
0
}
7429
7430
/************************************************************************/
7431
/*                             OSRSetLCCB()                             */
7432
/************************************************************************/
7433
7434
OGRErr OSRSetLCCB(OGRSpatialReferenceH hSRS, double dfStdP1, double dfStdP2,
7435
                  double dfCenterLat, double dfCenterLong,
7436
                  double dfFalseEasting, double dfFalseNorthing)
7437
7438
0
{
7439
0
    VALIDATE_POINTER1(hSRS, "OSRSetLCCB", OGRERR_FAILURE);
7440
7441
0
    return ToPointer(hSRS)->SetLCCB(dfStdP1, dfStdP2, dfCenterLat, dfCenterLong,
7442
0
                                    dfFalseEasting, dfFalseNorthing);
7443
0
}
7444
7445
/************************************************************************/
7446
/*                               SetMC()                                */
7447
/************************************************************************/
7448
7449
OGRErr OGRSpatialReference::SetMC(double dfCenterLat, double dfCenterLong,
7450
                                  double dfFalseEasting, double dfFalseNorthing)
7451
7452
0
{
7453
0
    TAKE_OPTIONAL_LOCK();
7454
7455
0
    (void)dfCenterLat;  // ignored
7456
7457
0
    return d->replaceConversionAndUnref(
7458
0
        proj_create_conversion_miller_cylindrical(
7459
0
            d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7460
0
            nullptr, 0, nullptr, 0));
7461
0
}
7462
7463
/************************************************************************/
7464
/*                              OSRSetMC()                              */
7465
/************************************************************************/
7466
7467
OGRErr OSRSetMC(OGRSpatialReferenceH hSRS, double dfCenterLat,
7468
                double dfCenterLong, double dfFalseEasting,
7469
                double dfFalseNorthing)
7470
7471
0
{
7472
0
    VALIDATE_POINTER1(hSRS, "OSRSetMC", OGRERR_FAILURE);
7473
7474
0
    return ToPointer(hSRS)->SetMC(dfCenterLat, dfCenterLong, dfFalseEasting,
7475
0
                                  dfFalseNorthing);
7476
0
}
7477
7478
/************************************************************************/
7479
/*                            SetMercator()                             */
7480
/************************************************************************/
7481
7482
OGRErr OGRSpatialReference::SetMercator(double dfCenterLat, double dfCenterLong,
7483
                                        double dfScale, double dfFalseEasting,
7484
                                        double dfFalseNorthing)
7485
7486
0
{
7487
0
    TAKE_OPTIONAL_LOCK();
7488
7489
0
    if (dfCenterLat != 0.0 && dfScale == 1.0)
7490
0
    {
7491
        // Not sure this is correct, but this is how it has been used
7492
        // historically
7493
0
        return SetMercator2SP(dfCenterLat, 0.0, dfCenterLong, dfFalseEasting,
7494
0
                              dfFalseNorthing);
7495
0
    }
7496
0
    return d->replaceConversionAndUnref(
7497
0
        proj_create_conversion_mercator_variant_a(
7498
0
            d->getPROJContext(),
7499
0
            dfCenterLat,  // should be zero
7500
0
            dfCenterLong, dfScale, dfFalseEasting, dfFalseNorthing, nullptr, 0,
7501
0
            nullptr, 0));
7502
0
}
7503
7504
/************************************************************************/
7505
/*                           OSRSetMercator()                           */
7506
/************************************************************************/
7507
7508
OGRErr OSRSetMercator(OGRSpatialReferenceH hSRS, double dfCenterLat,
7509
                      double dfCenterLong, double dfScale,
7510
                      double dfFalseEasting, double dfFalseNorthing)
7511
7512
0
{
7513
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator", OGRERR_FAILURE);
7514
7515
0
    return ToPointer(hSRS)->SetMercator(dfCenterLat, dfCenterLong, dfScale,
7516
0
                                        dfFalseEasting, dfFalseNorthing);
7517
0
}
7518
7519
/************************************************************************/
7520
/*                           SetMercator2SP()                           */
7521
/************************************************************************/
7522
7523
OGRErr OGRSpatialReference::SetMercator2SP(double dfStdP1, double dfCenterLat,
7524
                                           double dfCenterLong,
7525
                                           double dfFalseEasting,
7526
                                           double dfFalseNorthing)
7527
7528
0
{
7529
0
    if (dfCenterLat == 0.0)
7530
0
    {
7531
0
        return d->replaceConversionAndUnref(
7532
0
            proj_create_conversion_mercator_variant_b(
7533
0
                d->getPROJContext(), dfStdP1, dfCenterLong, dfFalseEasting,
7534
0
                dfFalseNorthing, nullptr, 0, nullptr, 0));
7535
0
    }
7536
7537
0
    TAKE_OPTIONAL_LOCK();
7538
7539
0
    SetProjection(SRS_PT_MERCATOR_2SP);
7540
7541
0
    SetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, dfStdP1);
7542
0
    SetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, dfCenterLat);
7543
0
    SetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, dfCenterLong);
7544
0
    SetNormProjParm(SRS_PP_FALSE_EASTING, dfFalseEasting);
7545
0
    SetNormProjParm(SRS_PP_FALSE_NORTHING, dfFalseNorthing);
7546
7547
0
    return OGRERR_NONE;
7548
0
}
7549
7550
/************************************************************************/
7551
/*                         OSRSetMercator2SP()                          */
7552
/************************************************************************/
7553
7554
OGRErr OSRSetMercator2SP(OGRSpatialReferenceH hSRS, double dfStdP1,
7555
                         double dfCenterLat, double dfCenterLong,
7556
                         double dfFalseEasting, double dfFalseNorthing)
7557
7558
0
{
7559
0
    VALIDATE_POINTER1(hSRS, "OSRSetMercator2SP", OGRERR_FAILURE);
7560
7561
0
    return ToPointer(hSRS)->SetMercator2SP(dfStdP1, dfCenterLat, dfCenterLong,
7562
0
                                           dfFalseEasting, dfFalseNorthing);
7563
0
}
7564
7565
/************************************************************************/
7566
/*                            SetMollweide()                            */
7567
/************************************************************************/
7568
7569
OGRErr OGRSpatialReference::SetMollweide(double dfCentralMeridian,
7570
                                         double dfFalseEasting,
7571
                                         double dfFalseNorthing)
7572
7573
0
{
7574
0
    TAKE_OPTIONAL_LOCK();
7575
7576
0
    return d->replaceConversionAndUnref(proj_create_conversion_mollweide(
7577
0
        d->getPROJContext(), dfCentralMeridian, dfFalseEasting, dfFalseNorthing,
7578
0
        nullptr, 0, nullptr, 0));
7579
0
}
7580
7581
/************************************************************************/
7582
/*                          OSRSetMollweide()                           */
7583
/************************************************************************/
7584
7585
OGRErr OSRSetMollweide(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7586
                       double dfFalseEasting, double dfFalseNorthing)
7587
7588
0
{
7589
0
    VALIDATE_POINTER1(hSRS, "OSRSetMollweide", OGRERR_FAILURE);
7590
7591
0
    return ToPointer(hSRS)->SetMollweide(dfCentralMeridian, dfFalseEasting,
7592
0
                                         dfFalseNorthing);
7593
0
}
7594
7595
/************************************************************************/
7596
/*                              SetNZMG()                               */
7597
/************************************************************************/
7598
7599
OGRErr OGRSpatialReference::SetNZMG(double dfCenterLat, double dfCenterLong,
7600
                                    double dfFalseEasting,
7601
                                    double dfFalseNorthing)
7602
7603
0
{
7604
0
    TAKE_OPTIONAL_LOCK();
7605
7606
0
    return d->replaceConversionAndUnref(
7607
0
        proj_create_conversion_new_zealand_mapping_grid(
7608
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7609
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7610
0
}
7611
7612
/************************************************************************/
7613
/*                             OSRSetNZMG()                             */
7614
/************************************************************************/
7615
7616
OGRErr OSRSetNZMG(OGRSpatialReferenceH hSRS, double dfCenterLat,
7617
                  double dfCenterLong, double dfFalseEasting,
7618
                  double dfFalseNorthing)
7619
7620
0
{
7621
0
    VALIDATE_POINTER1(hSRS, "OSRSetNZMG", OGRERR_FAILURE);
7622
7623
0
    return ToPointer(hSRS)->SetNZMG(dfCenterLat, dfCenterLong, dfFalseEasting,
7624
0
                                    dfFalseNorthing);
7625
0
}
7626
7627
/************************************************************************/
7628
/*                               SetOS()                                */
7629
/************************************************************************/
7630
7631
OGRErr OGRSpatialReference::SetOS(double dfOriginLat, double dfCMeridian,
7632
                                  double dfScale, double dfFalseEasting,
7633
                                  double dfFalseNorthing)
7634
7635
0
{
7636
0
    TAKE_OPTIONAL_LOCK();
7637
7638
0
    return d->replaceConversionAndUnref(
7639
0
        proj_create_conversion_oblique_stereographic(
7640
0
            d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale,
7641
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0));
7642
0
}
7643
7644
/************************************************************************/
7645
/*                              OSRSetOS()                              */
7646
/************************************************************************/
7647
7648
OGRErr OSRSetOS(OGRSpatialReferenceH hSRS, double dfOriginLat,
7649
                double dfCMeridian, double dfScale, double dfFalseEasting,
7650
                double dfFalseNorthing)
7651
7652
0
{
7653
0
    VALIDATE_POINTER1(hSRS, "OSRSetOS", OGRERR_FAILURE);
7654
7655
0
    return ToPointer(hSRS)->SetOS(dfOriginLat, dfCMeridian, dfScale,
7656
0
                                  dfFalseEasting, dfFalseNorthing);
7657
0
}
7658
7659
/************************************************************************/
7660
/*                          SetOrthographic()                           */
7661
/************************************************************************/
7662
7663
OGRErr OGRSpatialReference::SetOrthographic(double dfCenterLat,
7664
                                            double dfCenterLong,
7665
                                            double dfFalseEasting,
7666
                                            double dfFalseNorthing)
7667
7668
0
{
7669
0
    TAKE_OPTIONAL_LOCK();
7670
7671
0
    return d->replaceConversionAndUnref(proj_create_conversion_orthographic(
7672
0
        d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7673
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7674
0
}
7675
7676
/************************************************************************/
7677
/*                         OSRSetOrthographic()                         */
7678
/************************************************************************/
7679
7680
OGRErr OSRSetOrthographic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7681
                          double dfCenterLong, double dfFalseEasting,
7682
                          double dfFalseNorthing)
7683
7684
0
{
7685
0
    VALIDATE_POINTER1(hSRS, "OSRSetOrthographic", OGRERR_FAILURE);
7686
7687
0
    return ToPointer(hSRS)->SetOrthographic(dfCenterLat, dfCenterLong,
7688
0
                                            dfFalseEasting, dfFalseNorthing);
7689
0
}
7690
7691
/************************************************************************/
7692
/*                            SetPolyconic()                            */
7693
/************************************************************************/
7694
7695
OGRErr OGRSpatialReference::SetPolyconic(double dfCenterLat,
7696
                                         double dfCenterLong,
7697
                                         double dfFalseEasting,
7698
                                         double dfFalseNorthing)
7699
7700
0
{
7701
0
    TAKE_OPTIONAL_LOCK();
7702
7703
    // note: it seems that by some definitions this should include a
7704
    //       scale_factor parameter.
7705
0
    return d->replaceConversionAndUnref(
7706
0
        proj_create_conversion_american_polyconic(
7707
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7708
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
7709
0
}
7710
7711
/************************************************************************/
7712
/*                          OSRSetPolyconic()                           */
7713
/************************************************************************/
7714
7715
OGRErr OSRSetPolyconic(OGRSpatialReferenceH hSRS, double dfCenterLat,
7716
                       double dfCenterLong, double dfFalseEasting,
7717
                       double dfFalseNorthing)
7718
7719
0
{
7720
0
    VALIDATE_POINTER1(hSRS, "OSRSetPolyconic", OGRERR_FAILURE);
7721
7722
0
    return ToPointer(hSRS)->SetPolyconic(dfCenterLat, dfCenterLong,
7723
0
                                         dfFalseEasting, dfFalseNorthing);
7724
0
}
7725
7726
/************************************************************************/
7727
/*                               SetPS()                                */
7728
/************************************************************************/
7729
7730
/** Sets a Polar Stereographic projection.
7731
 *
7732
 * Two variants are possible:
7733
 * - Polar Stereographic Variant A: dfCenterLat must be +/- 90° and is
7734
 *   interpreted as the latitude of origin, combined with the scale factor
7735
 * - Polar Stereographic Variant B: dfCenterLat is different from +/- 90° and
7736
 *   is interpreted as the latitude of true scale. In that situation, dfScale
7737
 *   must be set to 1 (it is ignored in the projection parameters)
7738
 */
7739
OGRErr OGRSpatialReference::SetPS(double dfCenterLat, double dfCenterLong,
7740
                                  double dfScale, double dfFalseEasting,
7741
                                  double dfFalseNorthing)
7742
7743
0
{
7744
0
    TAKE_OPTIONAL_LOCK();
7745
7746
0
    PJ *conv;
7747
0
    if (dfScale == 1.0 && std::abs(std::abs(dfCenterLat) - 90) > 1e-8)
7748
0
    {
7749
0
        conv = proj_create_conversion_polar_stereographic_variant_b(
7750
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfFalseEasting,
7751
0
            dfFalseNorthing, nullptr, 0, nullptr, 0);
7752
0
    }
7753
0
    else
7754
0
    {
7755
0
        conv = proj_create_conversion_polar_stereographic_variant_a(
7756
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, dfScale,
7757
0
            dfFalseEasting, dfFalseNorthing, nullptr, 0, nullptr, 0);
7758
0
    }
7759
7760
0
    const char *pszName = nullptr;
7761
0
    double dfConvFactor = GetTargetLinearUnits(nullptr, &pszName);
7762
0
    CPLString osName = pszName ? pszName : "";
7763
7764
0
    d->refreshProjObj();
7765
7766
0
    d->demoteFromBoundCRS();
7767
7768
0
    auto cs = proj_create_cartesian_2D_cs(
7769
0
        d->getPROJContext(),
7770
0
        dfCenterLat > 0 ? PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH
7771
0
                        : PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH,
7772
0
        !osName.empty() ? osName.c_str() : nullptr, dfConvFactor);
7773
0
    auto projCRS =
7774
0
        proj_create_projected_crs(d->getPROJContext(), d->getProjCRSName(),
7775
0
                                  d->getGeodBaseCRS(), conv, cs);
7776
0
    proj_destroy(conv);
7777
0
    proj_destroy(cs);
7778
7779
0
    d->setPjCRS(projCRS);
7780
7781
0
    d->undoDemoteFromBoundCRS();
7782
7783
0
    return OGRERR_NONE;
7784
0
}
7785
7786
/************************************************************************/
7787
/*                              OSRSetPS()                              */
7788
/************************************************************************/
7789
7790
OGRErr OSRSetPS(OGRSpatialReferenceH hSRS, double dfCenterLat,
7791
                double dfCenterLong, double dfScale, double dfFalseEasting,
7792
                double dfFalseNorthing)
7793
7794
0
{
7795
0
    VALIDATE_POINTER1(hSRS, "OSRSetPS", OGRERR_FAILURE);
7796
7797
0
    return ToPointer(hSRS)->SetPS(dfCenterLat, dfCenterLong, dfScale,
7798
0
                                  dfFalseEasting, dfFalseNorthing);
7799
0
}
7800
7801
/************************************************************************/
7802
/*                            SetRobinson()                             */
7803
/************************************************************************/
7804
7805
OGRErr OGRSpatialReference::SetRobinson(double dfCenterLong,
7806
                                        double dfFalseEasting,
7807
                                        double dfFalseNorthing)
7808
7809
0
{
7810
0
    TAKE_OPTIONAL_LOCK();
7811
7812
0
    return d->replaceConversionAndUnref(proj_create_conversion_robinson(
7813
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7814
0
        nullptr, 0, nullptr, 0));
7815
0
}
7816
7817
/************************************************************************/
7818
/*                           OSRSetRobinson()                           */
7819
/************************************************************************/
7820
7821
OGRErr OSRSetRobinson(OGRSpatialReferenceH hSRS, double dfCenterLong,
7822
                      double dfFalseEasting, double dfFalseNorthing)
7823
7824
0
{
7825
0
    VALIDATE_POINTER1(hSRS, "OSRSetRobinson", OGRERR_FAILURE);
7826
7827
0
    return ToPointer(hSRS)->SetRobinson(dfCenterLong, dfFalseEasting,
7828
0
                                        dfFalseNorthing);
7829
0
}
7830
7831
/************************************************************************/
7832
/*                           SetSinusoidal()                            */
7833
/************************************************************************/
7834
7835
OGRErr OGRSpatialReference::SetSinusoidal(double dfCenterLong,
7836
                                          double dfFalseEasting,
7837
                                          double dfFalseNorthing)
7838
7839
0
{
7840
0
    TAKE_OPTIONAL_LOCK();
7841
7842
0
    return d->replaceConversionAndUnref(proj_create_conversion_sinusoidal(
7843
0
        d->getPROJContext(), dfCenterLong, dfFalseEasting, dfFalseNorthing,
7844
0
        nullptr, 0, nullptr, 0));
7845
0
}
7846
7847
/************************************************************************/
7848
/*                          OSRSetSinusoidal()                          */
7849
/************************************************************************/
7850
7851
OGRErr OSRSetSinusoidal(OGRSpatialReferenceH hSRS, double dfCenterLong,
7852
                        double dfFalseEasting, double dfFalseNorthing)
7853
7854
0
{
7855
0
    VALIDATE_POINTER1(hSRS, "OSRSetSinusoidal", OGRERR_FAILURE);
7856
7857
0
    return ToPointer(hSRS)->SetSinusoidal(dfCenterLong, dfFalseEasting,
7858
0
                                          dfFalseNorthing);
7859
0
}
7860
7861
/************************************************************************/
7862
/*                          SetStereographic()                          */
7863
/************************************************************************/
7864
7865
OGRErr OGRSpatialReference::SetStereographic(double dfOriginLat,
7866
                                             double dfCMeridian, double dfScale,
7867
                                             double dfFalseEasting,
7868
                                             double dfFalseNorthing)
7869
7870
0
{
7871
0
    TAKE_OPTIONAL_LOCK();
7872
7873
0
    return d->replaceConversionAndUnref(proj_create_conversion_stereographic(
7874
0
        d->getPROJContext(), dfOriginLat, dfCMeridian, dfScale, dfFalseEasting,
7875
0
        dfFalseNorthing, nullptr, 0, nullptr, 0));
7876
0
}
7877
7878
/************************************************************************/
7879
/*                        OSRSetStereographic()                         */
7880
/************************************************************************/
7881
7882
OGRErr OSRSetStereographic(OGRSpatialReferenceH hSRS, double dfOriginLat,
7883
                           double dfCMeridian, double dfScale,
7884
                           double dfFalseEasting, double dfFalseNorthing)
7885
7886
0
{
7887
0
    VALIDATE_POINTER1(hSRS, "OSRSetStereographic", OGRERR_FAILURE);
7888
7889
0
    return ToPointer(hSRS)->SetStereographic(dfOriginLat, dfCMeridian, dfScale,
7890
0
                                             dfFalseEasting, dfFalseNorthing);
7891
0
}
7892
7893
/************************************************************************/
7894
/*                               SetSOC()                               */
7895
/*                                                                      */
7896
/*      NOTE: This definition isn't really used in practice any more    */
7897
/*      and should be considered deprecated.  It seems that swiss       */
7898
/*      oblique mercator is now define as Hotine_Oblique_Mercator       */
7899
/*      with an azimuth of 90 and a rectified_grid_angle of 90.  See    */
7900
/*      EPSG:2056 and Bug 423.                                          */
7901
/************************************************************************/
7902
7903
OGRErr OGRSpatialReference::SetSOC(double dfLatitudeOfOrigin,
7904
                                   double dfCentralMeridian,
7905
                                   double dfFalseEasting,
7906
                                   double dfFalseNorthing)
7907
7908
0
{
7909
0
    TAKE_OPTIONAL_LOCK();
7910
7911
0
    return d->replaceConversionAndUnref(
7912
0
        proj_create_conversion_hotine_oblique_mercator_variant_b(
7913
0
            d->getPROJContext(), dfLatitudeOfOrigin, dfCentralMeridian, 90.0,
7914
0
            90.0, 1.0, dfFalseEasting, dfFalseNorthing, nullptr, 0.0, nullptr,
7915
0
            0.0));
7916
#if 0
7917
    SetProjection( SRS_PT_SWISS_OBLIQUE_CYLINDRICAL );
7918
    SetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, dfLatitudeOfOrigin );
7919
    SetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, dfCentralMeridian );
7920
    SetNormProjParm( SRS_PP_FALSE_EASTING, dfFalseEasting );
7921
    SetNormProjParm( SRS_PP_FALSE_NORTHING, dfFalseNorthing );
7922
7923
    return OGRERR_NONE;
7924
#endif
7925
0
}
7926
7927
/************************************************************************/
7928
/*                             OSRSetSOC()                              */
7929
/************************************************************************/
7930
7931
OGRErr OSRSetSOC(OGRSpatialReferenceH hSRS, double dfLatitudeOfOrigin,
7932
                 double dfCentralMeridian, double dfFalseEasting,
7933
                 double dfFalseNorthing)
7934
7935
0
{
7936
0
    VALIDATE_POINTER1(hSRS, "OSRSetSOC", OGRERR_FAILURE);
7937
7938
0
    return ToPointer(hSRS)->SetSOC(dfLatitudeOfOrigin, dfCentralMeridian,
7939
0
                                   dfFalseEasting, dfFalseNorthing);
7940
0
}
7941
7942
/************************************************************************/
7943
/*                               SetVDG()                               */
7944
/************************************************************************/
7945
7946
OGRErr OGRSpatialReference::SetVDG(double dfCMeridian, double dfFalseEasting,
7947
                                   double dfFalseNorthing)
7948
7949
0
{
7950
0
    TAKE_OPTIONAL_LOCK();
7951
7952
0
    return d->replaceConversionAndUnref(proj_create_conversion_van_der_grinten(
7953
0
        d->getPROJContext(), dfCMeridian, dfFalseEasting, dfFalseNorthing,
7954
0
        nullptr, 0, nullptr, 0));
7955
0
}
7956
7957
/************************************************************************/
7958
/*                             OSRSetVDG()                              */
7959
/************************************************************************/
7960
7961
OGRErr OSRSetVDG(OGRSpatialReferenceH hSRS, double dfCentralMeridian,
7962
                 double dfFalseEasting, double dfFalseNorthing)
7963
7964
0
{
7965
0
    VALIDATE_POINTER1(hSRS, "OSRSetVDG", OGRERR_FAILURE);
7966
7967
0
    return ToPointer(hSRS)->SetVDG(dfCentralMeridian, dfFalseEasting,
7968
0
                                   dfFalseNorthing);
7969
0
}
7970
7971
/************************************************************************/
7972
/*                               SetUTM()                               */
7973
/************************************************************************/
7974
7975
/**
7976
 * \brief Set UTM projection definition.
7977
 *
7978
 * This will generate a projection definition with the full set of
7979
 * transverse mercator projection parameters for the given UTM zone.
7980
 * If no PROJCS[] description is set yet, one will be set to look
7981
 * like "UTM Zone %d, {Northern, Southern} Hemisphere".
7982
 *
7983
 * This method is the same as the C function OSRSetUTM().
7984
 *
7985
 * @param nZone UTM zone.
7986
 *
7987
 * @param bNorth TRUE for northern hemisphere, or FALSE for southern
7988
 * hemisphere.
7989
 *
7990
 * @return OGRERR_NONE on success.
7991
 */
7992
7993
OGRErr OGRSpatialReference::SetUTM(int nZone, int bNorth)
7994
7995
0
{
7996
0
    TAKE_OPTIONAL_LOCK();
7997
7998
0
    if (nZone < 0 || nZone > 60)
7999
0
    {
8000
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid zone: %d", nZone);
8001
0
        return OGRERR_FAILURE;
8002
0
    }
8003
8004
0
    return d->replaceConversionAndUnref(
8005
0
        proj_create_conversion_utm(d->getPROJContext(), nZone, bNorth));
8006
0
}
8007
8008
/************************************************************************/
8009
/*                             OSRSetUTM()                              */
8010
/************************************************************************/
8011
8012
/**
8013
 * \brief Set UTM projection definition.
8014
 *
8015
 * This is the same as the C++ method OGRSpatialReference::SetUTM()
8016
 */
8017
OGRErr OSRSetUTM(OGRSpatialReferenceH hSRS, int nZone, int bNorth)
8018
8019
0
{
8020
0
    VALIDATE_POINTER1(hSRS, "OSRSetUTM", OGRERR_FAILURE);
8021
8022
0
    return ToPointer(hSRS)->SetUTM(nZone, bNorth);
8023
0
}
8024
8025
/************************************************************************/
8026
/*                             GetUTMZone()                             */
8027
/*                                                                      */
8028
/*      Returns zero if it isn't UTM.                                   */
8029
/************************************************************************/
8030
8031
/**
8032
 * \brief Get utm zone information.
8033
 *
8034
 * This is the same as the C function OSRGetUTMZone().
8035
 *
8036
 * In SWIG bindings (Python, Java, etc) the GetUTMZone() method returns a
8037
 * zone which is negative in the southern hemisphere instead of having the
8038
 * pbNorth flag used in the C and C++ interface.
8039
 *
8040
 * @param pbNorth pointer to in to set to TRUE if northern hemisphere, or
8041
 * FALSE if southern.
8042
 *
8043
 * @return UTM zone number or zero if this isn't a UTM definition.
8044
 */
8045
8046
int OGRSpatialReference::GetUTMZone(int *pbNorth) const
8047
8048
0
{
8049
0
    TAKE_OPTIONAL_LOCK();
8050
8051
0
    if (IsProjected() && GetAxesCount() == 3)
8052
0
    {
8053
0
        OGRSpatialReference *poSRSTmp = Clone();
8054
0
        poSRSTmp->DemoteTo2D(nullptr);
8055
0
        const int nZone = poSRSTmp->GetUTMZone(pbNorth);
8056
0
        delete poSRSTmp;
8057
0
        return nZone;
8058
0
    }
8059
8060
0
    const char *pszProjection = GetAttrValue("PROJECTION");
8061
8062
0
    if (pszProjection == nullptr ||
8063
0
        !EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
8064
0
        return 0;
8065
8066
0
    if (GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
8067
0
        return 0;
8068
8069
0
    if (GetProjParm(SRS_PP_SCALE_FACTOR, 1.0) != 0.9996)
8070
0
        return 0;
8071
8072
0
    if (fabs(GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) - 500000.0) > 0.001)
8073
0
        return 0;
8074
8075
0
    const double dfFalseNorthing = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
8076
8077
0
    if (dfFalseNorthing != 0.0 && fabs(dfFalseNorthing - 10000000.0) > 0.001)
8078
0
        return 0;
8079
8080
0
    if (pbNorth != nullptr)
8081
0
        *pbNorth = (dfFalseNorthing == 0);
8082
8083
0
    const double dfCentralMeridian =
8084
0
        GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
8085
0
    const double dfZone = (dfCentralMeridian + 186.0) / 6.0;
8086
8087
0
    if (dfCentralMeridian < -177.00001 || dfCentralMeridian > 177.000001 ||
8088
0
        std::isnan(dfZone) ||
8089
0
        std::abs(dfZone - static_cast<int>(dfZone) - 0.5) > 0.00001)
8090
0
        return 0;
8091
8092
0
    return static_cast<int>(dfZone);
8093
0
}
8094
8095
/************************************************************************/
8096
/*                           OSRGetUTMZone()                            */
8097
/************************************************************************/
8098
8099
/**
8100
 * \brief Get utm zone information.
8101
 *
8102
 * This is the same as the C++ method OGRSpatialReference::GetUTMZone()
8103
 */
8104
int OSRGetUTMZone(OGRSpatialReferenceH hSRS, int *pbNorth)
8105
8106
0
{
8107
0
    VALIDATE_POINTER1(hSRS, "OSRGetUTMZone", 0);
8108
8109
0
    return ToPointer(hSRS)->GetUTMZone(pbNorth);
8110
0
}
8111
8112
/************************************************************************/
8113
/*                             SetWagner()                              */
8114
/************************************************************************/
8115
8116
OGRErr OGRSpatialReference::SetWagner(int nVariation,  // 1--7.
8117
                                      double dfCenterLat, double dfFalseEasting,
8118
                                      double dfFalseNorthing)
8119
8120
0
{
8121
0
    TAKE_OPTIONAL_LOCK();
8122
8123
0
    PJ *conv;
8124
0
    if (nVariation == 1)
8125
0
    {
8126
0
        conv = proj_create_conversion_wagner_i(d->getPROJContext(), 0.0,
8127
0
                                               dfFalseEasting, dfFalseNorthing,
8128
0
                                               nullptr, 0.0, nullptr, 0.0);
8129
0
    }
8130
0
    else if (nVariation == 2)
8131
0
    {
8132
0
        conv = proj_create_conversion_wagner_ii(d->getPROJContext(), 0.0,
8133
0
                                                dfFalseEasting, dfFalseNorthing,
8134
0
                                                nullptr, 0.0, nullptr, 0.0);
8135
0
    }
8136
0
    else if (nVariation == 3)
8137
0
    {
8138
0
        conv = proj_create_conversion_wagner_iii(
8139
0
            d->getPROJContext(), dfCenterLat, 0.0, dfFalseEasting,
8140
0
            dfFalseNorthing, nullptr, 0.0, nullptr, 0.0);
8141
0
    }
8142
0
    else if (nVariation == 4)
8143
0
    {
8144
0
        conv = proj_create_conversion_wagner_iv(d->getPROJContext(), 0.0,
8145
0
                                                dfFalseEasting, dfFalseNorthing,
8146
0
                                                nullptr, 0.0, nullptr, 0.0);
8147
0
    }
8148
0
    else if (nVariation == 5)
8149
0
    {
8150
0
        conv = proj_create_conversion_wagner_v(d->getPROJContext(), 0.0,
8151
0
                                               dfFalseEasting, dfFalseNorthing,
8152
0
                                               nullptr, 0.0, nullptr, 0.0);
8153
0
    }
8154
0
    else if (nVariation == 6)
8155
0
    {
8156
0
        conv = proj_create_conversion_wagner_vi(d->getPROJContext(), 0.0,
8157
0
                                                dfFalseEasting, dfFalseNorthing,
8158
0
                                                nullptr, 0.0, nullptr, 0.0);
8159
0
    }
8160
0
    else if (nVariation == 7)
8161
0
    {
8162
0
        conv = proj_create_conversion_wagner_vii(
8163
0
            d->getPROJContext(), 0.0, dfFalseEasting, dfFalseNorthing, nullptr,
8164
0
            0.0, nullptr, 0.0);
8165
0
    }
8166
0
    else
8167
0
    {
8168
0
        CPLError(CE_Failure, CPLE_AppDefined,
8169
0
                 "Unsupported Wagner variation (%d).", nVariation);
8170
0
        return OGRERR_UNSUPPORTED_SRS;
8171
0
    }
8172
8173
0
    return d->replaceConversionAndUnref(conv);
8174
0
}
8175
8176
/************************************************************************/
8177
/*                            OSRSetWagner()                            */
8178
/************************************************************************/
8179
8180
OGRErr OSRSetWagner(OGRSpatialReferenceH hSRS, int nVariation,
8181
                    double dfCenterLat, double dfFalseEasting,
8182
                    double dfFalseNorthing)
8183
8184
0
{
8185
0
    VALIDATE_POINTER1(hSRS, "OSRSetWagner", OGRERR_FAILURE);
8186
8187
0
    return ToPointer(hSRS)->SetWagner(nVariation, dfCenterLat, dfFalseEasting,
8188
0
                                      dfFalseNorthing);
8189
0
}
8190
8191
/************************************************************************/
8192
/*                            SetQSC()                     */
8193
/************************************************************************/
8194
8195
OGRErr OGRSpatialReference::SetQSC(double dfCenterLat, double dfCenterLong)
8196
0
{
8197
0
    TAKE_OPTIONAL_LOCK();
8198
8199
0
    return d->replaceConversionAndUnref(
8200
0
        proj_create_conversion_quadrilateralized_spherical_cube(
8201
0
            d->getPROJContext(), dfCenterLat, dfCenterLong, 0.0, 0.0, nullptr,
8202
0
            0, nullptr, 0));
8203
0
}
8204
8205
/************************************************************************/
8206
/*                           OSRSetQSC()                   */
8207
/************************************************************************/
8208
8209
OGRErr OSRSetQSC(OGRSpatialReferenceH hSRS, double dfCenterLat,
8210
                 double dfCenterLong)
8211
8212
0
{
8213
0
    VALIDATE_POINTER1(hSRS, "OSRSetQSC", OGRERR_FAILURE);
8214
8215
0
    return ToPointer(hSRS)->SetQSC(dfCenterLat, dfCenterLong);
8216
0
}
8217
8218
/************************************************************************/
8219
/*                            SetSCH()                     */
8220
/************************************************************************/
8221
8222
OGRErr OGRSpatialReference::SetSCH(double dfPegLat, double dfPegLong,
8223
                                   double dfPegHeading, double dfPegHgt)
8224
8225
0
{
8226
0
    TAKE_OPTIONAL_LOCK();
8227
8228
0
    return d->replaceConversionAndUnref(
8229
0
        proj_create_conversion_spherical_cross_track_height(
8230
0
            d->getPROJContext(), dfPegLat, dfPegLong, dfPegHeading, dfPegHgt,
8231
0
            nullptr, 0, nullptr, 0));
8232
0
}
8233
8234
/************************************************************************/
8235
/*                           OSRSetSCH()                   */
8236
/************************************************************************/
8237
8238
OGRErr OSRSetSCH(OGRSpatialReferenceH hSRS, double dfPegLat, double dfPegLong,
8239
                 double dfPegHeading, double dfPegHgt)
8240
8241
0
{
8242
0
    VALIDATE_POINTER1(hSRS, "OSRSetSCH", OGRERR_FAILURE);
8243
8244
0
    return ToPointer(hSRS)->SetSCH(dfPegLat, dfPegLong, dfPegHeading, dfPegHgt);
8245
0
}
8246
8247
/************************************************************************/
8248
/*                         SetVerticalPerspective()                     */
8249
/************************************************************************/
8250
8251
OGRErr OGRSpatialReference::SetVerticalPerspective(
8252
    double dfTopoOriginLat, double dfTopoOriginLon, double dfTopoOriginHeight,
8253
    double dfViewPointHeight, double dfFalseEasting, double dfFalseNorthing)
8254
0
{
8255
0
    TAKE_OPTIONAL_LOCK();
8256
8257
0
    return d->replaceConversionAndUnref(
8258
0
        proj_create_conversion_vertical_perspective(
8259
0
            d->getPROJContext(), dfTopoOriginLat, dfTopoOriginLon,
8260
0
            dfTopoOriginHeight, dfViewPointHeight, dfFalseEasting,
8261
0
            dfFalseNorthing, nullptr, 0, nullptr, 0));
8262
0
}
8263
8264
/************************************************************************/
8265
/*                       OSRSetVerticalPerspective()                    */
8266
/************************************************************************/
8267
8268
OGRErr OSRSetVerticalPerspective(OGRSpatialReferenceH hSRS,
8269
                                 double dfTopoOriginLat, double dfTopoOriginLon,
8270
                                 double dfTopoOriginHeight,
8271
                                 double dfViewPointHeight,
8272
                                 double dfFalseEasting, double dfFalseNorthing)
8273
8274
0
{
8275
0
    VALIDATE_POINTER1(hSRS, "OSRSetVerticalPerspective", OGRERR_FAILURE);
8276
8277
0
    return ToPointer(hSRS)->SetVerticalPerspective(
8278
0
        dfTopoOriginLat, dfTopoOriginLon, dfTopoOriginHeight, dfViewPointHeight,
8279
0
        dfFalseEasting, dfFalseNorthing);
8280
0
}
8281
8282
/************************************************************************/
8283
/*             SetDerivedGeogCRSWithPoleRotationGRIBConvention()        */
8284
/************************************************************************/
8285
8286
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationGRIBConvention(
8287
    const char *pszCRSName, double dfSouthPoleLat, double dfSouthPoleLon,
8288
    double dfAxisRotation)
8289
0
{
8290
0
    TAKE_OPTIONAL_LOCK();
8291
8292
0
    d->refreshProjObj();
8293
0
    if (!d->m_pj_crs)
8294
0
        return OGRERR_FAILURE;
8295
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8296
0
        return OGRERR_FAILURE;
8297
0
    auto ctxt = d->getPROJContext();
8298
0
    auto conv = proj_create_conversion_pole_rotation_grib_convention(
8299
0
        ctxt, dfSouthPoleLat, dfSouthPoleLon, dfAxisRotation, nullptr, 0);
8300
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8301
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8302
0
                                                   d->m_pj_crs, conv, cs));
8303
0
    proj_destroy(conv);
8304
0
    proj_destroy(cs);
8305
0
    return OGRERR_NONE;
8306
0
}
8307
8308
/************************************************************************/
8309
/*         SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention()        */
8310
/************************************************************************/
8311
8312
OGRErr OGRSpatialReference::SetDerivedGeogCRSWithPoleRotationNetCDFCFConvention(
8313
    const char *pszCRSName, double dfGridNorthPoleLat,
8314
    double dfGridNorthPoleLon, double dfNorthPoleGridLon)
8315
0
{
8316
0
    TAKE_OPTIONAL_LOCK();
8317
8318
0
#if PROJ_VERSION_MAJOR > 8 ||                                                  \
8319
0
    (PROJ_VERSION_MAJOR == 8 && PROJ_VERSION_MINOR >= 2)
8320
0
    d->refreshProjObj();
8321
0
    if (!d->m_pj_crs)
8322
0
        return OGRERR_FAILURE;
8323
0
    if (d->m_pjType != PJ_TYPE_GEOGRAPHIC_2D_CRS)
8324
0
        return OGRERR_FAILURE;
8325
0
    auto ctxt = d->getPROJContext();
8326
0
    auto conv = proj_create_conversion_pole_rotation_netcdf_cf_convention(
8327
0
        ctxt, dfGridNorthPoleLat, dfGridNorthPoleLon, dfNorthPoleGridLon,
8328
0
        nullptr, 0);
8329
0
    auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8330
0
    d->setPjCRS(proj_create_derived_geographic_crs(ctxt, pszCRSName,
8331
0
                                                   d->m_pj_crs, conv, cs));
8332
0
    proj_destroy(conv);
8333
0
    proj_destroy(cs);
8334
0
    return OGRERR_NONE;
8335
#else
8336
    (void)pszCRSName;
8337
    SetProjection("Rotated_pole");
8338
    SetExtension(
8339
        "PROJCS", "PROJ4",
8340
        CPLSPrintf("+proj=ob_tran +o_proj=longlat +lon_0=%.17g +o_lon_p=%.17g "
8341
                   "+o_lat_p=%.17g +a=%.17g +b=%.17g "
8342
                   "+to_meter=0.0174532925199433 "
8343
                   "+wktext",
8344
                   180.0 + dfGridNorthPoleLon, dfNorthPoleGridLon,
8345
                   dfGridNorthPoleLat, GetSemiMajor(nullptr),
8346
                   GetSemiMinor(nullptr)));
8347
    return OGRERR_NONE;
8348
#endif
8349
0
}
8350
8351
/************************************************************************/
8352
/*                            SetAuthority()                            */
8353
/************************************************************************/
8354
8355
/**
8356
 * \brief Set the authority for a node.
8357
 *
8358
 * This method is the same as the C function OSRSetAuthority().
8359
 *
8360
 * @param pszTargetKey the partial or complete path to the node to
8361
 * set an authority on.  i.e. "PROJCS", "GEOGCS" or "GEOGCS|UNIT".
8362
 *
8363
 * @param pszAuthority authority name, such as "EPSG".
8364
 *
8365
 * @param nCode code for value with this authority.
8366
 *
8367
 * @return OGRERR_NONE on success.
8368
 */
8369
8370
OGRErr OGRSpatialReference::SetAuthority(const char *pszTargetKey,
8371
                                         const char *pszAuthority, int nCode)
8372
8373
0
{
8374
0
    TAKE_OPTIONAL_LOCK();
8375
8376
0
    d->refreshProjObj();
8377
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8378
8379
0
    if (pszTargetKey == nullptr)
8380
0
    {
8381
0
        if (!d->m_pj_crs)
8382
0
            return OGRERR_FAILURE;
8383
0
        CPLString osCode;
8384
0
        osCode.Printf("%d", nCode);
8385
0
        d->demoteFromBoundCRS();
8386
0
        d->setPjCRS(proj_alter_id(d->getPROJContext(), d->m_pj_crs,
8387
0
                                  pszAuthority, osCode.c_str()));
8388
0
        d->undoDemoteFromBoundCRS();
8389
0
        return OGRERR_NONE;
8390
0
    }
8391
8392
0
    d->demoteFromBoundCRS();
8393
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS && EQUAL(pszTargetKey, "GEOGCS"))
8394
0
    {
8395
0
        CPLString osCode;
8396
0
        osCode.Printf("%d", nCode);
8397
0
        auto newGeogCRS =
8398
0
            proj_alter_id(d->getPROJContext(), d->getGeodBaseCRS(),
8399
0
                          pszAuthority, osCode.c_str());
8400
8401
0
        auto conv =
8402
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
8403
8404
0
        auto projCRS = proj_create_projected_crs(
8405
0
            d->getPROJContext(), d->getProjCRSName(), newGeogCRS, conv,
8406
0
            d->getProjCRSCoordSys());
8407
8408
        // Preserve existing id on the PROJCRS
8409
0
        const char *pszProjCRSAuthName = proj_get_id_auth_name(d->m_pj_crs, 0);
8410
0
        const char *pszProjCRSCode = proj_get_id_code(d->m_pj_crs, 0);
8411
0
        if (pszProjCRSAuthName && pszProjCRSCode)
8412
0
        {
8413
0
            auto projCRSWithId =
8414
0
                proj_alter_id(d->getPROJContext(), projCRS, pszProjCRSAuthName,
8415
0
                              pszProjCRSCode);
8416
0
            proj_destroy(projCRS);
8417
0
            projCRS = projCRSWithId;
8418
0
        }
8419
8420
0
        proj_destroy(newGeogCRS);
8421
0
        proj_destroy(conv);
8422
8423
0
        d->setPjCRS(projCRS);
8424
0
        d->undoDemoteFromBoundCRS();
8425
0
        return OGRERR_NONE;
8426
0
    }
8427
0
    d->undoDemoteFromBoundCRS();
8428
8429
    /* -------------------------------------------------------------------- */
8430
    /*      Find the node below which the authority should be put.          */
8431
    /* -------------------------------------------------------------------- */
8432
0
    OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8433
8434
0
    if (poNode == nullptr)
8435
0
        return OGRERR_FAILURE;
8436
8437
    /* -------------------------------------------------------------------- */
8438
    /*      If there is an existing AUTHORITY child blow it away before     */
8439
    /*      trying to set a new one.                                        */
8440
    /* -------------------------------------------------------------------- */
8441
0
    int iOldChild = poNode->FindChild("AUTHORITY");
8442
0
    if (iOldChild != -1)
8443
0
        poNode->DestroyChild(iOldChild);
8444
8445
    /* -------------------------------------------------------------------- */
8446
    /*      Create a new authority node.                                    */
8447
    /* -------------------------------------------------------------------- */
8448
0
    char szCode[32] = {};
8449
8450
0
    snprintf(szCode, sizeof(szCode), "%d", nCode);
8451
8452
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("AUTHORITY");
8453
0
    poAuthNode->AddChild(new OGR_SRSNode(pszAuthority));
8454
0
    poAuthNode->AddChild(new OGR_SRSNode(szCode));
8455
8456
0
    poNode->AddChild(poAuthNode);
8457
8458
0
    return OGRERR_NONE;
8459
0
}
8460
8461
/************************************************************************/
8462
/*                          OSRSetAuthority()                           */
8463
/************************************************************************/
8464
8465
/**
8466
 * \brief Set the authority for a node.
8467
 *
8468
 * This function is the same as OGRSpatialReference::SetAuthority().
8469
 */
8470
OGRErr OSRSetAuthority(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
8471
                       const char *pszAuthority, int nCode)
8472
8473
0
{
8474
0
    VALIDATE_POINTER1(hSRS, "OSRSetAuthority", OGRERR_FAILURE);
8475
8476
0
    return ToPointer(hSRS)->SetAuthority(pszTargetKey, pszAuthority, nCode);
8477
0
}
8478
8479
/************************************************************************/
8480
/*                          GetAuthorityCode()                          */
8481
/************************************************************************/
8482
8483
/**
8484
 * \brief Get the authority code for a node.
8485
 *
8486
 * This method is used to query an AUTHORITY[] node from within the
8487
 * WKT tree, and fetch the code value.
8488
 *
8489
 * While in theory values may be non-numeric, for the EPSG authority all
8490
 * code values should be integral.
8491
 *
8492
 * This method is the same as the C function OSRGetAuthorityCode().
8493
 *
8494
 * @param pszTargetKey the partial or complete path to the node to
8495
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8496
 * search for an authority node on the root element.
8497
 *
8498
 * @return value code from authority node, or NULL on failure.  The value
8499
 * returned is internal and should not be freed or modified.
8500
 */
8501
8502
const char *
8503
OGRSpatialReference::GetAuthorityCode(const char *pszTargetKey) const
8504
8505
0
{
8506
0
    TAKE_OPTIONAL_LOCK();
8507
8508
0
    d->refreshProjObj();
8509
0
    const char *pszInputTargetKey = pszTargetKey;
8510
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8511
0
    if (pszTargetKey == nullptr)
8512
0
    {
8513
0
        if (!d->m_pj_crs)
8514
0
        {
8515
0
            return nullptr;
8516
0
        }
8517
0
        d->demoteFromBoundCRS();
8518
0
        auto ret = proj_get_id_code(d->m_pj_crs, 0);
8519
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8520
0
        {
8521
0
            auto ctxt = d->getPROJContext();
8522
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8523
0
            if (cs)
8524
0
            {
8525
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8526
0
                proj_destroy(cs);
8527
0
                if (axisCount == 3)
8528
0
                {
8529
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8530
                    // 2002 in which case, using the WKT1 representation will
8531
                    // enable us to recover the EPSG code.
8532
0
                    pszTargetKey = pszInputTargetKey;
8533
0
                }
8534
0
            }
8535
0
        }
8536
0
        d->undoDemoteFromBoundCRS();
8537
0
        if (ret != nullptr || pszTargetKey == nullptr)
8538
0
        {
8539
0
            return ret;
8540
0
        }
8541
0
    }
8542
8543
    // Special key for that context
8544
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8545
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8546
0
    {
8547
0
        auto ctxt = d->getPROJContext();
8548
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8549
0
        if (crs)
8550
0
        {
8551
0
            const char *ret = proj_get_id_code(crs, 0);
8552
0
            if (ret)
8553
0
                ret = CPLSPrintf("%s", ret);
8554
0
            proj_destroy(crs);
8555
0
            return ret;
8556
0
        }
8557
0
    }
8558
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8559
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8560
0
    {
8561
0
        auto ctxt = d->getPROJContext();
8562
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8563
0
        if (crs)
8564
0
        {
8565
0
            const char *ret = proj_get_id_code(crs, 0);
8566
0
            if (ret)
8567
0
                ret = CPLSPrintf("%s", ret);
8568
0
            proj_destroy(crs);
8569
0
            return ret;
8570
0
        }
8571
0
    }
8572
8573
    /* -------------------------------------------------------------------- */
8574
    /*      Find the node below which the authority should be put.          */
8575
    /* -------------------------------------------------------------------- */
8576
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8577
8578
0
    if (poNode == nullptr)
8579
0
        return nullptr;
8580
8581
    /* -------------------------------------------------------------------- */
8582
    /*      Fetch AUTHORITY child if there is one.                          */
8583
    /* -------------------------------------------------------------------- */
8584
0
    if (poNode->FindChild("AUTHORITY") == -1)
8585
0
        return nullptr;
8586
8587
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8588
8589
    /* -------------------------------------------------------------------- */
8590
    /*      Create a new authority node.                                    */
8591
    /* -------------------------------------------------------------------- */
8592
0
    if (poNode->GetChildCount() < 2)
8593
0
        return nullptr;
8594
8595
0
    return poNode->GetChild(1)->GetValue();
8596
0
}
8597
8598
/************************************************************************/
8599
/*                          OSRGetAuthorityCode()                       */
8600
/************************************************************************/
8601
8602
/**
8603
 * \brief Get the authority code for a node.
8604
 *
8605
 * This function is the same as OGRSpatialReference::GetAuthorityCode().
8606
 */
8607
const char *OSRGetAuthorityCode(OGRSpatialReferenceH hSRS,
8608
                                const char *pszTargetKey)
8609
8610
0
{
8611
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityCode", nullptr);
8612
8613
0
    return ToPointer(hSRS)->GetAuthorityCode(pszTargetKey);
8614
0
}
8615
8616
/************************************************************************/
8617
/*                          GetAuthorityName()                          */
8618
/************************************************************************/
8619
8620
/**
8621
 * \brief Get the authority name for a node.
8622
 *
8623
 * This method is used to query an AUTHORITY[] node from within the
8624
 * WKT tree, and fetch the authority name value.
8625
 *
8626
 * The most common authority is "EPSG".
8627
 *
8628
 * This method is the same as the C function OSRGetAuthorityName().
8629
 *
8630
 * @param pszTargetKey the partial or complete path to the node to
8631
 * get an authority from.  i.e. "PROJCS", "GEOGCS", "GEOGCS|UNIT" or NULL to
8632
 * search for an authority node on the root element.
8633
 *
8634
 * @return value code from authority node, or NULL on failure. The value
8635
 * returned is internal and should not be freed or modified.
8636
 */
8637
8638
const char *
8639
OGRSpatialReference::GetAuthorityName(const char *pszTargetKey) const
8640
8641
0
{
8642
0
    TAKE_OPTIONAL_LOCK();
8643
8644
0
    d->refreshProjObj();
8645
0
    const char *pszInputTargetKey = pszTargetKey;
8646
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
8647
0
    if (pszTargetKey == nullptr)
8648
0
    {
8649
0
        if (!d->m_pj_crs)
8650
0
        {
8651
0
            return nullptr;
8652
0
        }
8653
0
        d->demoteFromBoundCRS();
8654
0
        auto ret = proj_get_id_auth_name(d->m_pj_crs, 0);
8655
0
        if (ret == nullptr && d->m_pjType == PJ_TYPE_PROJECTED_CRS)
8656
0
        {
8657
0
            auto ctxt = d->getPROJContext();
8658
0
            auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
8659
0
            if (cs)
8660
0
            {
8661
0
                const int axisCount = proj_cs_get_axis_count(ctxt, cs);
8662
0
                proj_destroy(cs);
8663
0
                if (axisCount == 3)
8664
0
                {
8665
                    // This might come from a COMPD_CS with a VERT_DATUM type =
8666
                    // 2002 in which case, using the WKT1 representation will
8667
                    // enable us to recover the EPSG code.
8668
0
                    pszTargetKey = pszInputTargetKey;
8669
0
                }
8670
0
            }
8671
0
        }
8672
0
        d->undoDemoteFromBoundCRS();
8673
0
        if (ret != nullptr || pszTargetKey == nullptr)
8674
0
        {
8675
0
            return ret;
8676
0
        }
8677
0
    }
8678
8679
    // Special key for that context
8680
0
    else if (EQUAL(pszTargetKey, "HORIZCRS") &&
8681
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8682
0
    {
8683
0
        auto ctxt = d->getPROJContext();
8684
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
8685
0
        if (crs)
8686
0
        {
8687
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8688
0
            if (ret)
8689
0
                ret = CPLSPrintf("%s", ret);
8690
0
            proj_destroy(crs);
8691
0
            return ret;
8692
0
        }
8693
0
    }
8694
0
    else if (EQUAL(pszTargetKey, "VERTCRS") &&
8695
0
             d->m_pjType == PJ_TYPE_COMPOUND_CRS)
8696
0
    {
8697
0
        auto ctxt = d->getPROJContext();
8698
0
        auto crs = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
8699
0
        if (crs)
8700
0
        {
8701
0
            const char *ret = proj_get_id_auth_name(crs, 0);
8702
0
            if (ret)
8703
0
                ret = CPLSPrintf("%s", ret);
8704
0
            proj_destroy(crs);
8705
0
            return ret;
8706
0
        }
8707
0
    }
8708
8709
    /* -------------------------------------------------------------------- */
8710
    /*      Find the node below which the authority should be put.          */
8711
    /* -------------------------------------------------------------------- */
8712
0
    const OGR_SRSNode *poNode = GetAttrNode(pszTargetKey);
8713
8714
0
    if (poNode == nullptr)
8715
0
        return nullptr;
8716
8717
    /* -------------------------------------------------------------------- */
8718
    /*      Fetch AUTHORITY child if there is one.                          */
8719
    /* -------------------------------------------------------------------- */
8720
0
    if (poNode->FindChild("AUTHORITY") == -1)
8721
0
        return nullptr;
8722
8723
0
    poNode = poNode->GetChild(poNode->FindChild("AUTHORITY"));
8724
8725
    /* -------------------------------------------------------------------- */
8726
    /*      Create a new authority node.                                    */
8727
    /* -------------------------------------------------------------------- */
8728
0
    if (poNode->GetChildCount() < 2)
8729
0
        return nullptr;
8730
8731
0
    return poNode->GetChild(0)->GetValue();
8732
0
}
8733
8734
/************************************************************************/
8735
/*                        OSRGetAuthorityName()                         */
8736
/************************************************************************/
8737
8738
/**
8739
 * \brief Get the authority name for a node.
8740
 *
8741
 * This function is the same as OGRSpatialReference::GetAuthorityName().
8742
 */
8743
const char *OSRGetAuthorityName(OGRSpatialReferenceH hSRS,
8744
                                const char *pszTargetKey)
8745
8746
0
{
8747
0
    VALIDATE_POINTER1(hSRS, "OSRGetAuthorityName", nullptr);
8748
8749
0
    return ToPointer(hSRS)->GetAuthorityName(pszTargetKey);
8750
0
}
8751
8752
/************************************************************************/
8753
/*                          GetOGCURN()                                 */
8754
/************************************************************************/
8755
8756
/**
8757
 * \brief Get a OGC URN string describing the CRS, when possible
8758
 *
8759
 * This method assumes that the CRS has a top-level identifier, or is
8760
 * a compound CRS whose horizontal and vertical parts have a top-level
8761
 * identifier.
8762
 *
8763
 * @return a string to free with CPLFree(), or nullptr when no result can be
8764
 * generated
8765
 *
8766
 * @since GDAL 3.5
8767
 */
8768
8769
char *OGRSpatialReference::GetOGCURN() const
8770
8771
0
{
8772
0
    TAKE_OPTIONAL_LOCK();
8773
8774
0
    const char *pszAuthName = GetAuthorityName(nullptr);
8775
0
    const char *pszAuthCode = GetAuthorityCode(nullptr);
8776
0
    if (pszAuthName && pszAuthCode)
8777
0
        return CPLStrdup(
8778
0
            CPLSPrintf("urn:ogc:def:crs:%s::%s", pszAuthName, pszAuthCode));
8779
0
    if (d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8780
0
        return nullptr;
8781
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8782
0
    auto vertCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
8783
0
    char *pszRet = nullptr;
8784
0
    if (horizCRS && vertCRS)
8785
0
    {
8786
0
        auto horizAuthName = proj_get_id_auth_name(horizCRS, 0);
8787
0
        auto horizAuthCode = proj_get_id_code(horizCRS, 0);
8788
0
        auto vertAuthName = proj_get_id_auth_name(vertCRS, 0);
8789
0
        auto vertAuthCode = proj_get_id_code(vertCRS, 0);
8790
0
        if (horizAuthName && horizAuthCode && vertAuthName && vertAuthCode)
8791
0
        {
8792
0
            pszRet = CPLStrdup(CPLSPrintf(
8793
0
                "urn:ogc:def:crs,crs:%s::%s,crs:%s::%s", horizAuthName,
8794
0
                horizAuthCode, vertAuthName, vertAuthCode));
8795
0
        }
8796
0
    }
8797
0
    proj_destroy(horizCRS);
8798
0
    proj_destroy(vertCRS);
8799
0
    return pszRet;
8800
0
}
8801
8802
/************************************************************************/
8803
/*                           StripVertical()                            */
8804
/************************************************************************/
8805
8806
/**
8807
 * \brief Convert a compound cs into a horizontal CS.
8808
 *
8809
 * If this SRS is of type COMPD_CS[] then the vertical CS and the root COMPD_CS
8810
 * nodes are stripped resulting and only the horizontal coordinate system
8811
 * portion remains (normally PROJCS, GEOGCS or LOCAL_CS).
8812
 *
8813
 * If this is not a compound coordinate system then nothing is changed.
8814
 *
8815
 * This method is the same as the C function OSRStripVertical().
8816
 *
8817
 * @since OGR 1.8.0
8818
 */
8819
8820
OGRErr OGRSpatialReference::StripVertical()
8821
8822
0
{
8823
0
    TAKE_OPTIONAL_LOCK();
8824
8825
0
    d->refreshProjObj();
8826
0
    d->demoteFromBoundCRS();
8827
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_COMPOUND_CRS)
8828
0
    {
8829
0
        d->undoDemoteFromBoundCRS();
8830
0
        return OGRERR_NONE;
8831
0
    }
8832
0
    auto horizCRS = proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
8833
0
    if (!horizCRS)
8834
0
    {
8835
0
        d->undoDemoteFromBoundCRS();
8836
0
        return OGRERR_FAILURE;
8837
0
    }
8838
8839
0
    bool reuseExistingBoundCRS = false;
8840
0
    if (d->m_pj_bound_crs_target)
8841
0
    {
8842
0
        auto type = proj_get_type(d->m_pj_bound_crs_target);
8843
0
        reuseExistingBoundCRS = type == PJ_TYPE_GEOCENTRIC_CRS ||
8844
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
8845
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS;
8846
0
    }
8847
8848
0
    if (reuseExistingBoundCRS)
8849
0
    {
8850
0
        auto newBoundCRS = proj_crs_create_bound_crs(
8851
0
            d->getPROJContext(), horizCRS, d->m_pj_bound_crs_target,
8852
0
            d->m_pj_bound_crs_co);
8853
0
        proj_destroy(horizCRS);
8854
0
        d->undoDemoteFromBoundCRS();
8855
0
        d->setPjCRS(newBoundCRS);
8856
0
    }
8857
0
    else
8858
0
    {
8859
0
        d->undoDemoteFromBoundCRS();
8860
0
        d->setPjCRS(horizCRS);
8861
0
    }
8862
8863
0
    return OGRERR_NONE;
8864
0
}
8865
8866
/************************************************************************/
8867
/*                            OSRStripVertical()                             */
8868
/************************************************************************/
8869
/**
8870
 * \brief Convert a compound cs into a horizontal CS.
8871
 *
8872
 * This function is the same as the C++ method
8873
 * OGRSpatialReference::StripVertical().
8874
 */
8875
OGRErr OSRStripVertical(OGRSpatialReferenceH hSRS)
8876
8877
0
{
8878
0
    VALIDATE_POINTER1(hSRS, "OSRStripVertical", OGRERR_FAILURE);
8879
8880
0
    return OGRSpatialReference::FromHandle(hSRS)->StripVertical();
8881
0
}
8882
8883
/************************************************************************/
8884
/*                   StripTOWGS84IfKnownDatumAndAllowed()               */
8885
/************************************************************************/
8886
8887
/**
8888
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8889
 *        and this is allowed by the user.
8890
 *
8891
 * The default behavior is to remove TOWGS84 information if the CRS has a
8892
 * known horizontal datum. This can be disabled by setting the
8893
 * OSR_STRIP_TOWGS84 configuration option to NO.
8894
 *
8895
 * @return true if TOWGS84 has been removed.
8896
 * @since OGR 3.1.0
8897
 */
8898
8899
bool OGRSpatialReference::StripTOWGS84IfKnownDatumAndAllowed()
8900
0
{
8901
0
    if (CPLTestBool(CPLGetConfigOption("OSR_STRIP_TOWGS84", "YES")))
8902
0
    {
8903
0
        if (StripTOWGS84IfKnownDatum())
8904
0
        {
8905
0
            CPLDebug("OSR", "TOWGS84 information has been removed. "
8906
0
                            "It can be kept by setting the OSR_STRIP_TOWGS84 "
8907
0
                            "configuration option to NO");
8908
0
            return true;
8909
0
        }
8910
0
    }
8911
0
    return false;
8912
0
}
8913
8914
/************************************************************************/
8915
/*                      StripTOWGS84IfKnownDatum()                      */
8916
/************************************************************************/
8917
8918
/**
8919
 * \brief Remove TOWGS84 information if the CRS has a known horizontal datum
8920
 *
8921
 * @return true if TOWGS84 has been removed.
8922
 * @since OGR 3.1.0
8923
 */
8924
8925
bool OGRSpatialReference::StripTOWGS84IfKnownDatum()
8926
8927
0
{
8928
0
    TAKE_OPTIONAL_LOCK();
8929
8930
0
    d->refreshProjObj();
8931
0
    if (!d->m_pj_crs || d->m_pjType != PJ_TYPE_BOUND_CRS)
8932
0
    {
8933
0
        return false;
8934
0
    }
8935
0
    auto ctxt = d->getPROJContext();
8936
0
    auto baseCRS = proj_get_source_crs(ctxt, d->m_pj_crs);
8937
0
    if (proj_get_type(baseCRS) == PJ_TYPE_COMPOUND_CRS)
8938
0
    {
8939
0
        proj_destroy(baseCRS);
8940
0
        return false;
8941
0
    }
8942
8943
    // Known base CRS code ? Return base CRS
8944
0
    const char *pszCode = proj_get_id_code(baseCRS, 0);
8945
0
    if (pszCode)
8946
0
    {
8947
0
        d->setPjCRS(baseCRS);
8948
0
        return true;
8949
0
    }
8950
8951
0
    auto datum = proj_crs_get_datum(ctxt, baseCRS);
8952
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
8953
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
8954
0
    if (datum == nullptr)
8955
0
    {
8956
0
        datum = proj_crs_get_datum_ensemble(ctxt, baseCRS);
8957
0
    }
8958
0
#endif
8959
0
    if (!datum)
8960
0
    {
8961
0
        proj_destroy(baseCRS);
8962
0
        return false;
8963
0
    }
8964
8965
    // Known datum code ? Return base CRS
8966
0
    pszCode = proj_get_id_code(datum, 0);
8967
0
    if (pszCode)
8968
0
    {
8969
0
        proj_destroy(datum);
8970
0
        d->setPjCRS(baseCRS);
8971
0
        return true;
8972
0
    }
8973
8974
0
    const char *name = proj_get_name(datum);
8975
0
    if (EQUAL(name, "unknown"))
8976
0
    {
8977
0
        proj_destroy(datum);
8978
0
        proj_destroy(baseCRS);
8979
0
        return false;
8980
0
    }
8981
0
    const PJ_TYPE type = PJ_TYPE_GEODETIC_REFERENCE_FRAME;
8982
0
    PJ_OBJ_LIST *list =
8983
0
        proj_create_from_name(ctxt, nullptr, name, &type, 1, false, 1, nullptr);
8984
8985
0
    bool knownDatumName = false;
8986
0
    if (list)
8987
0
    {
8988
0
        if (proj_list_get_count(list) == 1)
8989
0
        {
8990
0
            knownDatumName = true;
8991
0
        }
8992
0
        proj_list_destroy(list);
8993
0
    }
8994
8995
0
    proj_destroy(datum);
8996
0
    if (knownDatumName)
8997
0
    {
8998
0
        d->setPjCRS(baseCRS);
8999
0
        return true;
9000
0
    }
9001
0
    proj_destroy(baseCRS);
9002
0
    return false;
9003
0
}
9004
9005
/************************************************************************/
9006
/*                             IsCompound()                             */
9007
/************************************************************************/
9008
9009
/**
9010
 * \brief Check if coordinate system is compound.
9011
 *
9012
 * This method is the same as the C function OSRIsCompound().
9013
 *
9014
 * @return TRUE if this is rooted with a COMPD_CS node.
9015
 */
9016
9017
int OGRSpatialReference::IsCompound() const
9018
9019
0
{
9020
0
    TAKE_OPTIONAL_LOCK();
9021
9022
0
    d->refreshProjObj();
9023
0
    d->demoteFromBoundCRS();
9024
0
    bool isCompound = d->m_pjType == PJ_TYPE_COMPOUND_CRS;
9025
0
    d->undoDemoteFromBoundCRS();
9026
0
    return isCompound;
9027
0
}
9028
9029
/************************************************************************/
9030
/*                           OSRIsCompound()                            */
9031
/************************************************************************/
9032
9033
/**
9034
 * \brief Check if the coordinate system is compound.
9035
 *
9036
 * This function is the same as OGRSpatialReference::IsCompound().
9037
 */
9038
int OSRIsCompound(OGRSpatialReferenceH hSRS)
9039
9040
0
{
9041
0
    VALIDATE_POINTER1(hSRS, "OSRIsCompound", 0);
9042
9043
0
    return ToPointer(hSRS)->IsCompound();
9044
0
}
9045
9046
/************************************************************************/
9047
/*                            IsProjected()                             */
9048
/************************************************************************/
9049
9050
/**
9051
 * \brief Check if projected coordinate system.
9052
 *
9053
 * This method is the same as the C function OSRIsProjected().
9054
 *
9055
 * @return TRUE if this contains a PROJCS node indicating a it is a
9056
 * projected coordinate system. Also if it is a CompoundCRS made of a
9057
 * ProjectedCRS
9058
 */
9059
9060
int OGRSpatialReference::IsProjected() const
9061
9062
0
{
9063
0
    TAKE_OPTIONAL_LOCK();
9064
9065
0
    d->refreshProjObj();
9066
0
    d->demoteFromBoundCRS();
9067
0
    bool isProjected = d->m_pjType == PJ_TYPE_PROJECTED_CRS;
9068
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9069
0
    {
9070
0
        auto horizCRS =
9071
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
9072
0
        if (horizCRS)
9073
0
        {
9074
0
            auto horizCRSType = proj_get_type(horizCRS);
9075
0
            isProjected = horizCRSType == PJ_TYPE_PROJECTED_CRS;
9076
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9077
0
            {
9078
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9079
0
                if (base)
9080
0
                {
9081
0
                    isProjected = proj_get_type(base) == PJ_TYPE_PROJECTED_CRS;
9082
0
                    proj_destroy(base);
9083
0
                }
9084
0
            }
9085
0
            proj_destroy(horizCRS);
9086
0
        }
9087
0
    }
9088
0
    d->undoDemoteFromBoundCRS();
9089
0
    return isProjected;
9090
0
}
9091
9092
/************************************************************************/
9093
/*                           OSRIsProjected()                           */
9094
/************************************************************************/
9095
/**
9096
 * \brief Check if projected coordinate system.
9097
 *
9098
 * This function is the same as OGRSpatialReference::IsProjected().
9099
 */
9100
int OSRIsProjected(OGRSpatialReferenceH hSRS)
9101
9102
0
{
9103
0
    VALIDATE_POINTER1(hSRS, "OSRIsProjected", 0);
9104
9105
0
    return ToPointer(hSRS)->IsProjected();
9106
0
}
9107
9108
/************************************************************************/
9109
/*                            IsGeocentric()                            */
9110
/************************************************************************/
9111
9112
/**
9113
 * \brief Check if geocentric coordinate system.
9114
 *
9115
 * This method is the same as the C function OSRIsGeocentric().
9116
 *
9117
 * @return TRUE if this contains a GEOCCS node indicating a it is a
9118
 * geocentric coordinate system.
9119
 *
9120
 * @since OGR 1.9.0
9121
 */
9122
9123
int OGRSpatialReference::IsGeocentric() const
9124
9125
0
{
9126
0
    TAKE_OPTIONAL_LOCK();
9127
9128
0
    d->refreshProjObj();
9129
0
    d->demoteFromBoundCRS();
9130
0
    bool isGeocentric = d->m_pjType == PJ_TYPE_GEOCENTRIC_CRS;
9131
0
    d->undoDemoteFromBoundCRS();
9132
0
    return isGeocentric;
9133
0
}
9134
9135
/************************************************************************/
9136
/*                           OSRIsGeocentric()                          */
9137
/************************************************************************/
9138
/**
9139
 * \brief Check if geocentric coordinate system.
9140
 *
9141
 * This function is the same as OGRSpatialReference::IsGeocentric().
9142
 *
9143
 * @since OGR 1.9.0
9144
 */
9145
int OSRIsGeocentric(OGRSpatialReferenceH hSRS)
9146
9147
0
{
9148
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeocentric", 0);
9149
9150
0
    return ToPointer(hSRS)->IsGeocentric();
9151
0
}
9152
9153
/************************************************************************/
9154
/*                            IsEmpty()                                 */
9155
/************************************************************************/
9156
9157
/**
9158
 * \brief Return if the SRS is not set.
9159
 */
9160
9161
bool OGRSpatialReference::IsEmpty() const
9162
0
{
9163
0
    TAKE_OPTIONAL_LOCK();
9164
9165
0
    d->refreshProjObj();
9166
0
    return d->m_pj_crs == nullptr;
9167
0
}
9168
9169
/************************************************************************/
9170
/*                            IsGeographic()                            */
9171
/************************************************************************/
9172
9173
/**
9174
 * \brief Check if geographic coordinate system.
9175
 *
9176
 * This method is the same as the C function OSRIsGeographic().
9177
 *
9178
 * @return TRUE if this spatial reference is geographic ... that is the
9179
 * root is a GEOGCS node. Also if it is a CompoundCRS made of a
9180
 * GeographicCRS
9181
 */
9182
9183
int OGRSpatialReference::IsGeographic() const
9184
9185
0
{
9186
0
    TAKE_OPTIONAL_LOCK();
9187
9188
0
    d->refreshProjObj();
9189
0
    d->demoteFromBoundCRS();
9190
0
    bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9191
0
                  d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_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
            isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9200
0
                     horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9201
0
            if (horizCRSType == PJ_TYPE_BOUND_CRS)
9202
0
            {
9203
0
                auto base = proj_get_source_crs(d->getPROJContext(), horizCRS);
9204
0
                if (base)
9205
0
                {
9206
0
                    horizCRSType = proj_get_type(base);
9207
0
                    isGeog = horizCRSType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9208
0
                             horizCRSType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9209
0
                    proj_destroy(base);
9210
0
                }
9211
0
            }
9212
0
            proj_destroy(horizCRS);
9213
0
        }
9214
0
    }
9215
0
    d->undoDemoteFromBoundCRS();
9216
0
    return isGeog;
9217
0
}
9218
9219
/************************************************************************/
9220
/*                          OSRIsGeographic()                           */
9221
/************************************************************************/
9222
/**
9223
 * \brief Check if geographic coordinate system.
9224
 *
9225
 * This function is the same as OGRSpatialReference::IsGeographic().
9226
 */
9227
int OSRIsGeographic(OGRSpatialReferenceH hSRS)
9228
9229
0
{
9230
0
    VALIDATE_POINTER1(hSRS, "OSRIsGeographic", 0);
9231
9232
0
    return ToPointer(hSRS)->IsGeographic();
9233
0
}
9234
9235
/************************************************************************/
9236
/*                      IsDerivedGeographic()                           */
9237
/************************************************************************/
9238
9239
/**
9240
 * \brief Check if the CRS is a derived geographic coordinate system.
9241
 * (for example a rotated long/lat grid)
9242
 *
9243
 * This method is the same as the C function OSRIsDerivedGeographic().
9244
 *
9245
 * @since GDAL 3.1.0 and PROJ 6.3.0
9246
 */
9247
9248
int OGRSpatialReference::IsDerivedGeographic() const
9249
9250
0
{
9251
0
    TAKE_OPTIONAL_LOCK();
9252
9253
0
    d->refreshProjObj();
9254
0
    d->demoteFromBoundCRS();
9255
0
    const bool isGeog = d->m_pjType == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
9256
0
                        d->m_pjType == PJ_TYPE_GEOGRAPHIC_3D_CRS;
9257
0
    const bool isDerivedGeographic =
9258
0
        isGeog && proj_is_derived_crs(d->getPROJContext(), d->m_pj_crs);
9259
0
    d->undoDemoteFromBoundCRS();
9260
0
    return isDerivedGeographic ? TRUE : FALSE;
9261
0
}
9262
9263
/************************************************************************/
9264
/*                      OSRIsDerivedGeographic()                        */
9265
/************************************************************************/
9266
/**
9267
 * \brief Check if the CRS is a derived geographic coordinate system.
9268
 * (for example a rotated long/lat grid)
9269
 *
9270
 * This function is the same as OGRSpatialReference::IsDerivedGeographic().
9271
 */
9272
int OSRIsDerivedGeographic(OGRSpatialReferenceH hSRS)
9273
9274
0
{
9275
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedGeographic", 0);
9276
9277
0
    return ToPointer(hSRS)->IsDerivedGeographic();
9278
0
}
9279
9280
/************************************************************************/
9281
/*                      IsDerivedProjected()                            */
9282
/************************************************************************/
9283
9284
/**
9285
 * \brief Check if the CRS is a derived projected coordinate system.
9286
 *
9287
 * This method is the same as the C function OSRIsDerivedGeographic().
9288
 *
9289
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9290
 */
9291
9292
int OGRSpatialReference::IsDerivedProjected() const
9293
9294
0
{
9295
0
#if PROJ_AT_LEAST_VERSION(9, 2, 0)
9296
0
    TAKE_OPTIONAL_LOCK();
9297
0
    d->refreshProjObj();
9298
0
    d->demoteFromBoundCRS();
9299
0
    const bool isDerivedProjected =
9300
0
        d->m_pjType == PJ_TYPE_DERIVED_PROJECTED_CRS;
9301
0
    d->undoDemoteFromBoundCRS();
9302
0
    return isDerivedProjected ? TRUE : FALSE;
9303
#else
9304
    return FALSE;
9305
#endif
9306
0
}
9307
9308
/************************************************************************/
9309
/*                      OSRIsDerivedProjected()                         */
9310
/************************************************************************/
9311
/**
9312
 * \brief Check if the CRS is a derived projected coordinate system.
9313
 *
9314
 * This function is the same as OGRSpatialReference::IsDerivedProjected().
9315
 *
9316
 * @since GDAL 3.9.0 (and may only return non-zero starting with PROJ 9.2.0)
9317
 */
9318
int OSRIsDerivedProjected(OGRSpatialReferenceH hSRS)
9319
9320
0
{
9321
0
    VALIDATE_POINTER1(hSRS, "OSRIsDerivedProjected", 0);
9322
9323
0
    return ToPointer(hSRS)->IsDerivedProjected();
9324
0
}
9325
9326
/************************************************************************/
9327
/*                              IsLocal()                               */
9328
/************************************************************************/
9329
9330
/**
9331
 * \brief Check if local coordinate system.
9332
 *
9333
 * This method is the same as the C function OSRIsLocal().
9334
 *
9335
 * @return TRUE if this spatial reference is local ... that is the
9336
 * root is a LOCAL_CS node.
9337
 */
9338
9339
int OGRSpatialReference::IsLocal() const
9340
9341
0
{
9342
0
    TAKE_OPTIONAL_LOCK();
9343
0
    d->refreshProjObj();
9344
0
    return d->m_pjType == PJ_TYPE_ENGINEERING_CRS;
9345
0
}
9346
9347
/************************************************************************/
9348
/*                          OSRIsLocal()                                */
9349
/************************************************************************/
9350
/**
9351
 * \brief Check if local coordinate system.
9352
 *
9353
 * This function is the same as OGRSpatialReference::IsLocal().
9354
 */
9355
int OSRIsLocal(OGRSpatialReferenceH hSRS)
9356
9357
0
{
9358
0
    VALIDATE_POINTER1(hSRS, "OSRIsLocal", 0);
9359
9360
0
    return ToPointer(hSRS)->IsLocal();
9361
0
}
9362
9363
/************************************************************************/
9364
/*                            IsVertical()                              */
9365
/************************************************************************/
9366
9367
/**
9368
 * \brief Check if vertical coordinate system.
9369
 *
9370
 * This method is the same as the C function OSRIsVertical().
9371
 *
9372
 * @return TRUE if this contains a VERT_CS node indicating a it is a
9373
 * vertical coordinate system. Also if it is a CompoundCRS made of a
9374
 * VerticalCRS
9375
 *
9376
 * @since OGR 1.8.0
9377
 */
9378
9379
int OGRSpatialReference::IsVertical() const
9380
9381
0
{
9382
0
    TAKE_OPTIONAL_LOCK();
9383
0
    d->refreshProjObj();
9384
0
    d->demoteFromBoundCRS();
9385
0
    bool isVertical = d->m_pjType == PJ_TYPE_VERTICAL_CRS;
9386
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9387
0
    {
9388
0
        auto vertCRS =
9389
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 1);
9390
0
        if (vertCRS)
9391
0
        {
9392
0
            const auto vertCRSType = proj_get_type(vertCRS);
9393
0
            isVertical = vertCRSType == PJ_TYPE_VERTICAL_CRS;
9394
0
            if (vertCRSType == PJ_TYPE_BOUND_CRS)
9395
0
            {
9396
0
                auto base = proj_get_source_crs(d->getPROJContext(), vertCRS);
9397
0
                if (base)
9398
0
                {
9399
0
                    isVertical = proj_get_type(base) == PJ_TYPE_VERTICAL_CRS;
9400
0
                    proj_destroy(base);
9401
0
                }
9402
0
            }
9403
0
            proj_destroy(vertCRS);
9404
0
        }
9405
0
    }
9406
0
    d->undoDemoteFromBoundCRS();
9407
0
    return isVertical;
9408
0
}
9409
9410
/************************************************************************/
9411
/*                           OSRIsVertical()                            */
9412
/************************************************************************/
9413
/**
9414
 * \brief Check if vertical coordinate system.
9415
 *
9416
 * This function is the same as OGRSpatialReference::IsVertical().
9417
 *
9418
 * @since OGR 1.8.0
9419
 */
9420
int OSRIsVertical(OGRSpatialReferenceH hSRS)
9421
9422
0
{
9423
0
    VALIDATE_POINTER1(hSRS, "OSRIsVertical", 0);
9424
9425
0
    return ToPointer(hSRS)->IsVertical();
9426
0
}
9427
9428
/************************************************************************/
9429
/*                            IsDynamic()                               */
9430
/************************************************************************/
9431
9432
/**
9433
 * \brief Check if a CRS is a dynamic CRS.
9434
 *
9435
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9436
 * plate-fixed.
9437
 *
9438
 * This method is the same as the C function OSRIsDynamic().
9439
 *
9440
 * @return true if the CRS is dynamic
9441
 *
9442
 * @since OGR 3.4.0
9443
 *
9444
 * @see HasPointMotionOperation()
9445
 */
9446
9447
bool OGRSpatialReference::IsDynamic() const
9448
9449
0
{
9450
0
    TAKE_OPTIONAL_LOCK();
9451
0
    bool isDynamic = false;
9452
0
    d->refreshProjObj();
9453
0
    d->demoteFromBoundCRS();
9454
0
    auto ctxt = d->getPROJContext();
9455
0
    PJ *horiz = nullptr;
9456
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
9457
0
    {
9458
0
        horiz = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
9459
0
    }
9460
0
    else if (d->m_pj_crs)
9461
0
    {
9462
0
        horiz = proj_clone(ctxt, d->m_pj_crs);
9463
0
    }
9464
0
    if (horiz && proj_get_type(horiz) == PJ_TYPE_BOUND_CRS)
9465
0
    {
9466
0
        auto baseCRS = proj_get_source_crs(ctxt, horiz);
9467
0
        if (baseCRS)
9468
0
        {
9469
0
            proj_destroy(horiz);
9470
0
            horiz = baseCRS;
9471
0
        }
9472
0
    }
9473
0
    auto datum = horiz ? proj_crs_get_datum(ctxt, horiz) : nullptr;
9474
0
    if (datum)
9475
0
    {
9476
0
        const auto type = proj_get_type(datum);
9477
0
        isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9478
0
                    type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9479
0
        if (!isDynamic)
9480
0
        {
9481
0
            const char *auth_name = proj_get_id_auth_name(datum, 0);
9482
0
            const char *code = proj_get_id_code(datum, 0);
9483
0
            if (auth_name && code && EQUAL(auth_name, "EPSG") &&
9484
0
                EQUAL(code, "6326"))
9485
0
            {
9486
0
                isDynamic = true;
9487
0
            }
9488
0
        }
9489
0
        proj_destroy(datum);
9490
0
    }
9491
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9492
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9493
0
    else
9494
0
    {
9495
0
        auto ensemble =
9496
0
            horiz ? proj_crs_get_datum_ensemble(ctxt, horiz) : nullptr;
9497
0
        if (ensemble)
9498
0
        {
9499
0
            auto member = proj_datum_ensemble_get_member(ctxt, ensemble, 0);
9500
0
            if (member)
9501
0
            {
9502
0
                const auto type = proj_get_type(member);
9503
0
                isDynamic = type == PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME ||
9504
0
                            type == PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
9505
0
                proj_destroy(member);
9506
0
            }
9507
0
            proj_destroy(ensemble);
9508
0
        }
9509
0
    }
9510
0
#endif
9511
0
    proj_destroy(horiz);
9512
0
    d->undoDemoteFromBoundCRS();
9513
0
    return isDynamic;
9514
0
}
9515
9516
/************************************************************************/
9517
/*                           OSRIsDynamic()                             */
9518
/************************************************************************/
9519
/**
9520
 * \brief Check if a CRS is a dynamic CRS.
9521
 *
9522
 * A dynamic CRS relies on a dynamic datum, that is a datum that is not
9523
 * plate-fixed.
9524
 *
9525
 * This function is the same as OGRSpatialReference::IsDynamic().
9526
 *
9527
 * @since OGR 3.4.0
9528
 */
9529
int OSRIsDynamic(OGRSpatialReferenceH hSRS)
9530
9531
0
{
9532
0
    VALIDATE_POINTER1(hSRS, "OSRIsDynamic", 0);
9533
9534
0
    return ToPointer(hSRS)->IsDynamic();
9535
0
}
9536
9537
/************************************************************************/
9538
/*                         HasPointMotionOperation()                    */
9539
/************************************************************************/
9540
9541
/**
9542
 * \brief Check if a CRS has at least an associated point motion operation.
9543
 *
9544
 * Some CRS are not formally declared as dynamic, but may behave as such
9545
 * in practice due to the presence of point motion operation, to perform
9546
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9547
 *
9548
 * @return true if the CRS has at least an associated point motion operation.
9549
 *
9550
 * @since OGR 3.8.0 and PROJ 9.4.0
9551
 *
9552
 * @see IsDynamic()
9553
 */
9554
9555
bool OGRSpatialReference::HasPointMotionOperation() const
9556
9557
0
{
9558
0
#if PROJ_VERSION_MAJOR > 9 ||                                                  \
9559
0
    (PROJ_VERSION_MAJOR == 9 && PROJ_VERSION_MINOR >= 4)
9560
0
    TAKE_OPTIONAL_LOCK();
9561
0
    d->refreshProjObj();
9562
0
    d->demoteFromBoundCRS();
9563
0
    auto ctxt = d->getPROJContext();
9564
0
    auto res =
9565
0
        CPL_TO_BOOL(proj_crs_has_point_motion_operation(ctxt, d->m_pj_crs));
9566
0
    d->undoDemoteFromBoundCRS();
9567
0
    return res;
9568
#else
9569
    return false;
9570
#endif
9571
0
}
9572
9573
/************************************************************************/
9574
/*                      OSRHasPointMotionOperation()                    */
9575
/************************************************************************/
9576
9577
/**
9578
 * \brief Check if a CRS has at least an associated point motion operation.
9579
 *
9580
 * Some CRS are not formally declared as dynamic, but may behave as such
9581
 * in practice due to the presence of point motion operation, to perform
9582
 * coordinate epoch changes within the CRS. Typically NAD83(CSRS)v7
9583
 *
9584
 * This function is the same as OGRSpatialReference::HasPointMotionOperation().
9585
 *
9586
 * @since OGR 3.8.0 and PROJ 9.4.0
9587
 */
9588
int OSRHasPointMotionOperation(OGRSpatialReferenceH hSRS)
9589
9590
0
{
9591
0
    VALIDATE_POINTER1(hSRS, "OSRHasPointMotionOperation", 0);
9592
9593
0
    return ToPointer(hSRS)->HasPointMotionOperation();
9594
0
}
9595
9596
/************************************************************************/
9597
/*                            CloneGeogCS()                             */
9598
/************************************************************************/
9599
9600
/**
9601
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9602
 * object.
9603
 *
9604
 * @return a new SRS, which becomes the responsibility of the caller.
9605
 */
9606
OGRSpatialReference *OGRSpatialReference::CloneGeogCS() const
9607
9608
0
{
9609
0
    TAKE_OPTIONAL_LOCK();
9610
0
    d->refreshProjObj();
9611
0
    if (d->m_pj_crs)
9612
0
    {
9613
0
        if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
9614
0
            return nullptr;
9615
9616
0
        auto geodCRS =
9617
0
            proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9618
0
        if (geodCRS)
9619
0
        {
9620
0
            OGRSpatialReference *poNewSRS = new OGRSpatialReference();
9621
0
            if (d->m_pjType == PJ_TYPE_BOUND_CRS)
9622
0
            {
9623
0
                PJ *hub_crs =
9624
0
                    proj_get_target_crs(d->getPROJContext(), d->m_pj_crs);
9625
0
                PJ *co = proj_crs_get_coordoperation(d->getPROJContext(),
9626
0
                                                     d->m_pj_crs);
9627
0
                auto temp = proj_crs_create_bound_crs(d->getPROJContext(),
9628
0
                                                      geodCRS, hub_crs, co);
9629
0
                proj_destroy(geodCRS);
9630
0
                geodCRS = temp;
9631
0
                proj_destroy(hub_crs);
9632
0
                proj_destroy(co);
9633
0
            }
9634
9635
            /* --------------------------------------------------------------------
9636
             */
9637
            /*      We have to reconstruct the GEOGCS node for geocentric */
9638
            /*      coordinate systems. */
9639
            /* --------------------------------------------------------------------
9640
             */
9641
0
            if (proj_get_type(geodCRS) == PJ_TYPE_GEOCENTRIC_CRS)
9642
0
            {
9643
0
                auto datum = proj_crs_get_datum(d->getPROJContext(), geodCRS);
9644
0
#if PROJ_VERSION_MAJOR > 7 ||                                                  \
9645
0
    (PROJ_VERSION_MAJOR == 7 && PROJ_VERSION_MINOR >= 2)
9646
0
                if (datum == nullptr)
9647
0
                {
9648
0
                    datum = proj_crs_get_datum_ensemble(d->getPROJContext(),
9649
0
                                                        geodCRS);
9650
0
                }
9651
0
#endif
9652
0
                if (datum)
9653
0
                {
9654
0
                    auto cs = proj_create_ellipsoidal_2D_cs(
9655
0
                        d->getPROJContext(), PJ_ELLPS2D_LATITUDE_LONGITUDE,
9656
0
                        nullptr, 0);
9657
0
                    auto temp = proj_create_geographic_crs_from_datum(
9658
0
                        d->getPROJContext(), "unnamed", datum, cs);
9659
0
                    proj_destroy(datum);
9660
0
                    proj_destroy(cs);
9661
0
                    proj_destroy(geodCRS);
9662
0
                    geodCRS = temp;
9663
0
                }
9664
0
            }
9665
9666
0
            poNewSRS->d->setPjCRS(geodCRS);
9667
0
            if (d->m_axisMappingStrategy == OAMS_TRADITIONAL_GIS_ORDER)
9668
0
                poNewSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
9669
0
            return poNewSRS;
9670
0
        }
9671
0
    }
9672
0
    return nullptr;
9673
0
}
9674
9675
/************************************************************************/
9676
/*                           OSRCloneGeogCS()                           */
9677
/************************************************************************/
9678
/**
9679
 * \brief Make a duplicate of the GEOGCS node of this OGRSpatialReference
9680
 * object.
9681
 *
9682
 * This function is the same as OGRSpatialReference::CloneGeogCS().
9683
 */
9684
OGRSpatialReferenceH CPL_STDCALL OSRCloneGeogCS(OGRSpatialReferenceH hSource)
9685
9686
0
{
9687
0
    VALIDATE_POINTER1(hSource, "OSRCloneGeogCS", nullptr);
9688
9689
0
    return ToHandle(ToPointer(hSource)->CloneGeogCS());
9690
0
}
9691
9692
/************************************************************************/
9693
/*                            IsSameGeogCS()                            */
9694
/************************************************************************/
9695
9696
/**
9697
 * \brief Do the GeogCS'es match?
9698
 *
9699
 * This method is the same as the C function OSRIsSameGeogCS().
9700
 *
9701
 * @param poOther the SRS being compared against.
9702
 *
9703
 * @return TRUE if they are the same or FALSE otherwise.
9704
 */
9705
9706
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther) const
9707
9708
0
{
9709
0
    return IsSameGeogCS(poOther, nullptr);
9710
0
}
9711
9712
/**
9713
 * \brief Do the GeogCS'es match?
9714
 *
9715
 * This method is the same as the C function OSRIsSameGeogCS().
9716
 *
9717
 * @param poOther the SRS being compared against.
9718
 * @param papszOptions options. ignored
9719
 *
9720
 * @return TRUE if they are the same or FALSE otherwise.
9721
 */
9722
9723
int OGRSpatialReference::IsSameGeogCS(const OGRSpatialReference *poOther,
9724
                                      const char *const *papszOptions) const
9725
9726
0
{
9727
0
    TAKE_OPTIONAL_LOCK();
9728
9729
0
    CPL_IGNORE_RET_VAL(papszOptions);
9730
9731
0
    d->refreshProjObj();
9732
0
    poOther->d->refreshProjObj();
9733
0
    if (!d->m_pj_crs || !poOther->d->m_pj_crs)
9734
0
        return FALSE;
9735
0
    if (d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9736
0
        d->m_pjType == PJ_TYPE_VERTICAL_CRS ||
9737
0
        poOther->d->m_pjType == PJ_TYPE_ENGINEERING_CRS ||
9738
0
        poOther->d->m_pjType == PJ_TYPE_VERTICAL_CRS)
9739
0
    {
9740
0
        return FALSE;
9741
0
    }
9742
9743
0
    auto geodCRS = proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
9744
0
    auto otherGeodCRS =
9745
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), poOther->d->m_pj_crs);
9746
0
    if (!geodCRS || !otherGeodCRS)
9747
0
    {
9748
0
        proj_destroy(geodCRS);
9749
0
        proj_destroy(otherGeodCRS);
9750
0
        return FALSE;
9751
0
    }
9752
9753
0
    int ret = proj_is_equivalent_to(
9754
0
        geodCRS, otherGeodCRS, PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS);
9755
9756
0
    proj_destroy(geodCRS);
9757
0
    proj_destroy(otherGeodCRS);
9758
0
    return ret;
9759
0
}
9760
9761
/************************************************************************/
9762
/*                          OSRIsSameGeogCS()                           */
9763
/************************************************************************/
9764
9765
/**
9766
 * \brief Do the GeogCS'es match?
9767
 *
9768
 * This function is the same as OGRSpatialReference::IsSameGeogCS().
9769
 */
9770
int OSRIsSameGeogCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9771
9772
0
{
9773
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameGeogCS", 0);
9774
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameGeogCS", 0);
9775
9776
0
    return ToPointer(hSRS1)->IsSameGeogCS(ToPointer(hSRS2));
9777
0
}
9778
9779
/************************************************************************/
9780
/*                            IsSameVertCS()                            */
9781
/************************************************************************/
9782
9783
/**
9784
 * \brief Do the VertCS'es match?
9785
 *
9786
 * This method is the same as the C function OSRIsSameVertCS().
9787
 *
9788
 * @param poOther the SRS being compared against.
9789
 *
9790
 * @return TRUE if they are the same or FALSE otherwise.
9791
 */
9792
9793
int OGRSpatialReference::IsSameVertCS(const OGRSpatialReference *poOther) const
9794
9795
0
{
9796
0
    TAKE_OPTIONAL_LOCK();
9797
9798
    /* -------------------------------------------------------------------- */
9799
    /*      Does the datum name match?                                      */
9800
    /* -------------------------------------------------------------------- */
9801
0
    const char *pszThisValue = this->GetAttrValue("VERT_DATUM");
9802
0
    const char *pszOtherValue = poOther->GetAttrValue("VERT_DATUM");
9803
9804
0
    if (pszThisValue == nullptr || pszOtherValue == nullptr ||
9805
0
        !EQUAL(pszThisValue, pszOtherValue))
9806
0
        return FALSE;
9807
9808
    /* -------------------------------------------------------------------- */
9809
    /*      Do the units match?                                             */
9810
    /* -------------------------------------------------------------------- */
9811
0
    pszThisValue = this->GetAttrValue("VERT_CS|UNIT", 1);
9812
0
    if (pszThisValue == nullptr)
9813
0
        pszThisValue = "1.0";
9814
9815
0
    pszOtherValue = poOther->GetAttrValue("VERT_CS|UNIT", 1);
9816
0
    if (pszOtherValue == nullptr)
9817
0
        pszOtherValue = "1.0";
9818
9819
0
    if (std::abs(CPLAtof(pszOtherValue) - CPLAtof(pszThisValue)) > 0.00000001)
9820
0
        return FALSE;
9821
9822
0
    return TRUE;
9823
0
}
9824
9825
/************************************************************************/
9826
/*                          OSRIsSameVertCS()                           */
9827
/************************************************************************/
9828
9829
/**
9830
 * \brief Do the VertCS'es match?
9831
 *
9832
 * This function is the same as OGRSpatialReference::IsSameVertCS().
9833
 */
9834
int OSRIsSameVertCS(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9835
9836
0
{
9837
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSameVertCS", 0);
9838
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSameVertCS", 0);
9839
9840
0
    return ToPointer(hSRS1)->IsSameVertCS(ToPointer(hSRS2));
9841
0
}
9842
9843
/************************************************************************/
9844
/*                               IsSame()                               */
9845
/************************************************************************/
9846
9847
/**
9848
 * \brief Do these two spatial references describe the same system ?
9849
 *
9850
 * @param poOtherSRS the SRS being compared to.
9851
 *
9852
 * @return TRUE if equivalent or FALSE otherwise.
9853
 */
9854
9855
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS) const
9856
9857
0
{
9858
0
    return IsSame(poOtherSRS, nullptr);
9859
0
}
9860
9861
/**
9862
 * \brief Do these two spatial references describe the same system ?
9863
 *
9864
 * This also takes into account the data axis to CRS axis mapping by default
9865
 *
9866
 * @param poOtherSRS the SRS being compared to.
9867
 * @param papszOptions options. NULL or NULL terminated list of options.
9868
 * Currently supported options are:
9869
 * <ul>
9870
 * <li>IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING=YES/NO. Defaults to NO</li>
9871
 * <li>CRITERION=STRICT/EQUIVALENT/EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.
9872
 *     Defaults to EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS.</li>
9873
 * <li>IGNORE_COORDINATE_EPOCH=YES/NO. Defaults to NO</li>
9874
 * </ul>
9875
 *
9876
 * @return TRUE if equivalent or FALSE otherwise.
9877
 */
9878
9879
int OGRSpatialReference::IsSame(const OGRSpatialReference *poOtherSRS,
9880
                                const char *const *papszOptions) const
9881
9882
0
{
9883
0
    TAKE_OPTIONAL_LOCK();
9884
9885
0
    d->refreshProjObj();
9886
0
    poOtherSRS->d->refreshProjObj();
9887
0
    if (!d->m_pj_crs || !poOtherSRS->d->m_pj_crs)
9888
0
        return d->m_pj_crs == poOtherSRS->d->m_pj_crs;
9889
0
    if (!CPLTestBool(CSLFetchNameValueDef(
9890
0
            papszOptions, "IGNORE_DATA_AXIS_TO_SRS_AXIS_MAPPING", "NO")))
9891
0
    {
9892
0
        if (d->m_axisMapping != poOtherSRS->d->m_axisMapping)
9893
0
            return false;
9894
0
    }
9895
9896
0
    if (!CPLTestBool(CSLFetchNameValueDef(papszOptions,
9897
0
                                          "IGNORE_COORDINATE_EPOCH", "NO")))
9898
0
    {
9899
0
        if (d->m_coordinateEpoch != poOtherSRS->d->m_coordinateEpoch)
9900
0
            return false;
9901
0
    }
9902
9903
0
    bool reboundSelf = false;
9904
0
    bool reboundOther = false;
9905
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS &&
9906
0
        poOtherSRS->d->m_pjType != PJ_TYPE_BOUND_CRS)
9907
0
    {
9908
0
        d->demoteFromBoundCRS();
9909
0
        reboundSelf = true;
9910
0
    }
9911
0
    else if (d->m_pjType != PJ_TYPE_BOUND_CRS &&
9912
0
             poOtherSRS->d->m_pjType == PJ_TYPE_BOUND_CRS)
9913
0
    {
9914
0
        poOtherSRS->d->demoteFromBoundCRS();
9915
0
        reboundOther = true;
9916
0
    }
9917
9918
0
    PJ_COMPARISON_CRITERION criterion =
9919
0
        PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
9920
0
    const char *pszCriterion = CSLFetchNameValueDef(
9921
0
        papszOptions, "CRITERION", "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS");
9922
0
    if (EQUAL(pszCriterion, "STRICT"))
9923
0
        criterion = PJ_COMP_STRICT;
9924
0
    else if (EQUAL(pszCriterion, "EQUIVALENT"))
9925
0
        criterion = PJ_COMP_EQUIVALENT;
9926
0
    else if (!EQUAL(pszCriterion, "EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS"))
9927
0
    {
9928
0
        CPLError(CE_Warning, CPLE_NotSupported,
9929
0
                 "Unsupported value for CRITERION: %s", pszCriterion);
9930
0
    }
9931
0
    int ret =
9932
0
        proj_is_equivalent_to(d->m_pj_crs, poOtherSRS->d->m_pj_crs, criterion);
9933
0
    if (reboundSelf)
9934
0
        d->undoDemoteFromBoundCRS();
9935
0
    if (reboundOther)
9936
0
        poOtherSRS->d->undoDemoteFromBoundCRS();
9937
9938
0
    return ret;
9939
0
}
9940
9941
/************************************************************************/
9942
/*                             OSRIsSame()                              */
9943
/************************************************************************/
9944
9945
/**
9946
 * \brief Do these two spatial references describe the same system ?
9947
 *
9948
 * This function is the same as OGRSpatialReference::IsSame().
9949
 */
9950
int OSRIsSame(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2)
9951
9952
0
{
9953
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9954
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9955
9956
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2));
9957
0
}
9958
9959
/************************************************************************/
9960
/*                             OSRIsSameEx()                            */
9961
/************************************************************************/
9962
9963
/**
9964
 * \brief Do these two spatial references describe the same system ?
9965
 *
9966
 * This function is the same as OGRSpatialReference::IsSame().
9967
 */
9968
int OSRIsSameEx(OGRSpatialReferenceH hSRS1, OGRSpatialReferenceH hSRS2,
9969
                const char *const *papszOptions)
9970
0
{
9971
0
    VALIDATE_POINTER1(hSRS1, "OSRIsSame", 0);
9972
0
    VALIDATE_POINTER1(hSRS2, "OSRIsSame", 0);
9973
9974
0
    return ToPointer(hSRS1)->IsSame(ToPointer(hSRS2), papszOptions);
9975
0
}
9976
9977
/************************************************************************/
9978
/*                    convertToOtherProjection()                        */
9979
/************************************************************************/
9980
9981
/**
9982
 * \brief Convert to another equivalent projection
9983
 *
9984
 * Currently implemented:
9985
 * <ul>
9986
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
9987
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
9988
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
9989
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
9990
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
9991
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
9992
 * </ul>
9993
 *
9994
 * @param pszTargetProjection target projection.
9995
 * @param papszOptions lists of options. None supported currently.
9996
 * @return a new SRS, or NULL in case of error.
9997
 *
9998
 * @since GDAL 2.3
9999
 */
10000
OGRSpatialReference *OGRSpatialReference::convertToOtherProjection(
10001
    const char *pszTargetProjection,
10002
    CPL_UNUSED const char *const *papszOptions) const
10003
0
{
10004
0
    TAKE_OPTIONAL_LOCK();
10005
10006
0
    if (pszTargetProjection == nullptr)
10007
0
        return nullptr;
10008
0
    int new_code;
10009
0
    if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_1SP))
10010
0
    {
10011
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
10012
0
    }
10013
0
    else if (EQUAL(pszTargetProjection, SRS_PT_MERCATOR_2SP))
10014
0
    {
10015
0
        new_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
10016
0
    }
10017
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
10018
0
    {
10019
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
10020
0
    }
10021
0
    else if (EQUAL(pszTargetProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
10022
0
    {
10023
0
        new_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
10024
0
    }
10025
0
    else
10026
0
    {
10027
0
        return nullptr;
10028
0
    }
10029
10030
0
    d->refreshProjObj();
10031
0
    d->demoteFromBoundCRS();
10032
0
    OGRSpatialReference *poNewSRS = nullptr;
10033
0
    if (d->m_pjType == PJ_TYPE_PROJECTED_CRS)
10034
0
    {
10035
0
        auto conv =
10036
0
            proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10037
0
        auto new_conv = proj_convert_conversion_to_other_method(
10038
0
            d->getPROJContext(), conv, new_code, nullptr);
10039
0
        proj_destroy(conv);
10040
0
        if (new_conv)
10041
0
        {
10042
0
            auto geodCRS =
10043
0
                proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10044
0
            auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
10045
0
                                                     d->m_pj_crs);
10046
0
            if (geodCRS && cs)
10047
0
            {
10048
0
                auto new_proj_crs = proj_create_projected_crs(
10049
0
                    d->getPROJContext(), proj_get_name(d->m_pj_crs), geodCRS,
10050
0
                    new_conv, cs);
10051
0
                proj_destroy(new_conv);
10052
0
                if (new_proj_crs)
10053
0
                {
10054
0
                    poNewSRS = new OGRSpatialReference();
10055
10056
0
                    if (d->m_pj_bound_crs_target && d->m_pj_bound_crs_co)
10057
0
                    {
10058
0
                        auto boundCRS = proj_crs_create_bound_crs(
10059
0
                            d->getPROJContext(), new_proj_crs,
10060
0
                            d->m_pj_bound_crs_target, d->m_pj_bound_crs_co);
10061
0
                        if (boundCRS)
10062
0
                        {
10063
0
                            proj_destroy(new_proj_crs);
10064
0
                            new_proj_crs = boundCRS;
10065
0
                        }
10066
0
                    }
10067
10068
0
                    poNewSRS->d->setPjCRS(new_proj_crs);
10069
0
                }
10070
0
            }
10071
0
            proj_destroy(geodCRS);
10072
0
            proj_destroy(cs);
10073
0
        }
10074
0
    }
10075
0
    d->undoDemoteFromBoundCRS();
10076
0
    return poNewSRS;
10077
0
}
10078
10079
/************************************************************************/
10080
/*                    OSRConvertToOtherProjection()                     */
10081
/************************************************************************/
10082
10083
/**
10084
 * \brief Convert to another equivalent projection
10085
 *
10086
 * Currently implemented:
10087
 * <ul>
10088
 * <li>SRS_PT_MERCATOR_1SP to SRS_PT_MERCATOR_2SP</li>
10089
 * <li>SRS_PT_MERCATOR_2SP to SRS_PT_MERCATOR_1SP</li>
10090
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP to
10091
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP</li>
10092
 * <li>SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP to
10093
 * SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP</li>
10094
 * </ul>
10095
 *
10096
 * @param hSRS source SRS
10097
 * @param pszTargetProjection target projection.
10098
 * @param papszOptions lists of options. None supported currently.
10099
 * @return a new SRS, or NULL in case of error.
10100
 *
10101
 * @since GDAL 2.3
10102
 */
10103
OGRSpatialReferenceH
10104
OSRConvertToOtherProjection(OGRSpatialReferenceH hSRS,
10105
                            const char *pszTargetProjection,
10106
                            const char *const *papszOptions)
10107
0
{
10108
0
    VALIDATE_POINTER1(hSRS, "OSRConvertToOtherProjection", nullptr);
10109
0
    return ToHandle(ToPointer(hSRS)->convertToOtherProjection(
10110
0
        pszTargetProjection, papszOptions));
10111
0
}
10112
10113
/************************************************************************/
10114
/*                           OSRFindMatches()                           */
10115
/************************************************************************/
10116
10117
/**
10118
 * \brief Try to identify a match between the passed SRS and a related SRS
10119
 * in a catalog.
10120
 *
10121
 * Matching may be partial, or may fail.
10122
 * Returned entries will be sorted by decreasing match confidence (first
10123
 * entry has the highest match confidence).
10124
 *
10125
 * The exact way matching is done may change in future versions. Starting with
10126
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
10127
 *
10128
 * This function is the same as OGRSpatialReference::FindMatches().
10129
 *
10130
 * @param hSRS SRS to match
10131
 * @param papszOptions NULL terminated list of options or NULL
10132
 * @param pnEntries Output parameter. Number of values in the returned array.
10133
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
10134
 * will be allocated to an array of *pnEntries whose values between 0 and 100
10135
 * indicate the confidence in the match. 100 is the highest confidence level.
10136
 * The array must be freed with CPLFree().
10137
 *
10138
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
10139
 * with OSRFreeSRSArray()
10140
 *
10141
 * @since GDAL 2.3
10142
 */
10143
OGRSpatialReferenceH *OSRFindMatches(OGRSpatialReferenceH hSRS,
10144
                                     char **papszOptions, int *pnEntries,
10145
                                     int **ppanMatchConfidence)
10146
0
{
10147
0
    if (pnEntries)
10148
0
        *pnEntries = 0;
10149
0
    if (ppanMatchConfidence)
10150
0
        *ppanMatchConfidence = nullptr;
10151
0
    VALIDATE_POINTER1(hSRS, "OSRFindMatches", nullptr);
10152
10153
0
    OGRSpatialReference *poSRS = ToPointer(hSRS);
10154
0
    return poSRS->FindMatches(papszOptions, pnEntries, ppanMatchConfidence);
10155
0
}
10156
10157
/************************************************************************/
10158
/*                           OSRFreeSRSArray()                          */
10159
/************************************************************************/
10160
10161
/**
10162
 * \brief Free return of OSRIdentifyMatches()
10163
 *
10164
 * @param pahSRS array of SRS (must be NULL terminated)
10165
 * @since GDAL 2.3
10166
 */
10167
void OSRFreeSRSArray(OGRSpatialReferenceH *pahSRS)
10168
0
{
10169
0
    if (pahSRS != nullptr)
10170
0
    {
10171
0
        for (int i = 0; pahSRS[i] != nullptr; ++i)
10172
0
        {
10173
0
            OSRRelease(pahSRS[i]);
10174
0
        }
10175
0
        CPLFree(pahSRS);
10176
0
    }
10177
0
}
10178
10179
/************************************************************************/
10180
/*                         FindBestMatch()                              */
10181
/************************************************************************/
10182
10183
/**
10184
 * \brief Try to identify the best match between the passed SRS and a related
10185
 * SRS in a catalog.
10186
 *
10187
 * This is a wrapper over OGRSpatialReference::FindMatches() that takes care
10188
 * of filtering its output.
10189
 * Only matches whose confidence is greater or equal to nMinimumMatchConfidence
10190
 * will be considered. If there is a single match, it is returned.
10191
 * If there are several matches, only return the one under the
10192
 * pszPreferredAuthority, if there is a single one under that authority.
10193
 *
10194
 * @param nMinimumMatchConfidence Minimum match confidence (value between 0 and
10195
 * 100). If set to 0, 90 is used.
10196
 * @param pszPreferredAuthority Preferred CRS authority. If set to nullptr,
10197
 * "EPSG" is used.
10198
 * @param papszOptions NULL terminated list of options or NULL. No option is
10199
 * defined at time of writing.
10200
 *
10201
 * @return a new OGRSpatialReference* object to free with Release(), or nullptr
10202
 *
10203
 * @since GDAL 3.6
10204
 * @see OGRSpatialReference::FindMatches()
10205
 */
10206
OGRSpatialReference *
10207
OGRSpatialReference::FindBestMatch(int nMinimumMatchConfidence,
10208
                                   const char *pszPreferredAuthority,
10209
                                   CSLConstList papszOptions) const
10210
0
{
10211
0
    TAKE_OPTIONAL_LOCK();
10212
10213
0
    CPL_IGNORE_RET_VAL(papszOptions);  // ignored for now.
10214
10215
0
    if (nMinimumMatchConfidence == 0)
10216
0
        nMinimumMatchConfidence = 90;
10217
0
    if (pszPreferredAuthority == nullptr)
10218
0
        pszPreferredAuthority = "EPSG";
10219
10220
    // Try to identify the CRS with the database
10221
0
    int nEntries = 0;
10222
0
    int *panConfidence = nullptr;
10223
0
    OGRSpatialReferenceH *pahSRS =
10224
0
        FindMatches(nullptr, &nEntries, &panConfidence);
10225
0
    if (nEntries == 1 && panConfidence[0] >= nMinimumMatchConfidence)
10226
0
    {
10227
0
        std::vector<double> adfTOWGS84(7);
10228
0
        if (GetTOWGS84(&adfTOWGS84[0], 7) != OGRERR_NONE)
10229
0
        {
10230
0
            adfTOWGS84.clear();
10231
0
        }
10232
10233
0
        auto poSRS = OGRSpatialReference::FromHandle(pahSRS[0]);
10234
10235
0
        auto poBaseGeogCRS =
10236
0
            std::unique_ptr<OGRSpatialReference>(poSRS->CloneGeogCS());
10237
10238
        // If the base geographic SRS of the SRS is EPSG:4326
10239
        // with TOWGS84[0,0,0,0,0,0], then just use the official
10240
        // SRS code
10241
        // Same with EPSG:4258 (ETRS89), since it's the only known
10242
        // TOWGS84[] style transformation to WGS 84, and given the
10243
        // "fuzzy" nature of both ETRS89 and WGS 84, there's little
10244
        // chance that a non-NULL TOWGS84[] will emerge.
10245
0
        const char *pszAuthorityName = nullptr;
10246
0
        const char *pszAuthorityCode = nullptr;
10247
0
        const char *pszBaseAuthorityName = nullptr;
10248
0
        const char *pszBaseAuthorityCode = nullptr;
10249
0
        if (adfTOWGS84 == std::vector<double>(7) &&
10250
0
            (pszAuthorityName = poSRS->GetAuthorityName(nullptr)) != nullptr &&
10251
0
            EQUAL(pszAuthorityName, "EPSG") &&
10252
0
            (pszAuthorityCode = poSRS->GetAuthorityCode(nullptr)) != nullptr &&
10253
0
            (pszBaseAuthorityName = poBaseGeogCRS->GetAuthorityName(nullptr)) !=
10254
0
                nullptr &&
10255
0
            EQUAL(pszBaseAuthorityName, "EPSG") &&
10256
0
            (pszBaseAuthorityCode = poBaseGeogCRS->GetAuthorityCode(nullptr)) !=
10257
0
                nullptr &&
10258
0
            (EQUAL(pszBaseAuthorityCode, "4326") ||
10259
0
             EQUAL(pszBaseAuthorityCode, "4258")))
10260
0
        {
10261
0
            poSRS->importFromEPSG(atoi(pszAuthorityCode));
10262
0
        }
10263
10264
0
        CPLFree(pahSRS);
10265
0
        CPLFree(panConfidence);
10266
10267
0
        return poSRS;
10268
0
    }
10269
0
    else
10270
0
    {
10271
        // If there are several matches >= nMinimumMatchConfidence, take the
10272
        // only one that is under pszPreferredAuthority
10273
0
        int iBestEntry = -1;
10274
0
        for (int i = 0; i < nEntries; i++)
10275
0
        {
10276
0
            if (panConfidence[i] >= nMinimumMatchConfidence)
10277
0
            {
10278
0
                const char *pszAuthName =
10279
0
                    OGRSpatialReference::FromHandle(pahSRS[i])
10280
0
                        ->GetAuthorityName(nullptr);
10281
0
                if (pszAuthName != nullptr &&
10282
0
                    EQUAL(pszAuthName, pszPreferredAuthority))
10283
0
                {
10284
0
                    if (iBestEntry < 0)
10285
0
                        iBestEntry = i;
10286
0
                    else
10287
0
                    {
10288
0
                        iBestEntry = -1;
10289
0
                        break;
10290
0
                    }
10291
0
                }
10292
0
            }
10293
0
        }
10294
0
        if (iBestEntry >= 0)
10295
0
        {
10296
0
            auto poRet = OGRSpatialReference::FromHandle(pahSRS[0])->Clone();
10297
0
            OSRFreeSRSArray(pahSRS);
10298
0
            CPLFree(panConfidence);
10299
0
            return poRet;
10300
0
        }
10301
0
    }
10302
0
    OSRFreeSRSArray(pahSRS);
10303
0
    CPLFree(panConfidence);
10304
0
    return nullptr;
10305
0
}
10306
10307
/************************************************************************/
10308
/*                             SetTOWGS84()                             */
10309
/************************************************************************/
10310
10311
/**
10312
 * \brief Set the Bursa-Wolf conversion to WGS84.
10313
 *
10314
 * This will create the TOWGS84 node as a child of the DATUM.  It will fail
10315
 * if there is no existing DATUM node. It will replace
10316
 * an existing TOWGS84 node if there is one.
10317
 *
10318
 * The parameters have the same meaning as EPSG transformation 9606
10319
 * (Position Vector 7-param. transformation).
10320
 *
10321
 * This method is the same as the C function OSRSetTOWGS84().
10322
 *
10323
 * @param dfDX X child in meters.
10324
 * @param dfDY Y child in meters.
10325
 * @param dfDZ Z child in meters.
10326
 * @param dfEX X rotation in arc seconds (optional, defaults to zero).
10327
 * @param dfEY Y rotation in arc seconds (optional, defaults to zero).
10328
 * @param dfEZ Z rotation in arc seconds (optional, defaults to zero).
10329
 * @param dfPPM scaling factor (parts per million).
10330
 *
10331
 * @return OGRERR_NONE on success.
10332
 */
10333
10334
OGRErr OGRSpatialReference::SetTOWGS84(double dfDX, double dfDY, double dfDZ,
10335
                                       double dfEX, double dfEY, double dfEZ,
10336
                                       double dfPPM)
10337
10338
0
{
10339
0
    TAKE_OPTIONAL_LOCK();
10340
10341
0
    d->refreshProjObj();
10342
0
    if (d->m_pj_crs == nullptr)
10343
0
    {
10344
0
        return OGRERR_FAILURE;
10345
0
    }
10346
10347
    // Remove existing BoundCRS
10348
0
    if (d->m_pjType == PJ_TYPE_BOUND_CRS)
10349
0
    {
10350
0
        auto baseCRS = proj_get_source_crs(d->getPROJContext(), d->m_pj_crs);
10351
0
        if (!baseCRS)
10352
0
            return OGRERR_FAILURE;
10353
0
        d->setPjCRS(baseCRS);
10354
0
    }
10355
10356
0
    PJ_PARAM_DESCRIPTION params[7];
10357
10358
0
    params[0].name = EPSG_NAME_PARAMETER_X_AXIS_TRANSLATION;
10359
0
    params[0].auth_name = "EPSG";
10360
0
    params[0].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_TRANSLATION);
10361
0
    params[0].value = dfDX;
10362
0
    params[0].unit_name = "metre";
10363
0
    params[0].unit_conv_factor = 1.0;
10364
0
    params[0].unit_type = PJ_UT_LINEAR;
10365
10366
0
    params[1].name = EPSG_NAME_PARAMETER_Y_AXIS_TRANSLATION;
10367
0
    params[1].auth_name = "EPSG";
10368
0
    params[1].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_TRANSLATION);
10369
0
    params[1].value = dfDY;
10370
0
    params[1].unit_name = "metre";
10371
0
    params[1].unit_conv_factor = 1.0;
10372
0
    params[1].unit_type = PJ_UT_LINEAR;
10373
10374
0
    params[2].name = EPSG_NAME_PARAMETER_Z_AXIS_TRANSLATION;
10375
0
    params[2].auth_name = "EPSG";
10376
0
    params[2].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_TRANSLATION);
10377
0
    params[2].value = dfDZ;
10378
0
    params[2].unit_name = "metre";
10379
0
    params[2].unit_conv_factor = 1.0;
10380
0
    params[2].unit_type = PJ_UT_LINEAR;
10381
10382
0
    params[3].name = EPSG_NAME_PARAMETER_X_AXIS_ROTATION;
10383
0
    params[3].auth_name = "EPSG";
10384
0
    params[3].code = XSTRINGIFY(EPSG_CODE_PARAMETER_X_AXIS_ROTATION);
10385
0
    params[3].value = dfEX;
10386
0
    params[3].unit_name = "arc-second";
10387
0
    params[3].unit_conv_factor = 1. / 3600 * M_PI / 180;
10388
0
    params[3].unit_type = PJ_UT_ANGULAR;
10389
10390
0
    params[4].name = EPSG_NAME_PARAMETER_Y_AXIS_ROTATION;
10391
0
    params[4].auth_name = "EPSG";
10392
0
    params[4].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Y_AXIS_ROTATION);
10393
0
    params[4].value = dfEY;
10394
0
    params[4].unit_name = "arc-second";
10395
0
    params[4].unit_conv_factor = 1. / 3600 * M_PI / 180;
10396
0
    params[4].unit_type = PJ_UT_ANGULAR;
10397
10398
0
    params[5].name = EPSG_NAME_PARAMETER_Z_AXIS_ROTATION;
10399
0
    params[5].auth_name = "EPSG";
10400
0
    params[5].code = XSTRINGIFY(EPSG_CODE_PARAMETER_Z_AXIS_ROTATION);
10401
0
    params[5].value = dfEZ;
10402
0
    params[5].unit_name = "arc-second";
10403
0
    params[5].unit_conv_factor = 1. / 3600 * M_PI / 180;
10404
0
    params[5].unit_type = PJ_UT_ANGULAR;
10405
10406
0
    params[6].name = EPSG_NAME_PARAMETER_SCALE_DIFFERENCE;
10407
0
    params[6].auth_name = "EPSG";
10408
0
    params[6].code = XSTRINGIFY(EPSG_CODE_PARAMETER_SCALE_DIFFERENCE);
10409
0
    params[6].value = dfPPM;
10410
0
    params[6].unit_name = "parts per million";
10411
0
    params[6].unit_conv_factor = 1e-6;
10412
0
    params[6].unit_type = PJ_UT_SCALE;
10413
10414
0
    auto sourceCRS =
10415
0
        proj_crs_get_geodetic_crs(d->getPROJContext(), d->m_pj_crs);
10416
0
    if (!sourceCRS)
10417
0
    {
10418
0
        return OGRERR_FAILURE;
10419
0
    }
10420
10421
0
    const auto sourceType = proj_get_type(sourceCRS);
10422
10423
0
    auto targetCRS = proj_create_from_database(
10424
0
        d->getPROJContext(), "EPSG",
10425
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS   ? "4326"
10426
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS ? "4979"
10427
0
                                                  : "4978",
10428
0
        PJ_CATEGORY_CRS, false, nullptr);
10429
0
    if (!targetCRS)
10430
0
    {
10431
0
        proj_destroy(sourceCRS);
10432
0
        return OGRERR_FAILURE;
10433
0
    }
10434
10435
0
    CPLString osMethodCode;
10436
0
    osMethodCode.Printf("%d",
10437
0
                        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10438
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10439
0
                        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10440
0
                            ? EPSG_CODE_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10441
0
                            : EPSG_CODE_METHOD_POSITION_VECTOR_GEOCENTRIC);
10442
10443
0
    auto transf = proj_create_transformation(
10444
0
        d->getPROJContext(), "Transformation to WGS84", nullptr, nullptr,
10445
0
        sourceCRS, targetCRS, nullptr,
10446
0
        sourceType == PJ_TYPE_GEOGRAPHIC_2D_CRS
10447
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_2D
10448
0
        : sourceType == PJ_TYPE_GEOGRAPHIC_3D_CRS
10449
0
            ? EPSG_NAME_METHOD_POSITION_VECTOR_GEOGRAPHIC_3D
10450
0
            : EPSG_NAME_METHOD_POSITION_VECTOR_GEOCENTRIC,
10451
0
        "EPSG", osMethodCode.c_str(), 7, params, -1);
10452
0
    proj_destroy(sourceCRS);
10453
0
    if (!transf)
10454
0
    {
10455
0
        proj_destroy(targetCRS);
10456
0
        return OGRERR_FAILURE;
10457
0
    }
10458
10459
0
    auto newBoundCRS = proj_crs_create_bound_crs(
10460
0
        d->getPROJContext(), d->m_pj_crs, targetCRS, transf);
10461
0
    proj_destroy(transf);
10462
0
    proj_destroy(targetCRS);
10463
0
    if (!newBoundCRS)
10464
0
    {
10465
0
        return OGRERR_FAILURE;
10466
0
    }
10467
10468
0
    d->setPjCRS(newBoundCRS);
10469
0
    return OGRERR_NONE;
10470
0
}
10471
10472
/************************************************************************/
10473
/*                           OSRSetTOWGS84()                            */
10474
/************************************************************************/
10475
10476
/**
10477
 * \brief Set the Bursa-Wolf conversion to WGS84.
10478
 *
10479
 * This function is the same as OGRSpatialReference::SetTOWGS84().
10480
 */
10481
OGRErr OSRSetTOWGS84(OGRSpatialReferenceH hSRS, double dfDX, double dfDY,
10482
                     double dfDZ, double dfEX, double dfEY, double dfEZ,
10483
                     double dfPPM)
10484
10485
0
{
10486
0
    VALIDATE_POINTER1(hSRS, "OSRSetTOWGS84", OGRERR_FAILURE);
10487
10488
0
    return ToPointer(hSRS)->SetTOWGS84(dfDX, dfDY, dfDZ, dfEX, dfEY, dfEZ,
10489
0
                                       dfPPM);
10490
0
}
10491
10492
/************************************************************************/
10493
/*                             GetTOWGS84()                             */
10494
/************************************************************************/
10495
10496
/**
10497
 * \brief Fetch TOWGS84 parameters, if available.
10498
 *
10499
 * The parameters have the same meaning as EPSG transformation 9606
10500
 * (Position Vector 7-param. transformation).
10501
 *
10502
 * @param padfCoeff array into which up to 7 coefficients are placed.
10503
 * @param nCoeffCount size of padfCoeff - defaults to 7.
10504
 *
10505
 * @return OGRERR_NONE on success, or OGRERR_FAILURE if there is no
10506
 * TOWGS84 node available.
10507
 */
10508
10509
OGRErr OGRSpatialReference::GetTOWGS84(double *padfCoeff, int nCoeffCount) const
10510
10511
0
{
10512
0
    TAKE_OPTIONAL_LOCK();
10513
10514
0
    d->refreshProjObj();
10515
0
    if (d->m_pjType != PJ_TYPE_BOUND_CRS)
10516
0
        return OGRERR_FAILURE;
10517
10518
0
    memset(padfCoeff, 0, sizeof(double) * nCoeffCount);
10519
10520
0
    auto transf = proj_crs_get_coordoperation(d->getPROJContext(), d->m_pj_crs);
10521
0
    int success = proj_coordoperation_get_towgs84_values(
10522
0
        d->getPROJContext(), transf, padfCoeff, nCoeffCount, false);
10523
0
    proj_destroy(transf);
10524
10525
0
    return success ? OGRERR_NONE : OGRERR_FAILURE;
10526
0
}
10527
10528
/************************************************************************/
10529
/*                           OSRGetTOWGS84()                            */
10530
/************************************************************************/
10531
10532
/**
10533
 * \brief Fetch TOWGS84 parameters, if available.
10534
 *
10535
 * This function is the same as OGRSpatialReference::GetTOWGS84().
10536
 */
10537
OGRErr OSRGetTOWGS84(OGRSpatialReferenceH hSRS, double *padfCoeff,
10538
                     int nCoeffCount)
10539
10540
0
{
10541
0
    VALIDATE_POINTER1(hSRS, "OSRGetTOWGS84", OGRERR_FAILURE);
10542
10543
0
    return ToPointer(hSRS)->GetTOWGS84(padfCoeff, nCoeffCount);
10544
0
}
10545
10546
/************************************************************************/
10547
/*                         IsAngularParameter()                         */
10548
/************************************************************************/
10549
10550
/** Is the passed projection parameter an angular one?
10551
 *
10552
 * @return TRUE or FALSE
10553
 */
10554
10555
/* static */
10556
int OGRSpatialReference::IsAngularParameter(const char *pszParameterName)
10557
10558
0
{
10559
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10560
0
        STARTS_WITH_CI(pszParameterName, "lati") ||
10561
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN) ||
10562
0
        STARTS_WITH_CI(pszParameterName, "standard_parallel") ||
10563
0
        EQUAL(pszParameterName, SRS_PP_AZIMUTH) ||
10564
0
        EQUAL(pszParameterName, SRS_PP_RECTIFIED_GRID_ANGLE))
10565
0
        return TRUE;
10566
10567
0
    return FALSE;
10568
0
}
10569
10570
/************************************************************************/
10571
/*                        IsLongitudeParameter()                        */
10572
/************************************************************************/
10573
10574
/** Is the passed projection parameter an angular longitude
10575
 * (relative to a prime meridian)?
10576
 *
10577
 * @return TRUE or FALSE
10578
 */
10579
10580
/* static */
10581
int OGRSpatialReference::IsLongitudeParameter(const char *pszParameterName)
10582
10583
0
{
10584
0
    if (STARTS_WITH_CI(pszParameterName, "long") ||
10585
0
        EQUAL(pszParameterName, SRS_PP_CENTRAL_MERIDIAN))
10586
0
        return TRUE;
10587
10588
0
    return FALSE;
10589
0
}
10590
10591
/************************************************************************/
10592
/*                         IsLinearParameter()                          */
10593
/************************************************************************/
10594
10595
/** Is the passed projection parameter an linear one measured in meters or
10596
 * some similar linear measure.
10597
 *
10598
 * @return TRUE or FALSE
10599
 */
10600
10601
/* static */
10602
int OGRSpatialReference::IsLinearParameter(const char *pszParameterName)
10603
10604
0
{
10605
0
    if (STARTS_WITH_CI(pszParameterName, "false_") ||
10606
0
        EQUAL(pszParameterName, SRS_PP_SATELLITE_HEIGHT))
10607
0
        return TRUE;
10608
10609
0
    return FALSE;
10610
0
}
10611
10612
/************************************************************************/
10613
/*                            GetNormInfo()                             */
10614
/************************************************************************/
10615
10616
/**
10617
 * \brief Set the internal information for normalizing linear, and angular
10618
 * values.
10619
 */
10620
void OGRSpatialReference::GetNormInfo() const
10621
10622
0
{
10623
0
    TAKE_OPTIONAL_LOCK();
10624
10625
0
    if (d->bNormInfoSet)
10626
0
        return;
10627
10628
    /* -------------------------------------------------------------------- */
10629
    /*      Initialize values.                                              */
10630
    /* -------------------------------------------------------------------- */
10631
0
    d->bNormInfoSet = TRUE;
10632
10633
0
    d->dfFromGreenwich = GetPrimeMeridian(nullptr);
10634
0
    d->dfToMeter = GetLinearUnits(nullptr);
10635
0
    d->dfToDegrees = GetAngularUnits(nullptr) / CPLAtof(SRS_UA_DEGREE_CONV);
10636
0
    if (fabs(d->dfToDegrees - 1.0) < 0.000000001)
10637
0
        d->dfToDegrees = 1.0;
10638
0
}
10639
10640
/************************************************************************/
10641
/*                            GetExtension()                            */
10642
/************************************************************************/
10643
10644
/**
10645
 * \brief Fetch extension value.
10646
 *
10647
 * Fetch the value of the named EXTENSION item for the identified
10648
 * target node.
10649
 *
10650
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10651
 * @param pszName the name of the extension being fetched.
10652
 * @param pszDefault the value to return if the extension is not found.
10653
 *
10654
 * @return node value if successful or pszDefault on failure.
10655
 */
10656
10657
const char *OGRSpatialReference::GetExtension(const char *pszTargetKey,
10658
                                              const char *pszName,
10659
                                              const char *pszDefault) const
10660
10661
0
{
10662
0
    TAKE_OPTIONAL_LOCK();
10663
10664
    /* -------------------------------------------------------------------- */
10665
    /*      Find the target node.                                           */
10666
    /* -------------------------------------------------------------------- */
10667
0
    const OGR_SRSNode *poNode =
10668
0
        pszTargetKey == nullptr ? GetRoot() : GetAttrNode(pszTargetKey);
10669
10670
0
    if (poNode == nullptr)
10671
0
        return nullptr;
10672
10673
    /* -------------------------------------------------------------------- */
10674
    /*      Fetch matching EXTENSION if there is one.                       */
10675
    /* -------------------------------------------------------------------- */
10676
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10677
0
    {
10678
0
        const OGR_SRSNode *poChild = poNode->GetChild(i);
10679
10680
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10681
0
            poChild->GetChildCount() >= 2)
10682
0
        {
10683
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10684
0
                return poChild->GetChild(1)->GetValue();
10685
0
        }
10686
0
    }
10687
10688
0
    return pszDefault;
10689
0
}
10690
10691
/************************************************************************/
10692
/*                            SetExtension()                            */
10693
/************************************************************************/
10694
/**
10695
 * \brief Set extension value.
10696
 *
10697
 * Set the value of the named EXTENSION item for the identified
10698
 * target node.
10699
 *
10700
 * @param pszTargetKey the name or path to the parent node of the EXTENSION.
10701
 * @param pszName the name of the extension being fetched.
10702
 * @param pszValue the value to set
10703
 *
10704
 * @return OGRERR_NONE on success
10705
 */
10706
10707
OGRErr OGRSpatialReference::SetExtension(const char *pszTargetKey,
10708
                                         const char *pszName,
10709
                                         const char *pszValue)
10710
10711
0
{
10712
0
    TAKE_OPTIONAL_LOCK();
10713
10714
    /* -------------------------------------------------------------------- */
10715
    /*      Find the target node.                                           */
10716
    /* -------------------------------------------------------------------- */
10717
0
    OGR_SRSNode *poNode = nullptr;
10718
10719
0
    if (pszTargetKey == nullptr)
10720
0
        poNode = GetRoot();
10721
0
    else
10722
0
        poNode = GetAttrNode(pszTargetKey);
10723
10724
0
    if (poNode == nullptr)
10725
0
        return OGRERR_FAILURE;
10726
10727
    /* -------------------------------------------------------------------- */
10728
    /*      Fetch matching EXTENSION if there is one.                       */
10729
    /* -------------------------------------------------------------------- */
10730
0
    for (int i = poNode->GetChildCount() - 1; i >= 0; i--)
10731
0
    {
10732
0
        OGR_SRSNode *poChild = poNode->GetChild(i);
10733
10734
0
        if (EQUAL(poChild->GetValue(), "EXTENSION") &&
10735
0
            poChild->GetChildCount() >= 2)
10736
0
        {
10737
0
            if (EQUAL(poChild->GetChild(0)->GetValue(), pszName))
10738
0
            {
10739
0
                poChild->GetChild(1)->SetValue(pszValue);
10740
0
                return OGRERR_NONE;
10741
0
            }
10742
0
        }
10743
0
    }
10744
10745
    /* -------------------------------------------------------------------- */
10746
    /*      Create a new EXTENSION node.                                    */
10747
    /* -------------------------------------------------------------------- */
10748
0
    OGR_SRSNode *poAuthNode = new OGR_SRSNode("EXTENSION");
10749
0
    poAuthNode->AddChild(new OGR_SRSNode(pszName));
10750
0
    poAuthNode->AddChild(new OGR_SRSNode(pszValue));
10751
10752
0
    poNode->AddChild(poAuthNode);
10753
10754
0
    return OGRERR_NONE;
10755
0
}
10756
10757
/************************************************************************/
10758
/*                             OSRCleanup()                             */
10759
/************************************************************************/
10760
10761
static void CleanupSRSWGS84Mutex();
10762
10763
/**
10764
 * \brief Cleanup cached SRS related memory.
10765
 *
10766
 * This function will attempt to cleanup any cache spatial reference
10767
 * related information, such as cached tables of coordinate systems.
10768
 *
10769
 * This function should not be called concurrently with any other GDAL/OGR
10770
 * function. It is meant at being called once before process termination
10771
 * (typically from the main thread). CPLCleanupTLS() might be used to clean
10772
 * thread-specific resources before thread termination.
10773
 */
10774
void OSRCleanup(void)
10775
10776
0
{
10777
0
    OGRCTDumpStatistics();
10778
0
    CSVDeaccess(nullptr);
10779
0
    CleanupSRSWGS84Mutex();
10780
0
    OSRCTCleanCache();
10781
0
    OSRCleanupTLSContext();
10782
0
}
10783
10784
/************************************************************************/
10785
/*                              GetAxesCount()                          */
10786
/************************************************************************/
10787
10788
/**
10789
 * \brief Return the number of axis of the coordinate system of the CRS.
10790
 *
10791
 * @since GDAL 3.0
10792
 */
10793
int OGRSpatialReference::GetAxesCount() const
10794
0
{
10795
0
    TAKE_OPTIONAL_LOCK();
10796
10797
0
    int axisCount = 0;
10798
0
    d->refreshProjObj();
10799
0
    if (d->m_pj_crs == nullptr)
10800
0
    {
10801
0
        return 0;
10802
0
    }
10803
0
    d->demoteFromBoundCRS();
10804
0
    auto ctxt = d->getPROJContext();
10805
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10806
0
    {
10807
0
        for (int i = 0;; i++)
10808
0
        {
10809
0
            auto subCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, i);
10810
0
            if (!subCRS)
10811
0
                break;
10812
0
            if (proj_get_type(subCRS) == PJ_TYPE_BOUND_CRS)
10813
0
            {
10814
0
                auto baseCRS = proj_get_source_crs(ctxt, subCRS);
10815
0
                if (baseCRS)
10816
0
                {
10817
0
                    proj_destroy(subCRS);
10818
0
                    subCRS = baseCRS;
10819
0
                }
10820
0
            }
10821
0
            auto cs = proj_crs_get_coordinate_system(ctxt, subCRS);
10822
0
            if (cs)
10823
0
            {
10824
0
                axisCount += proj_cs_get_axis_count(ctxt, cs);
10825
0
                proj_destroy(cs);
10826
0
            }
10827
0
            proj_destroy(subCRS);
10828
0
        }
10829
0
    }
10830
0
    else
10831
0
    {
10832
0
        auto cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10833
0
        if (cs)
10834
0
        {
10835
0
            axisCount = proj_cs_get_axis_count(ctxt, cs);
10836
0
            proj_destroy(cs);
10837
0
        }
10838
0
    }
10839
0
    d->undoDemoteFromBoundCRS();
10840
0
    return axisCount;
10841
0
}
10842
10843
/************************************************************************/
10844
/*                           OSRGetAxesCount()                          */
10845
/************************************************************************/
10846
10847
/**
10848
 * \brief Return the number of axis of the coordinate system of the CRS.
10849
 *
10850
 * This method is the equivalent of the C++ method
10851
 * OGRSpatialReference::GetAxesCount()
10852
 *
10853
 * @since GDAL 3.1
10854
 */
10855
int OSRGetAxesCount(OGRSpatialReferenceH hSRS)
10856
10857
0
{
10858
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxesCount", 0);
10859
10860
0
    return ToPointer(hSRS)->GetAxesCount();
10861
0
}
10862
10863
/************************************************************************/
10864
/*                              GetAxis()                               */
10865
/************************************************************************/
10866
10867
/**
10868
 * \brief Fetch the orientation of one axis.
10869
 *
10870
 * Fetches the request axis (iAxis - zero based) from the
10871
 * indicated portion of the coordinate system (pszTargetKey) which
10872
 * should be either "GEOGCS" or "PROJCS".
10873
 *
10874
 * No CPLError is issued on routine failures (such as not finding the AXIS).
10875
 *
10876
 * This method is equivalent to the C function OSRGetAxis().
10877
 *
10878
 * @param pszTargetKey the coordinate system part to query ("PROJCS" or
10879
 * "GEOGCS").
10880
 * @param iAxis the axis to query (0 for first, 1 for second, 2 for third).
10881
 * @param peOrientation location into which to place the fetch orientation, may
10882
 * be NULL.
10883
 * @param pdfConvUnit (GDAL >= 3.4) Location into which to place axis conversion
10884
 * factor. May be NULL. Only set if pszTargetKey == NULL
10885
 *
10886
 * @return the name of the axis or NULL on failure.
10887
 */
10888
10889
const char *OGRSpatialReference::GetAxis(const char *pszTargetKey, int iAxis,
10890
                                         OGRAxisOrientation *peOrientation,
10891
                                         double *pdfConvUnit) const
10892
10893
0
{
10894
0
    TAKE_OPTIONAL_LOCK();
10895
10896
0
    if (peOrientation != nullptr)
10897
0
        *peOrientation = OAO_Other;
10898
0
    if (pdfConvUnit != nullptr)
10899
0
        *pdfConvUnit = 0;
10900
10901
0
    d->refreshProjObj();
10902
0
    if (d->m_pj_crs == nullptr)
10903
0
    {
10904
0
        return nullptr;
10905
0
    }
10906
10907
0
    pszTargetKey = d->nullifyTargetKeyIfPossible(pszTargetKey);
10908
0
    if (pszTargetKey == nullptr && iAxis <= 2)
10909
0
    {
10910
0
        auto ctxt = d->getPROJContext();
10911
10912
0
        int iAxisModified = iAxis;
10913
10914
0
        d->demoteFromBoundCRS();
10915
10916
0
        PJ *cs = nullptr;
10917
0
        if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
10918
0
        {
10919
0
            auto horizCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
10920
0
            if (horizCRS)
10921
0
            {
10922
0
                if (proj_get_type(horizCRS) == PJ_TYPE_BOUND_CRS)
10923
0
                {
10924
0
                    auto baseCRS = proj_get_source_crs(ctxt, horizCRS);
10925
0
                    if (baseCRS)
10926
0
                    {
10927
0
                        proj_destroy(horizCRS);
10928
0
                        horizCRS = baseCRS;
10929
0
                    }
10930
0
                }
10931
0
                cs = proj_crs_get_coordinate_system(ctxt, horizCRS);
10932
0
                proj_destroy(horizCRS);
10933
0
                if (cs)
10934
0
                {
10935
0
                    if (iAxisModified >= proj_cs_get_axis_count(ctxt, cs))
10936
0
                    {
10937
0
                        iAxisModified -= proj_cs_get_axis_count(ctxt, cs);
10938
0
                        proj_destroy(cs);
10939
0
                        cs = nullptr;
10940
0
                    }
10941
0
                }
10942
0
            }
10943
10944
0
            if (cs == nullptr)
10945
0
            {
10946
0
                auto vertCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 1);
10947
0
                if (vertCRS)
10948
0
                {
10949
0
                    if (proj_get_type(vertCRS) == PJ_TYPE_BOUND_CRS)
10950
0
                    {
10951
0
                        auto baseCRS = proj_get_source_crs(ctxt, vertCRS);
10952
0
                        if (baseCRS)
10953
0
                        {
10954
0
                            proj_destroy(vertCRS);
10955
0
                            vertCRS = baseCRS;
10956
0
                        }
10957
0
                    }
10958
10959
0
                    cs = proj_crs_get_coordinate_system(ctxt, vertCRS);
10960
0
                    proj_destroy(vertCRS);
10961
0
                }
10962
0
            }
10963
0
        }
10964
0
        else
10965
0
        {
10966
0
            cs = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
10967
0
        }
10968
10969
0
        if (cs)
10970
0
        {
10971
0
            const char *pszName = nullptr;
10972
0
            const char *pszOrientation = nullptr;
10973
0
            double dfConvFactor = 0.0;
10974
0
            proj_cs_get_axis_info(ctxt, cs, iAxisModified, &pszName, nullptr,
10975
0
                                  &pszOrientation, &dfConvFactor, nullptr,
10976
0
                                  nullptr, nullptr);
10977
10978
0
            if (pdfConvUnit != nullptr)
10979
0
            {
10980
0
                *pdfConvUnit = dfConvFactor;
10981
0
            }
10982
10983
0
            if (pszName && pszOrientation)
10984
0
            {
10985
0
                d->m_osAxisName[iAxis] = pszName;
10986
0
                if (peOrientation)
10987
0
                {
10988
0
                    if (EQUAL(pszOrientation, "NORTH"))
10989
0
                        *peOrientation = OAO_North;
10990
0
                    else if (EQUAL(pszOrientation, "EAST"))
10991
0
                        *peOrientation = OAO_East;
10992
0
                    else if (EQUAL(pszOrientation, "SOUTH"))
10993
0
                        *peOrientation = OAO_South;
10994
0
                    else if (EQUAL(pszOrientation, "WEST"))
10995
0
                        *peOrientation = OAO_West;
10996
0
                    else if (EQUAL(pszOrientation, "UP"))
10997
0
                        *peOrientation = OAO_Up;
10998
0
                    else if (EQUAL(pszOrientation, "DOWN"))
10999
0
                        *peOrientation = OAO_Down;
11000
0
                }
11001
0
                proj_destroy(cs);
11002
0
                d->undoDemoteFromBoundCRS();
11003
0
                return d->m_osAxisName[iAxis].c_str();
11004
0
            }
11005
0
            proj_destroy(cs);
11006
0
        }
11007
0
        d->undoDemoteFromBoundCRS();
11008
0
    }
11009
11010
    /* -------------------------------------------------------------------- */
11011
    /*      Find the target node.                                           */
11012
    /* -------------------------------------------------------------------- */
11013
0
    const OGR_SRSNode *poNode = nullptr;
11014
11015
0
    if (pszTargetKey == nullptr)
11016
0
        poNode = GetRoot();
11017
0
    else
11018
0
        poNode = GetAttrNode(pszTargetKey);
11019
11020
0
    if (poNode == nullptr)
11021
0
        return nullptr;
11022
11023
    /* -------------------------------------------------------------------- */
11024
    /*      Find desired child AXIS.                                        */
11025
    /* -------------------------------------------------------------------- */
11026
0
    const OGR_SRSNode *poAxis = nullptr;
11027
0
    const int nChildCount = poNode->GetChildCount();
11028
11029
0
    for (int iChild = 0; iChild < nChildCount; iChild++)
11030
0
    {
11031
0
        const OGR_SRSNode *poChild = poNode->GetChild(iChild);
11032
11033
0
        if (!EQUAL(poChild->GetValue(), "AXIS"))
11034
0
            continue;
11035
11036
0
        if (iAxis == 0)
11037
0
        {
11038
0
            poAxis = poChild;
11039
0
            break;
11040
0
        }
11041
0
        iAxis--;
11042
0
    }
11043
11044
0
    if (poAxis == nullptr)
11045
0
        return nullptr;
11046
11047
0
    if (poAxis->GetChildCount() < 2)
11048
0
        return nullptr;
11049
11050
    /* -------------------------------------------------------------------- */
11051
    /*      Extract name and orientation if possible.                       */
11052
    /* -------------------------------------------------------------------- */
11053
0
    if (peOrientation != nullptr)
11054
0
    {
11055
0
        const char *pszOrientation = poAxis->GetChild(1)->GetValue();
11056
11057
0
        if (EQUAL(pszOrientation, "NORTH"))
11058
0
            *peOrientation = OAO_North;
11059
0
        else if (EQUAL(pszOrientation, "EAST"))
11060
0
            *peOrientation = OAO_East;
11061
0
        else if (EQUAL(pszOrientation, "SOUTH"))
11062
0
            *peOrientation = OAO_South;
11063
0
        else if (EQUAL(pszOrientation, "WEST"))
11064
0
            *peOrientation = OAO_West;
11065
0
        else if (EQUAL(pszOrientation, "UP"))
11066
0
            *peOrientation = OAO_Up;
11067
0
        else if (EQUAL(pszOrientation, "DOWN"))
11068
0
            *peOrientation = OAO_Down;
11069
0
        else if (EQUAL(pszOrientation, "OTHER"))
11070
0
            *peOrientation = OAO_Other;
11071
0
        else
11072
0
        {
11073
0
            CPLDebug("OSR", "Unrecognized orientation value '%s'.",
11074
0
                     pszOrientation);
11075
0
        }
11076
0
    }
11077
11078
0
    return poAxis->GetChild(0)->GetValue();
11079
0
}
11080
11081
/************************************************************************/
11082
/*                             OSRGetAxis()                             */
11083
/************************************************************************/
11084
11085
/**
11086
 * \brief Fetch the orientation of one axis.
11087
 *
11088
 * This method is the equivalent of the C++ method OGRSpatialReference::GetAxis
11089
 */
11090
const char *OSRGetAxis(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11091
                       int iAxis, OGRAxisOrientation *peOrientation)
11092
11093
0
{
11094
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxis", nullptr);
11095
11096
0
    return ToPointer(hSRS)->GetAxis(pszTargetKey, iAxis, peOrientation);
11097
0
}
11098
11099
/************************************************************************/
11100
/*                         OSRAxisEnumToName()                          */
11101
/************************************************************************/
11102
11103
/**
11104
 * \brief Return the string representation for the OGRAxisOrientation
11105
 * enumeration.
11106
 *
11107
 * For example "NORTH" for OAO_North.
11108
 *
11109
 * @return an internal string
11110
 */
11111
const char *OSRAxisEnumToName(OGRAxisOrientation eOrientation)
11112
11113
0
{
11114
0
    if (eOrientation == OAO_North)
11115
0
        return "NORTH";
11116
0
    if (eOrientation == OAO_East)
11117
0
        return "EAST";
11118
0
    if (eOrientation == OAO_South)
11119
0
        return "SOUTH";
11120
0
    if (eOrientation == OAO_West)
11121
0
        return "WEST";
11122
0
    if (eOrientation == OAO_Up)
11123
0
        return "UP";
11124
0
    if (eOrientation == OAO_Down)
11125
0
        return "DOWN";
11126
0
    if (eOrientation == OAO_Other)
11127
0
        return "OTHER";
11128
11129
0
    return "UNKNOWN";
11130
0
}
11131
11132
/************************************************************************/
11133
/*                              SetAxes()                               */
11134
/************************************************************************/
11135
11136
/**
11137
 * \brief Set the axes for a coordinate system.
11138
 *
11139
 * Set the names, and orientations of the axes for either a projected
11140
 * (PROJCS) or geographic (GEOGCS) coordinate system.
11141
 *
11142
 * This method is equivalent to the C function OSRSetAxes().
11143
 *
11144
 * @param pszTargetKey either "PROJCS" or "GEOGCS", must already exist in SRS.
11145
 * @param pszXAxisName name of first axis, normally "Long" or "Easting".
11146
 * @param eXAxisOrientation normally OAO_East.
11147
 * @param pszYAxisName name of second axis, normally "Lat" or "Northing".
11148
 * @param eYAxisOrientation normally OAO_North.
11149
 *
11150
 * @return OGRERR_NONE on success or an error code.
11151
 */
11152
11153
OGRErr OGRSpatialReference::SetAxes(const char *pszTargetKey,
11154
                                    const char *pszXAxisName,
11155
                                    OGRAxisOrientation eXAxisOrientation,
11156
                                    const char *pszYAxisName,
11157
                                    OGRAxisOrientation eYAxisOrientation)
11158
11159
0
{
11160
0
    TAKE_OPTIONAL_LOCK();
11161
11162
    /* -------------------------------------------------------------------- */
11163
    /*      Find the target node.                                           */
11164
    /* -------------------------------------------------------------------- */
11165
0
    OGR_SRSNode *poNode = nullptr;
11166
11167
0
    if (pszTargetKey == nullptr)
11168
0
        poNode = GetRoot();
11169
0
    else
11170
0
        poNode = GetAttrNode(pszTargetKey);
11171
11172
0
    if (poNode == nullptr)
11173
0
        return OGRERR_FAILURE;
11174
11175
    /* -------------------------------------------------------------------- */
11176
    /*      Strip any existing AXIS children.                               */
11177
    /* -------------------------------------------------------------------- */
11178
0
    while (poNode->FindChild("AXIS") >= 0)
11179
0
        poNode->DestroyChild(poNode->FindChild("AXIS"));
11180
11181
    /* -------------------------------------------------------------------- */
11182
    /*      Insert desired axes                                             */
11183
    /* -------------------------------------------------------------------- */
11184
0
    OGR_SRSNode *poAxis = new OGR_SRSNode("AXIS");
11185
11186
0
    poAxis->AddChild(new OGR_SRSNode(pszXAxisName));
11187
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eXAxisOrientation)));
11188
11189
0
    poNode->AddChild(poAxis);
11190
11191
0
    poAxis = new OGR_SRSNode("AXIS");
11192
11193
0
    poAxis->AddChild(new OGR_SRSNode(pszYAxisName));
11194
0
    poAxis->AddChild(new OGR_SRSNode(OSRAxisEnumToName(eYAxisOrientation)));
11195
11196
0
    poNode->AddChild(poAxis);
11197
11198
0
    return OGRERR_NONE;
11199
0
}
11200
11201
/************************************************************************/
11202
/*                             OSRSetAxes()                             */
11203
/************************************************************************/
11204
/**
11205
 * \brief Set the axes for a coordinate system.
11206
 *
11207
 * This method is the equivalent of the C++ method OGRSpatialReference::SetAxes
11208
 */
11209
OGRErr OSRSetAxes(OGRSpatialReferenceH hSRS, const char *pszTargetKey,
11210
                  const char *pszXAxisName,
11211
                  OGRAxisOrientation eXAxisOrientation,
11212
                  const char *pszYAxisName,
11213
                  OGRAxisOrientation eYAxisOrientation)
11214
0
{
11215
0
    VALIDATE_POINTER1(hSRS, "OSRSetAxes", OGRERR_FAILURE);
11216
11217
0
    return ToPointer(hSRS)->SetAxes(pszTargetKey, pszXAxisName,
11218
0
                                    eXAxisOrientation, pszYAxisName,
11219
0
                                    eYAxisOrientation);
11220
0
}
11221
11222
/************************************************************************/
11223
/*                       OSRExportToMICoordSys()                        */
11224
/************************************************************************/
11225
/**
11226
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11227
 *
11228
 * This method is the equivalent of the C++ method
11229
 * OGRSpatialReference::exportToMICoordSys
11230
 */
11231
OGRErr OSRExportToMICoordSys(OGRSpatialReferenceH hSRS, char **ppszReturn)
11232
11233
0
{
11234
0
    VALIDATE_POINTER1(hSRS, "OSRExportToMICoordSys", OGRERR_FAILURE);
11235
11236
0
    *ppszReturn = nullptr;
11237
11238
0
    return ToPointer(hSRS)->exportToMICoordSys(ppszReturn);
11239
0
}
11240
11241
/************************************************************************/
11242
/*                         exportToMICoordSys()                         */
11243
/************************************************************************/
11244
11245
/**
11246
 * \brief Export coordinate system in Mapinfo style CoordSys format.
11247
 *
11248
 * Note that the returned WKT string should be freed with
11249
 * CPLFree() when no longer needed.  It is the responsibility of the caller.
11250
 *
11251
 * This method is the same as the C function OSRExportToMICoordSys().
11252
 *
11253
 * @param ppszResult pointer to which dynamically allocated Mapinfo CoordSys
11254
 * definition will be assigned.
11255
 *
11256
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11257
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11258
 */
11259
11260
OGRErr OGRSpatialReference::exportToMICoordSys(char **ppszResult) const
11261
11262
0
{
11263
0
    *ppszResult = MITABSpatialRef2CoordSys(this);
11264
0
    if (*ppszResult != nullptr && strlen(*ppszResult) > 0)
11265
0
        return OGRERR_NONE;
11266
11267
0
    return OGRERR_FAILURE;
11268
0
}
11269
11270
/************************************************************************/
11271
/*                       OSRImportFromMICoordSys()                      */
11272
/************************************************************************/
11273
/**
11274
 * \brief Import Mapinfo style CoordSys definition.
11275
 *
11276
 * This method is the equivalent of the C++ method
11277
 * OGRSpatialReference::importFromMICoordSys
11278
 */
11279
11280
OGRErr OSRImportFromMICoordSys(OGRSpatialReferenceH hSRS,
11281
                               const char *pszCoordSys)
11282
11283
0
{
11284
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromMICoordSys", OGRERR_FAILURE);
11285
11286
0
    return ToPointer(hSRS)->importFromMICoordSys(pszCoordSys);
11287
0
}
11288
11289
/************************************************************************/
11290
/*                        importFromMICoordSys()                        */
11291
/************************************************************************/
11292
11293
/**
11294
 * \brief Import Mapinfo style CoordSys definition.
11295
 *
11296
 * The OGRSpatialReference is initialized from the passed Mapinfo style CoordSys
11297
 * definition string.
11298
 *
11299
 * This method is the equivalent of the C function OSRImportFromMICoordSys().
11300
 *
11301
 * @param pszCoordSys Mapinfo style CoordSys definition string.
11302
 *
11303
 * @return OGRERR_NONE on success, OGRERR_FAILURE on failure,
11304
 * OGRERR_UNSUPPORTED_OPERATION if MITAB library was not linked in.
11305
 */
11306
11307
OGRErr OGRSpatialReference::importFromMICoordSys(const char *pszCoordSys)
11308
11309
0
{
11310
0
    OGRSpatialReference *poResult = MITABCoordSys2SpatialRef(pszCoordSys);
11311
11312
0
    if (poResult == nullptr)
11313
0
        return OGRERR_FAILURE;
11314
11315
0
    *this = *poResult;
11316
0
    delete poResult;
11317
11318
0
    return OGRERR_NONE;
11319
0
}
11320
11321
/************************************************************************/
11322
/*                        OSRCalcInvFlattening()                        */
11323
/************************************************************************/
11324
11325
/**
11326
 * \brief Compute inverse flattening from semi-major and semi-minor axis
11327
 *
11328
 * @param dfSemiMajor Semi-major axis length.
11329
 * @param dfSemiMinor Semi-minor axis length.
11330
 *
11331
 * @return inverse flattening, or 0 if both axis are equal.
11332
 * @since GDAL 2.0
11333
 */
11334
11335
double OSRCalcInvFlattening(double dfSemiMajor, double dfSemiMinor)
11336
0
{
11337
0
    if (fabs(dfSemiMajor - dfSemiMinor) < 1e-1)
11338
0
        return 0;
11339
0
    if (dfSemiMajor <= 0 || dfSemiMinor <= 0 || dfSemiMinor > dfSemiMajor)
11340
0
    {
11341
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11342
0
                 "OSRCalcInvFlattening(): Wrong input values");
11343
0
        return 0;
11344
0
    }
11345
11346
0
    return dfSemiMajor / (dfSemiMajor - dfSemiMinor);
11347
0
}
11348
11349
/************************************************************************/
11350
/*                        OSRCalcInvFlattening()                        */
11351
/************************************************************************/
11352
11353
/**
11354
 * \brief Compute semi-minor axis from semi-major axis and inverse flattening.
11355
 *
11356
 * @param dfSemiMajor Semi-major axis length.
11357
 * @param dfInvFlattening Inverse flattening or 0 for sphere.
11358
 *
11359
 * @return semi-minor axis
11360
 * @since GDAL 2.0
11361
 */
11362
11363
double OSRCalcSemiMinorFromInvFlattening(double dfSemiMajor,
11364
                                         double dfInvFlattening)
11365
0
{
11366
0
    if (fabs(dfInvFlattening) < 0.000000000001)
11367
0
        return dfSemiMajor;
11368
0
    if (dfSemiMajor <= 0.0 || dfInvFlattening <= 1.0)
11369
0
    {
11370
0
        CPLError(CE_Failure, CPLE_IllegalArg,
11371
0
                 "OSRCalcSemiMinorFromInvFlattening(): Wrong input values");
11372
0
        return dfSemiMajor;
11373
0
    }
11374
11375
0
    return dfSemiMajor * (1.0 - 1.0 / dfInvFlattening);
11376
0
}
11377
11378
/************************************************************************/
11379
/*                        GetWGS84SRS()                                 */
11380
/************************************************************************/
11381
11382
static OGRSpatialReference *poSRSWGS84 = nullptr;
11383
static CPLMutex *hMutex = nullptr;
11384
11385
/**
11386
 * \brief Returns an instance of a SRS object with WGS84 WKT.
11387
 *
11388
 * Note: the instance will have
11389
 * SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER)
11390
 *
11391
 * The reference counter of the returned object is not increased by this
11392
 * operation.
11393
 *
11394
 * @return instance.
11395
 * @since GDAL 2.0
11396
 */
11397
11398
OGRSpatialReference *OGRSpatialReference::GetWGS84SRS()
11399
0
{
11400
0
    CPLMutexHolderD(&hMutex);
11401
0
    if (poSRSWGS84 == nullptr)
11402
0
    {
11403
0
        poSRSWGS84 = new OGRSpatialReference(SRS_WKT_WGS84_LAT_LONG);
11404
0
        poSRSWGS84->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
11405
0
    }
11406
0
    return poSRSWGS84;
11407
0
}
11408
11409
/************************************************************************/
11410
/*                        CleanupSRSWGS84Mutex()                       */
11411
/************************************************************************/
11412
11413
static void CleanupSRSWGS84Mutex()
11414
0
{
11415
0
    if (hMutex != nullptr)
11416
0
    {
11417
0
        poSRSWGS84->Release();
11418
0
        poSRSWGS84 = nullptr;
11419
0
        CPLDestroyMutex(hMutex);
11420
0
        hMutex = nullptr;
11421
0
    }
11422
0
}
11423
11424
/************************************************************************/
11425
/*                         OSRImportFromProj4()                         */
11426
/************************************************************************/
11427
/**
11428
 * \brief Import PROJ coordinate string.
11429
 *
11430
 * This function is the same as OGRSpatialReference::importFromProj4().
11431
 */
11432
OGRErr OSRImportFromProj4(OGRSpatialReferenceH hSRS, const char *pszProj4)
11433
11434
0
{
11435
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromProj4", OGRERR_FAILURE);
11436
11437
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromProj4(pszProj4);
11438
0
}
11439
11440
/************************************************************************/
11441
/*                          importFromProj4()                           */
11442
/************************************************************************/
11443
11444
/**
11445
 * \brief Import PROJ coordinate string.
11446
 *
11447
 * The OGRSpatialReference is initialized from the passed PROJs style
11448
 * coordinate system string.
11449
 *
11450
 * Example:
11451
 *   pszProj4 = "+proj=utm +zone=11 +datum=WGS84"
11452
 *
11453
 * It is also possible to import "+init=epsg:n" style definitions. Those are
11454
 * a legacy syntax that should be avoided in the future. In particular they will
11455
 * result in CRS objects whose axis order might not correspond to the official
11456
 * EPSG axis order.
11457
 *
11458
 * This method is the equivalent of the C function OSRImportFromProj4().
11459
 *
11460
 * @param pszProj4 the PROJ style string.
11461
 *
11462
 * @return OGRERR_NONE on success or OGRERR_CORRUPT_DATA on failure.
11463
 */
11464
11465
OGRErr OGRSpatialReference::importFromProj4(const char *pszProj4)
11466
11467
0
{
11468
0
    TAKE_OPTIONAL_LOCK();
11469
11470
0
    if (strlen(pszProj4) >= 10000)
11471
0
    {
11472
0
        CPLError(CE_Failure, CPLE_AppDefined, "Too long PROJ string");
11473
0
        return OGRERR_CORRUPT_DATA;
11474
0
    }
11475
11476
    /* -------------------------------------------------------------------- */
11477
    /*      Clear any existing definition.                                  */
11478
    /* -------------------------------------------------------------------- */
11479
0
    Clear();
11480
11481
0
    CPLString osProj4(pszProj4);
11482
0
    if (osProj4.find("type=crs") == std::string::npos)
11483
0
    {
11484
0
        osProj4 += " +type=crs";
11485
0
    }
11486
11487
0
    if (osProj4.find("+init=epsg:") != std::string::npos &&
11488
0
        getenv("PROJ_USE_PROJ4_INIT_RULES") == nullptr)
11489
0
    {
11490
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11491
0
                     "+init=epsg:XXXX syntax is deprecated. It might return "
11492
0
                     "a CRS with a non-EPSG compliant axis order.");
11493
0
    }
11494
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), true);
11495
0
    d->setPjCRS(proj_create(d->getPROJContext(), osProj4.c_str()));
11496
0
    proj_context_use_proj4_init_rules(d->getPROJContext(), false);
11497
0
    return d->m_pj_crs ? OGRERR_NONE : OGRERR_CORRUPT_DATA;
11498
0
}
11499
11500
/************************************************************************/
11501
/*                          OSRExportToProj4()                          */
11502
/************************************************************************/
11503
/**
11504
 * \brief Export coordinate system in PROJ.4 legacy format.
11505
 *
11506
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11507
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11508
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11509
 * will be missing most of the time. PROJ strings to encode CRS should be
11510
 * considered as a legacy solution. Using a AUTHORITY:CODE or WKT representation
11511
 * is the recommended way.
11512
 *
11513
 * This function is the same as OGRSpatialReference::exportToProj4().
11514
 */
11515
OGRErr CPL_STDCALL OSRExportToProj4(OGRSpatialReferenceH hSRS,
11516
                                    char **ppszReturn)
11517
11518
0
{
11519
0
    VALIDATE_POINTER1(hSRS, "OSRExportToProj4", OGRERR_FAILURE);
11520
11521
0
    *ppszReturn = nullptr;
11522
11523
0
    return OGRSpatialReference::FromHandle(hSRS)->exportToProj4(ppszReturn);
11524
0
}
11525
11526
/************************************************************************/
11527
/*                           exportToProj4()                            */
11528
/************************************************************************/
11529
11530
/**
11531
 * \brief Export coordinate system in PROJ.4 legacy format.
11532
 *
11533
 * \warning Use of this function is discouraged. Its behavior in GDAL &gt;= 3 /
11534
 * PROJ &gt;= 6 is significantly different from earlier versions. In particular
11535
 * +datum will only encode WGS84, NAD27 and NAD83, and +towgs84/+nadgrids terms
11536
 * will be missing most of the time. PROJ strings to encode CRS should be
11537
 * considered as a a legacy solution. Using a AUTHORITY:CODE or WKT
11538
 * representation is the recommended way.
11539
 *
11540
 * Converts the loaded coordinate reference system into PROJ format
11541
 * to the extent possible.  The string returned in ppszProj4 should be
11542
 * deallocated by the caller with CPLFree() when no longer needed.
11543
 *
11544
 * LOCAL_CS coordinate systems are not translatable.  An empty string
11545
 * will be returned along with OGRERR_NONE.
11546
 *
11547
 * Special processing for Transverse Mercator:
11548
 * Starting with GDAL 3.0, if the OSR_USE_APPROX_TMERC configuration option is
11549
 * set to YES, the PROJ definition built from the SRS will use the +approx flag
11550
 * for the tmerc and utm projection methods, rather than the more accurate
11551
 * method.
11552
 *
11553
 * Starting with GDAL 3.0.3, this method will try to add a +towgs84 parameter,
11554
 * if there's none attached yet to the SRS and if the SRS has a EPSG code.
11555
 * See the AddGuessedTOWGS84() method for how this +towgs84 parameter may be
11556
 * added. This automatic addition may be disabled by setting the
11557
 * OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4 configuration option to NO.
11558
 *
11559
 * This method is the equivalent of the C function OSRExportToProj4().
11560
 *
11561
 * @param ppszProj4 pointer to which dynamically allocated PROJ definition
11562
 * will be assigned.
11563
 *
11564
 * @return OGRERR_NONE on success or an error code on failure.
11565
 */
11566
11567
OGRErr OGRSpatialReference::exportToProj4(char **ppszProj4) const
11568
11569
0
{
11570
    // In the past calling this method was thread-safe, even if we never
11571
    // guaranteed it. Now proj_as_proj_string() will cache the result
11572
    // internally, so this is no longer thread-safe.
11573
0
    std::lock_guard oLock(d->m_mutex);
11574
11575
0
    d->refreshProjObj();
11576
0
    if (d->m_pj_crs == nullptr || d->m_pjType == PJ_TYPE_ENGINEERING_CRS)
11577
0
    {
11578
0
        *ppszProj4 = CPLStrdup("");
11579
0
        return OGRERR_FAILURE;
11580
0
    }
11581
11582
    // OSR_USE_ETMERC is here just for legacy
11583
0
    bool bForceApproxTMerc = false;
11584
0
    const char *pszUseETMERC = CPLGetConfigOption("OSR_USE_ETMERC", nullptr);
11585
0
    if (pszUseETMERC && pszUseETMERC[0])
11586
0
    {
11587
0
        CPLErrorOnce(CE_Warning, CPLE_AppDefined,
11588
0
                     "OSR_USE_ETMERC is a legacy configuration option, which "
11589
0
                     "now has only effect when set to NO (YES is the default). "
11590
0
                     "Use OSR_USE_APPROX_TMERC=YES instead");
11591
0
        bForceApproxTMerc = !CPLTestBool(pszUseETMERC);
11592
0
    }
11593
0
    else
11594
0
    {
11595
0
        const char *pszUseApproxTMERC =
11596
0
            CPLGetConfigOption("OSR_USE_APPROX_TMERC", nullptr);
11597
0
        if (pszUseApproxTMERC && pszUseApproxTMERC[0])
11598
0
        {
11599
0
            bForceApproxTMerc = CPLTestBool(pszUseApproxTMERC);
11600
0
        }
11601
0
    }
11602
0
    const char *options[] = {
11603
0
        bForceApproxTMerc ? "USE_APPROX_TMERC=YES" : nullptr, nullptr};
11604
11605
0
    const char *projString = proj_as_proj_string(
11606
0
        d->getPROJContext(), d->m_pj_crs, PJ_PROJ_4, options);
11607
11608
0
    PJ *boundCRS = nullptr;
11609
0
    if (projString &&
11610
0
        (strstr(projString, "+datum=") == nullptr ||
11611
0
         d->m_pjType == PJ_TYPE_COMPOUND_CRS) &&
11612
0
        CPLTestBool(
11613
0
            CPLGetConfigOption("OSR_ADD_TOWGS84_ON_EXPORT_TO_PROJ4", "YES")))
11614
0
    {
11615
0
        boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
11616
0
            d->getPROJContext(), d->m_pj_crs, true,
11617
0
            strstr(projString, "+datum=") == nullptr);
11618
0
        if (boundCRS)
11619
0
        {
11620
0
            projString = proj_as_proj_string(d->getPROJContext(), boundCRS,
11621
0
                                             PJ_PROJ_4, options);
11622
0
        }
11623
0
    }
11624
11625
0
    if (projString == nullptr)
11626
0
    {
11627
0
        *ppszProj4 = CPLStrdup("");
11628
0
        proj_destroy(boundCRS);
11629
0
        return OGRERR_FAILURE;
11630
0
    }
11631
0
    *ppszProj4 = CPLStrdup(projString);
11632
0
    proj_destroy(boundCRS);
11633
0
    char *pszTypeCrs = strstr(*ppszProj4, " +type=crs");
11634
0
    if (pszTypeCrs)
11635
0
        *pszTypeCrs = '\0';
11636
0
    return OGRERR_NONE;
11637
0
}
11638
11639
/************************************************************************/
11640
/*                            morphToESRI()                             */
11641
/************************************************************************/
11642
/**
11643
 * \brief Convert in place to ESRI WKT format.
11644
 *
11645
 * The value nodes of this coordinate system are modified in various manners
11646
 * more closely map onto the ESRI concept of WKT format.  This includes
11647
 * renaming a variety of projections and arguments, and stripping out
11648
 * nodes note recognised by ESRI (like AUTHORITY and AXIS).
11649
 *
11650
 * \note Since GDAL 3.0, this function has only user-visible effects at
11651
 * exportToWkt() time. It is recommended to use instead exportToWkt(char**,
11652
 * const char* const char*) const with options having FORMAT=WKT1_ESRI.
11653
 *
11654
 * This does the same as the C function OSRMorphToESRI().
11655
 *
11656
 * @return OGRERR_NONE unless something goes badly wrong.
11657
 * @deprecated
11658
 */
11659
11660
OGRErr OGRSpatialReference::morphToESRI()
11661
11662
0
{
11663
0
    TAKE_OPTIONAL_LOCK();
11664
11665
0
    d->refreshProjObj();
11666
0
    d->setMorphToESRI(true);
11667
11668
0
    return OGRERR_NONE;
11669
0
}
11670
11671
/************************************************************************/
11672
/*                           OSRMorphToESRI()                           */
11673
/************************************************************************/
11674
11675
/**
11676
 * \brief Convert in place to ESRI WKT format.
11677
 *
11678
 * This function is the same as the C++ method
11679
 * OGRSpatialReference::morphToESRI().
11680
 */
11681
OGRErr OSRMorphToESRI(OGRSpatialReferenceH hSRS)
11682
11683
0
{
11684
0
    VALIDATE_POINTER1(hSRS, "OSRMorphToESRI", OGRERR_FAILURE);
11685
11686
0
    return OGRSpatialReference::FromHandle(hSRS)->morphToESRI();
11687
0
}
11688
11689
/************************************************************************/
11690
/*                           morphFromESRI()                            */
11691
/************************************************************************/
11692
11693
/**
11694
 * \brief Convert in place from ESRI WKT format.
11695
 *
11696
 * The value notes of this coordinate system are modified in various manners
11697
 * to adhere more closely to the WKT standard.  This mostly involves
11698
 * translating a variety of ESRI names for projections, arguments and
11699
 * datums to "standard" names, as defined by Adam Gawne-Cain's reference
11700
 * translation of EPSG to WKT for the CT specification.
11701
 *
11702
 * \note Since GDAL 3.0, this function is essentially a no-operation, since
11703
 * morphing from ESRI is automatically done by importFromWkt(). Its only
11704
 * effect is to undo the effect of a potential prior call to morphToESRI().
11705
 *
11706
 * This does the same as the C function OSRMorphFromESRI().
11707
 *
11708
 * @return OGRERR_NONE unless something goes badly wrong.
11709
 * @deprecated
11710
 */
11711
11712
OGRErr OGRSpatialReference::morphFromESRI()
11713
11714
0
{
11715
0
    TAKE_OPTIONAL_LOCK();
11716
11717
0
    d->refreshProjObj();
11718
0
    d->setMorphToESRI(false);
11719
11720
0
    return OGRERR_NONE;
11721
0
}
11722
11723
/************************************************************************/
11724
/*                          OSRMorphFromESRI()                          */
11725
/************************************************************************/
11726
11727
/**
11728
 * \brief Convert in place from ESRI WKT format.
11729
 *
11730
 * This function is the same as the C++ method
11731
 * OGRSpatialReference::morphFromESRI().
11732
 */
11733
OGRErr OSRMorphFromESRI(OGRSpatialReferenceH hSRS)
11734
11735
0
{
11736
0
    VALIDATE_POINTER1(hSRS, "OSRMorphFromESRI", OGRERR_FAILURE);
11737
11738
0
    return OGRSpatialReference::FromHandle(hSRS)->morphFromESRI();
11739
0
}
11740
11741
/************************************************************************/
11742
/*                            FindMatches()                             */
11743
/************************************************************************/
11744
11745
/**
11746
 * \brief Try to identify a match between the passed SRS and a related SRS
11747
 * in a catalog.
11748
 *
11749
 * Matching may be partial, or may fail.
11750
 * Returned entries will be sorted by decreasing match confidence (first
11751
 * entry has the highest match confidence).
11752
 *
11753
 * The exact way matching is done may change in future versions. Starting with
11754
 * GDAL 3.0, it relies on PROJ' proj_identify() function.
11755
 *
11756
 * This method is the same as OSRFindMatches().
11757
 *
11758
 * @param papszOptions NULL terminated list of options or NULL
11759
 * @param pnEntries Output parameter. Number of values in the returned array.
11760
 * @param ppanMatchConfidence Output parameter (or NULL). *ppanMatchConfidence
11761
 * will be allocated to an array of *pnEntries whose values between 0 and 100
11762
 * indicate the confidence in the match. 100 is the highest confidence level.
11763
 * The array must be freed with CPLFree().
11764
 *
11765
 * @return an array of SRS that match the passed SRS, or NULL. Must be freed
11766
 * with OSRFreeSRSArray()
11767
 *
11768
 * @since GDAL 2.3
11769
 *
11770
 * @see OGRSpatialReference::FindBestMatch()
11771
 */
11772
OGRSpatialReferenceH *
11773
OGRSpatialReference::FindMatches(char **papszOptions, int *pnEntries,
11774
                                 int **ppanMatchConfidence) const
11775
0
{
11776
0
    TAKE_OPTIONAL_LOCK();
11777
11778
0
    CPL_IGNORE_RET_VAL(papszOptions);
11779
11780
0
    if (pnEntries)
11781
0
        *pnEntries = 0;
11782
0
    if (ppanMatchConfidence)
11783
0
        *ppanMatchConfidence = nullptr;
11784
11785
0
    d->refreshProjObj();
11786
0
    if (!d->m_pj_crs)
11787
0
        return nullptr;
11788
11789
0
    int *panConfidence = nullptr;
11790
0
    auto ctxt = d->getPROJContext();
11791
0
    auto list =
11792
0
        proj_identify(ctxt, d->m_pj_crs, nullptr, nullptr, &panConfidence);
11793
0
    if (!list)
11794
0
        return nullptr;
11795
11796
0
    const int nMatches = proj_list_get_count(list);
11797
11798
0
    if (pnEntries)
11799
0
        *pnEntries = static_cast<int>(nMatches);
11800
0
    OGRSpatialReferenceH *pahRet = static_cast<OGRSpatialReferenceH *>(
11801
0
        CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11802
0
    if (ppanMatchConfidence)
11803
0
    {
11804
0
        *ppanMatchConfidence =
11805
0
            static_cast<int *>(CPLMalloc(sizeof(int) * (nMatches + 1)));
11806
0
    }
11807
11808
0
    bool bSortAgain = false;
11809
11810
0
    for (int i = 0; i < nMatches; i++)
11811
0
    {
11812
0
        PJ *obj = proj_list_get(ctxt, list, i);
11813
0
        CPLAssert(obj);
11814
0
        OGRSpatialReference *poSRS = new OGRSpatialReference();
11815
0
        poSRS->d->setPjCRS(obj);
11816
0
        pahRet[i] = ToHandle(poSRS);
11817
11818
        // Identify matches that only differ by axis order
11819
0
        if (panConfidence[i] == 50 && GetAxesCount() == 2 &&
11820
0
            poSRS->GetAxesCount() == 2 &&
11821
0
            GetDataAxisToSRSAxisMapping() == std::vector<int>{1, 2})
11822
0
        {
11823
0
            OGRAxisOrientation eThisAxis0 = OAO_Other;
11824
0
            OGRAxisOrientation eThisAxis1 = OAO_Other;
11825
0
            OGRAxisOrientation eSRSAxis0 = OAO_Other;
11826
0
            OGRAxisOrientation eSRSAxis1 = OAO_Other;
11827
0
            GetAxis(nullptr, 0, &eThisAxis0);
11828
0
            GetAxis(nullptr, 1, &eThisAxis1);
11829
0
            poSRS->GetAxis(nullptr, 0, &eSRSAxis0);
11830
0
            poSRS->GetAxis(nullptr, 1, &eSRSAxis1);
11831
0
            if (eThisAxis0 == OAO_East && eThisAxis1 == OAO_North &&
11832
0
                eSRSAxis0 == OAO_North && eSRSAxis1 == OAO_East)
11833
0
            {
11834
0
                auto pj_crs_normalized =
11835
0
                    proj_normalize_for_visualization(ctxt, poSRS->d->m_pj_crs);
11836
0
                if (pj_crs_normalized)
11837
0
                {
11838
0
                    if (proj_is_equivalent_to(d->m_pj_crs, pj_crs_normalized,
11839
0
                                              PJ_COMP_EQUIVALENT))
11840
0
                    {
11841
0
                        bSortAgain = true;
11842
0
                        panConfidence[i] = 90;
11843
0
                        poSRS->SetDataAxisToSRSAxisMapping({2, 1});
11844
0
                    }
11845
0
                    proj_destroy(pj_crs_normalized);
11846
0
                }
11847
0
            }
11848
0
        }
11849
11850
0
        if (ppanMatchConfidence)
11851
0
            (*ppanMatchConfidence)[i] = panConfidence[i];
11852
0
    }
11853
11854
0
    if (bSortAgain)
11855
0
    {
11856
0
        std::vector<int> anIndices;
11857
0
        for (int i = 0; i < nMatches; ++i)
11858
0
            anIndices.push_back(i);
11859
11860
0
        std::stable_sort(anIndices.begin(), anIndices.end(),
11861
0
                         [&panConfidence](int i, int j)
11862
0
                         { return panConfidence[i] > panConfidence[j]; });
11863
11864
0
        OGRSpatialReferenceH *pahRetSorted =
11865
0
            static_cast<OGRSpatialReferenceH *>(
11866
0
                CPLCalloc(sizeof(OGRSpatialReferenceH), nMatches + 1));
11867
0
        for (int i = 0; i < nMatches; ++i)
11868
0
        {
11869
0
            pahRetSorted[i] = pahRet[anIndices[i]];
11870
0
            if (ppanMatchConfidence)
11871
0
                (*ppanMatchConfidence)[i] = panConfidence[anIndices[i]];
11872
0
        }
11873
0
        CPLFree(pahRet);
11874
0
        pahRet = pahRetSorted;
11875
0
    }
11876
11877
0
    pahRet[nMatches] = nullptr;
11878
0
    proj_list_destroy(list);
11879
0
    proj_int_list_destroy(panConfidence);
11880
11881
0
    return pahRet;
11882
0
}
11883
11884
/************************************************************************/
11885
/*                          importFromEPSGA()                           */
11886
/************************************************************************/
11887
11888
/**
11889
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
11890
 * code.
11891
 *
11892
 * This method will initialize the spatial reference based on the
11893
 * passed in EPSG CRS code found in the PROJ database.
11894
 *
11895
 * Since GDAL 3.0, this method is identical to importFromEPSG().
11896
 *
11897
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
11898
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
11899
 * such method available for the CRS. This behavior might not always be
11900
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
11901
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
11902
 * The AddGuessedTOWGS84() method can also be used for that purpose.
11903
 *
11904
 * The method will also by default substitute a deprecated EPSG code by its
11905
 * non-deprecated replacement. If this behavior is not desired, the
11906
 * OSR_USE_NON_DEPRECATED configuration option can be set to NO.
11907
 *
11908
 * This method is the same as the C function OSRImportFromEPSGA().
11909
 *
11910
 * @param nCode a CRS code.
11911
 *
11912
 * @return OGRERR_NONE on success, or an error code on failure.
11913
 */
11914
11915
OGRErr OGRSpatialReference::importFromEPSGA(int nCode)
11916
11917
0
{
11918
0
    TAKE_OPTIONAL_LOCK();
11919
11920
0
    Clear();
11921
11922
0
    const char *pszUseNonDeprecated =
11923
0
        CPLGetConfigOption("OSR_USE_NON_DEPRECATED", nullptr);
11924
0
    const bool bUseNonDeprecated =
11925
0
        CPLTestBool(pszUseNonDeprecated ? pszUseNonDeprecated : "YES");
11926
0
    const bool bAddTOWGS84 = CPLTestBool(
11927
0
        CPLGetConfigOption("OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG", "NO"));
11928
0
    auto tlsCache = OSRGetProjTLSCache();
11929
0
    if (tlsCache)
11930
0
    {
11931
0
        auto cachedObj =
11932
0
            tlsCache->GetPJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84);
11933
0
        if (cachedObj)
11934
0
        {
11935
0
            d->setPjCRS(cachedObj);
11936
0
            return OGRERR_NONE;
11937
0
        }
11938
0
    }
11939
11940
0
    CPLString osCode;
11941
0
    osCode.Printf("%d", nCode);
11942
0
    PJ *obj;
11943
0
    constexpr int FIRST_NON_DEPRECATED_ESRI_CODE = 53001;
11944
0
    if (nCode < FIRST_NON_DEPRECATED_ESRI_CODE)
11945
0
    {
11946
0
        obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11947
0
                                        osCode.c_str(), PJ_CATEGORY_CRS, true,
11948
0
                                        nullptr);
11949
0
        if (!obj)
11950
0
        {
11951
0
            return OGRERR_FAILURE;
11952
0
        }
11953
0
    }
11954
0
    else
11955
0
    {
11956
        // Likely to be an ESRI CRS...
11957
0
        CPLErr eLastErrorType = CE_None;
11958
0
        CPLErrorNum eLastErrorNum = CPLE_None;
11959
0
        std::string osLastErrorMsg;
11960
0
        bool bIsESRI = false;
11961
0
        {
11962
0
            CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
11963
0
            CPLErrorReset();
11964
0
            obj = proj_create_from_database(d->getPROJContext(), "EPSG",
11965
0
                                            osCode.c_str(), PJ_CATEGORY_CRS,
11966
0
                                            true, nullptr);
11967
0
            if (!obj)
11968
0
            {
11969
0
                eLastErrorType = CPLGetLastErrorType();
11970
0
                eLastErrorNum = CPLGetLastErrorNo();
11971
0
                osLastErrorMsg = CPLGetLastErrorMsg();
11972
0
                obj = proj_create_from_database(d->getPROJContext(), "ESRI",
11973
0
                                                osCode.c_str(), PJ_CATEGORY_CRS,
11974
0
                                                true, nullptr);
11975
0
                if (obj)
11976
0
                    bIsESRI = true;
11977
0
            }
11978
0
        }
11979
0
        if (!obj)
11980
0
        {
11981
0
            if (eLastErrorType != CE_None)
11982
0
                CPLError(eLastErrorType, eLastErrorNum, "%s",
11983
0
                         osLastErrorMsg.c_str());
11984
0
            return OGRERR_FAILURE;
11985
0
        }
11986
0
        if (bIsESRI)
11987
0
        {
11988
0
            CPLError(CE_Warning, CPLE_AppDefined,
11989
0
                     "EPSG:%d is not a valid CRS code, but ESRI:%d is. "
11990
0
                     "Assuming ESRI:%d was meant",
11991
0
                     nCode, nCode, nCode);
11992
0
        }
11993
0
    }
11994
11995
0
    if (bUseNonDeprecated && proj_is_deprecated(obj))
11996
0
    {
11997
0
        auto list = proj_get_non_deprecated(d->getPROJContext(), obj);
11998
0
        if (list)
11999
0
        {
12000
0
            const auto count = proj_list_get_count(list);
12001
0
            if (count == 1)
12002
0
            {
12003
0
                auto nonDeprecated =
12004
0
                    proj_list_get(d->getPROJContext(), list, 0);
12005
0
                if (nonDeprecated)
12006
0
                {
12007
0
                    if (pszUseNonDeprecated == nullptr)
12008
0
                    {
12009
0
                        const char *pszNewAuth =
12010
0
                            proj_get_id_auth_name(nonDeprecated, 0);
12011
0
                        const char *pszNewCode =
12012
0
                            proj_get_id_code(nonDeprecated, 0);
12013
0
                        CPLError(CE_Warning, CPLE_AppDefined,
12014
0
                                 "CRS EPSG:%d is deprecated. "
12015
0
                                 "Its non-deprecated replacement %s:%s "
12016
0
                                 "will be used instead. "
12017
0
                                 "To use the original CRS, set the "
12018
0
                                 "OSR_USE_NON_DEPRECATED "
12019
0
                                 "configuration option to NO.",
12020
0
                                 nCode, pszNewAuth ? pszNewAuth : "(null)",
12021
0
                                 pszNewCode ? pszNewCode : "(null)");
12022
0
                    }
12023
0
                    proj_destroy(obj);
12024
0
                    obj = nonDeprecated;
12025
0
                }
12026
0
            }
12027
0
        }
12028
0
        proj_list_destroy(list);
12029
0
    }
12030
12031
0
    if (bAddTOWGS84)
12032
0
    {
12033
0
        auto boundCRS = proj_crs_create_bound_crs_to_WGS84(d->getPROJContext(),
12034
0
                                                           obj, nullptr);
12035
0
        if (boundCRS)
12036
0
        {
12037
0
            proj_destroy(obj);
12038
0
            obj = boundCRS;
12039
0
        }
12040
0
    }
12041
12042
0
    d->setPjCRS(obj);
12043
12044
0
    if (tlsCache)
12045
0
    {
12046
0
        tlsCache->CachePJForEPSGCode(nCode, bUseNonDeprecated, bAddTOWGS84,
12047
0
                                     obj);
12048
0
    }
12049
12050
0
    return OGRERR_NONE;
12051
0
}
12052
12053
/************************************************************************/
12054
/*                          AddGuessedTOWGS84()                         */
12055
/************************************************************************/
12056
12057
/**
12058
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12059
 * to WGS84.
12060
 *
12061
 * This method try to attach a 3-parameter or 7-parameter Helmert transformation
12062
 * to WGS84 when there is one and only one such method available for the CRS.
12063
 * Note: this is more restrictive to how GDAL < 3 worked.
12064
 *
12065
 * This method is the same as the C function OSRAddGuessedTOWGS84().
12066
 *
12067
 * @return OGRERR_NONE on success, or an error code on failure (the CRS has
12068
 * already a transformation to WGS84 or none matching could be found).
12069
 *
12070
 * @since GDAL 3.0.3
12071
 */
12072
OGRErr OGRSpatialReference::AddGuessedTOWGS84()
12073
0
{
12074
0
    TAKE_OPTIONAL_LOCK();
12075
12076
0
    d->refreshProjObj();
12077
0
    if (!d->m_pj_crs)
12078
0
        return OGRERR_FAILURE;
12079
0
    auto boundCRS = GDAL_proj_crs_create_bound_crs_to_WGS84(
12080
0
        d->getPROJContext(), d->m_pj_crs, false, true);
12081
0
    if (!boundCRS)
12082
0
    {
12083
0
        return OGRERR_FAILURE;
12084
0
    }
12085
0
    d->setPjCRS(boundCRS);
12086
0
    return OGRERR_NONE;
12087
0
}
12088
12089
/************************************************************************/
12090
/*                         OSRImportFromEPSGA()                         */
12091
/************************************************************************/
12092
12093
/**
12094
 * \brief  Try to add a a 3-parameter or 7-parameter Helmert transformation
12095
 * to WGS84.
12096
 *
12097
 * This function is the same as OGRSpatialReference::AddGuessedTOWGS84().
12098
 *
12099
 * @since GDAL 3.0.3
12100
 */
12101
12102
OGRErr OSRAddGuessedTOWGS84(OGRSpatialReferenceH hSRS)
12103
12104
0
{
12105
0
    VALIDATE_POINTER1(hSRS, "OSRAddGuessedTOWGS84", OGRERR_FAILURE);
12106
12107
0
    return OGRSpatialReference::FromHandle(hSRS)->AddGuessedTOWGS84();
12108
0
}
12109
12110
/************************************************************************/
12111
/*                         OSRImportFromEPSGA()                         */
12112
/************************************************************************/
12113
12114
/**
12115
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12116
 * code.
12117
 *
12118
 * This function is the same as OGRSpatialReference::importFromEPSGA().
12119
 */
12120
12121
OGRErr CPL_STDCALL OSRImportFromEPSGA(OGRSpatialReferenceH hSRS, int nCode)
12122
12123
0
{
12124
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSGA", OGRERR_FAILURE);
12125
12126
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSGA(nCode);
12127
0
}
12128
12129
/************************************************************************/
12130
/*                           importFromEPSG()                           */
12131
/************************************************************************/
12132
12133
/**
12134
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12135
 * code.
12136
 *
12137
 * This method will initialize the spatial reference based on the
12138
 * passed in EPSG CRS code found in the PROJ database.
12139
 *
12140
 * This method is the same as the C function OSRImportFromEPSG().
12141
 *
12142
 * Before GDAL 3.0.3, this method would try to attach a 3-parameter or
12143
 * 7-parameter Helmert transformation to WGS84 when there is one and only one
12144
 * such method available for the CRS. This behavior might not always be
12145
 * desirable, so starting with GDAL 3.0.3, this is no longer done unless
12146
 * the OSR_ADD_TOWGS84_ON_IMPORT_FROM_EPSG configuration option is set to YES.
12147
 *
12148
 * @param nCode a GCS or PCS code from the horizontal coordinate system table.
12149
 *
12150
 * @return OGRERR_NONE on success, or an error code on failure.
12151
 */
12152
12153
OGRErr OGRSpatialReference::importFromEPSG(int nCode)
12154
12155
0
{
12156
0
    return importFromEPSGA(nCode);
12157
0
}
12158
12159
/************************************************************************/
12160
/*                         OSRImportFromEPSG()                          */
12161
/************************************************************************/
12162
12163
/**
12164
 * \brief  Initialize SRS based on EPSG geographic, projected or vertical CRS
12165
 * code.
12166
 *
12167
 * This function is the same as OGRSpatialReference::importFromEPSG().
12168
 */
12169
12170
OGRErr CPL_STDCALL OSRImportFromEPSG(OGRSpatialReferenceH hSRS, int nCode)
12171
12172
0
{
12173
0
    VALIDATE_POINTER1(hSRS, "OSRImportFromEPSG", OGRERR_FAILURE);
12174
12175
0
    return OGRSpatialReference::FromHandle(hSRS)->importFromEPSG(nCode);
12176
0
}
12177
12178
/************************************************************************/
12179
/*                        EPSGTreatsAsLatLong()                         */
12180
/************************************************************************/
12181
12182
/**
12183
 * \brief This method returns TRUE if this geographic coordinate
12184
 * system should be treated as having lat/long coordinate ordering.
12185
 *
12186
 * Currently this returns TRUE for all geographic coordinate systems
12187
 * with axes set defining it as lat, long (prior to GDAL 3.10, it
12188
 * also checked that the CRS had belonged to EPSG authority, but this check
12189
 * has now been removed).
12190
 *
12191
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12192
 * geographic CRS imported with importFromEPSG() would cause this method to
12193
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12194
 * is now equivalent to importFromEPSGA().
12195
 *
12196
 * FALSE will be returned for all coordinate systems that are not geographic,
12197
 * or whose axes ordering is not latitude, longitude.
12198
 *
12199
 * This method is the same as the C function OSREPSGTreatsAsLatLong().
12200
 *
12201
 * @return TRUE or FALSE.
12202
 */
12203
12204
int OGRSpatialReference::EPSGTreatsAsLatLong() const
12205
12206
0
{
12207
0
    TAKE_OPTIONAL_LOCK();
12208
12209
0
    if (!IsGeographic())
12210
0
        return FALSE;
12211
12212
0
    d->demoteFromBoundCRS();
12213
12214
0
    bool ret = false;
12215
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12216
0
    {
12217
0
        auto horizCRS =
12218
0
            proj_crs_get_sub_crs(d->getPROJContext(), d->m_pj_crs, 0);
12219
0
        if (horizCRS)
12220
0
        {
12221
0
            auto cs =
12222
0
                proj_crs_get_coordinate_system(d->getPROJContext(), horizCRS);
12223
0
            if (cs)
12224
0
            {
12225
0
                const char *pszDirection = nullptr;
12226
0
                if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12227
0
                                          nullptr, &pszDirection, nullptr,
12228
0
                                          nullptr, nullptr, nullptr))
12229
0
                {
12230
0
                    if (EQUAL(pszDirection, "north"))
12231
0
                    {
12232
0
                        ret = true;
12233
0
                    }
12234
0
                }
12235
12236
0
                proj_destroy(cs);
12237
0
            }
12238
12239
0
            proj_destroy(horizCRS);
12240
0
        }
12241
0
    }
12242
0
    else
12243
0
    {
12244
0
        auto cs =
12245
0
            proj_crs_get_coordinate_system(d->getPROJContext(), d->m_pj_crs);
12246
0
        if (cs)
12247
0
        {
12248
0
            const char *pszDirection = nullptr;
12249
0
            if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr,
12250
0
                                      nullptr, &pszDirection, nullptr, nullptr,
12251
0
                                      nullptr, nullptr))
12252
0
            {
12253
0
                if (EQUAL(pszDirection, "north"))
12254
0
                {
12255
0
                    ret = true;
12256
0
                }
12257
0
            }
12258
12259
0
            proj_destroy(cs);
12260
0
        }
12261
0
    }
12262
0
    d->undoDemoteFromBoundCRS();
12263
12264
0
    return ret;
12265
0
}
12266
12267
/************************************************************************/
12268
/*                       OSREPSGTreatsAsLatLong()                       */
12269
/************************************************************************/
12270
12271
/**
12272
 * \brief This function returns TRUE if this geographic coordinate
12273
 * system should be treated as having lat/long coordinate ordering.
12274
 *
12275
 * This function is the same as OGRSpatialReference::OSREPSGTreatsAsLatLong().
12276
 */
12277
12278
int OSREPSGTreatsAsLatLong(OGRSpatialReferenceH hSRS)
12279
12280
0
{
12281
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsLatLong", OGRERR_FAILURE);
12282
12283
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsLatLong();
12284
0
}
12285
12286
/************************************************************************/
12287
/*                     EPSGTreatsAsNorthingEasting()                    */
12288
/************************************************************************/
12289
12290
/**
12291
 * \brief This method returns TRUE if this projected coordinate
12292
 * system should be treated as having northing/easting coordinate ordering.
12293
 *
12294
 * Currently this returns TRUE for all projected coordinate systems
12295
 * with axes set defining it as northing, easting (prior to GDAL 3.10, it
12296
 * also checked that the CRS had belonged to EPSG authority, but this check
12297
 * has now been removed).
12298
 *
12299
 * \note Important change of behavior since GDAL 3.0. In previous versions,
12300
 * projected CRS with northing, easting axis order imported with
12301
 * importFromEPSG() would cause this method to
12302
 * return FALSE on them, whereas now it returns TRUE, since importFromEPSG()
12303
 * is now equivalent to importFromEPSGA().
12304
 *
12305
 * FALSE will be returned for all coordinate systems that are not projected,
12306
 * or whose axes ordering is not northing, easting.
12307
 *
12308
 * This method is the same as the C function EPSGTreatsAsNorthingEasting().
12309
 *
12310
 * @return TRUE or FALSE.
12311
 *
12312
 * @since OGR 1.10.0
12313
 */
12314
12315
int OGRSpatialReference::EPSGTreatsAsNorthingEasting() const
12316
12317
0
{
12318
0
    TAKE_OPTIONAL_LOCK();
12319
12320
0
    if (!IsProjected())
12321
0
        return FALSE;
12322
12323
0
    d->demoteFromBoundCRS();
12324
0
    PJ *projCRS;
12325
0
    const auto ctxt = d->getPROJContext();
12326
0
    if (d->m_pjType == PJ_TYPE_COMPOUND_CRS)
12327
0
    {
12328
0
        projCRS = proj_crs_get_sub_crs(ctxt, d->m_pj_crs, 0);
12329
0
        if (!projCRS || proj_get_type(projCRS) != PJ_TYPE_PROJECTED_CRS)
12330
0
        {
12331
0
            d->undoDemoteFromBoundCRS();
12332
0
            proj_destroy(projCRS);
12333
0
            return FALSE;
12334
0
        }
12335
0
    }
12336
0
    else
12337
0
    {
12338
0
        projCRS = proj_clone(ctxt, d->m_pj_crs);
12339
0
    }
12340
12341
0
    bool ret = false;
12342
0
    auto cs = proj_crs_get_coordinate_system(ctxt, projCRS);
12343
0
    proj_destroy(projCRS);
12344
0
    d->undoDemoteFromBoundCRS();
12345
12346
0
    if (cs)
12347
0
    {
12348
0
        ret = isNorthEastAxisOrder(ctxt, cs);
12349
0
        proj_destroy(cs);
12350
0
    }
12351
12352
0
    return ret;
12353
0
}
12354
12355
/************************************************************************/
12356
/*                     OSREPSGTreatsAsNorthingEasting()                 */
12357
/************************************************************************/
12358
12359
/**
12360
 * \brief This function returns TRUE if this projected coordinate
12361
 * system should be treated as having northing/easting coordinate ordering.
12362
 *
12363
 * This function is the same as
12364
 * OGRSpatialReference::EPSGTreatsAsNorthingEasting().
12365
 *
12366
 * @since OGR 1.10.0
12367
 */
12368
12369
int OSREPSGTreatsAsNorthingEasting(OGRSpatialReferenceH hSRS)
12370
12371
0
{
12372
0
    VALIDATE_POINTER1(hSRS, "OSREPSGTreatsAsNorthingEasting", OGRERR_FAILURE);
12373
12374
0
    return OGRSpatialReference::FromHandle(hSRS)->EPSGTreatsAsNorthingEasting();
12375
0
}
12376
12377
/************************************************************************/
12378
/*                     ImportFromESRIWisconsinWKT()                     */
12379
/*                                                                      */
12380
/*      Search a ESRI State Plane WKT and import it.                    */
12381
/************************************************************************/
12382
12383
// This is only used by the HFA driver and somewhat dubious we really need that
12384
// Coming from an old ESRI merge
12385
12386
OGRErr OGRSpatialReference::ImportFromESRIWisconsinWKT(const char *prjName,
12387
                                                       double centralMeridian,
12388
                                                       double latOfOrigin,
12389
                                                       const char *unitsName,
12390
                                                       const char *crsName)
12391
0
{
12392
0
    TAKE_OPTIONAL_LOCK();
12393
12394
0
    if (centralMeridian < -93 || centralMeridian > -87)
12395
0
        return OGRERR_FAILURE;
12396
0
    if (latOfOrigin < 40 || latOfOrigin > 47)
12397
0
        return OGRERR_FAILURE;
12398
12399
    // If the CS name is known.
12400
0
    if (!prjName && !unitsName && crsName)
12401
0
    {
12402
0
        const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12403
0
        PJ_OBJ_LIST *list = proj_create_from_name(
12404
0
            d->getPROJContext(), "ESRI", crsName, &type, 1, false, 1, nullptr);
12405
0
        if (list)
12406
0
        {
12407
0
            if (proj_list_get_count(list) == 1)
12408
0
            {
12409
0
                auto crs = proj_list_get(d->getPROJContext(), list, 0);
12410
0
                if (crs)
12411
0
                {
12412
0
                    Clear();
12413
0
                    d->setPjCRS(crs);
12414
0
                    proj_list_destroy(list);
12415
0
                    return OGRERR_NONE;
12416
0
                }
12417
0
            }
12418
0
            proj_list_destroy(list);
12419
0
        }
12420
0
        return OGRERR_FAILURE;
12421
0
    }
12422
12423
0
    if (prjName == nullptr || unitsName == nullptr)
12424
0
    {
12425
0
        return OGRERR_FAILURE;
12426
0
    }
12427
12428
0
    const PJ_TYPE type = PJ_TYPE_PROJECTED_CRS;
12429
0
    PJ_OBJ_LIST *list = proj_create_from_name(d->getPROJContext(), "ESRI",
12430
0
                                              "NAD_1983_HARN_WISCRS_", &type, 1,
12431
0
                                              true, 0, nullptr);
12432
0
    if (list)
12433
0
    {
12434
0
        const auto listSize = proj_list_get_count(list);
12435
0
        for (int i = 0; i < listSize; i++)
12436
0
        {
12437
0
            auto crs = proj_list_get(d->getPROJContext(), list, i);
12438
0
            if (!crs)
12439
0
            {
12440
0
                continue;
12441
0
            }
12442
12443
0
            auto conv = proj_crs_get_coordoperation(d->getPROJContext(), crs);
12444
0
            if (!conv)
12445
0
            {
12446
0
                proj_destroy(crs);
12447
0
                continue;
12448
0
            }
12449
0
            const char *pszMethodCode = nullptr;
12450
0
            proj_coordoperation_get_method_info(
12451
0
                d->getPROJContext(), conv, nullptr, nullptr, &pszMethodCode);
12452
0
            const int nMethodCode = atoi(pszMethodCode ? pszMethodCode : "0");
12453
0
            if (!((EQUAL(prjName, SRS_PT_TRANSVERSE_MERCATOR) &&
12454
0
                   nMethodCode == EPSG_CODE_METHOD_TRANSVERSE_MERCATOR) ||
12455
0
                  (EQUAL(prjName, "Lambert_Conformal_Conic") &&
12456
0
                   nMethodCode ==
12457
0
                       EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)))
12458
0
            {
12459
0
                proj_destroy(crs);
12460
0
                proj_destroy(conv);
12461
0
                continue;
12462
0
            }
12463
12464
0
            auto coordSys =
12465
0
                proj_crs_get_coordinate_system(d->getPROJContext(), crs);
12466
0
            if (!coordSys)
12467
0
            {
12468
0
                proj_destroy(crs);
12469
0
                proj_destroy(conv);
12470
0
                continue;
12471
0
            }
12472
12473
0
            double dfConvFactor = 0.0;
12474
0
            proj_cs_get_axis_info(d->getPROJContext(), coordSys, 0, nullptr,
12475
0
                                  nullptr, nullptr, &dfConvFactor, nullptr,
12476
0
                                  nullptr, nullptr);
12477
0
            proj_destroy(coordSys);
12478
12479
0
            if ((EQUAL(unitsName, "meters") && dfConvFactor != 1.0) ||
12480
0
                (!EQUAL(unitsName, "meters") &&
12481
0
                 std::fabs(dfConvFactor - CPLAtof(SRS_UL_US_FOOT_CONV)) >
12482
0
                     1e-10))
12483
0
            {
12484
0
                proj_destroy(crs);
12485
0
                proj_destroy(conv);
12486
0
                continue;
12487
0
            }
12488
12489
0
            int idx_lat = proj_coordoperation_get_param_index(
12490
0
                d->getPROJContext(), conv,
12491
0
                EPSG_NAME_PARAMETER_LATITUDE_OF_NATURAL_ORIGIN);
12492
0
            double valueLat = -1000;
12493
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lat,
12494
0
                                          nullptr, nullptr, nullptr, &valueLat,
12495
0
                                          nullptr, nullptr, nullptr, nullptr,
12496
0
                                          nullptr, nullptr);
12497
0
            int idx_lon = proj_coordoperation_get_param_index(
12498
0
                d->getPROJContext(), conv,
12499
0
                EPSG_NAME_PARAMETER_LONGITUDE_OF_NATURAL_ORIGIN);
12500
0
            double valueLong = -1000;
12501
0
            proj_coordoperation_get_param(d->getPROJContext(), conv, idx_lon,
12502
0
                                          nullptr, nullptr, nullptr, &valueLong,
12503
0
                                          nullptr, nullptr, nullptr, nullptr,
12504
0
                                          nullptr, nullptr);
12505
0
            if (std::fabs(centralMeridian - valueLong) <= 1e-10 &&
12506
0
                std::fabs(latOfOrigin - valueLat) <= 1e-10)
12507
0
            {
12508
0
                Clear();
12509
0
                d->setPjCRS(crs);
12510
0
                proj_list_destroy(list);
12511
0
                proj_destroy(conv);
12512
0
                return OGRERR_NONE;
12513
0
            }
12514
12515
0
            proj_destroy(crs);
12516
0
            proj_destroy(conv);
12517
0
        }
12518
0
        proj_list_destroy(list);
12519
0
    }
12520
12521
0
    return OGRERR_FAILURE;
12522
0
}
12523
12524
/************************************************************************/
12525
/*                      GetAxisMappingStrategy()                        */
12526
/************************************************************************/
12527
12528
/** \brief Return the data axis to CRS axis mapping strategy.
12529
 *
12530
 * <ul>
12531
 * <li>OAMS_TRADITIONAL_GIS_ORDER means that for geographic CRS with
12532
 *     lat/long order, the data will still be long/lat ordered. Similarly for
12533
 *     a projected CRS with northing/easting order, the data will still be
12534
 *     easting/northing ordered.
12535
 * <li>OAMS_AUTHORITY_COMPLIANT means that the data axis will be identical to
12536
 *     the CRS axis.
12537
 * <li>OAMS_CUSTOM means that the data axis are customly defined with
12538
 *     SetDataAxisToSRSAxisMapping()
12539
 * </ul>
12540
 * @return the data axis to CRS axis mapping strategy.
12541
 * @since GDAL 3.0
12542
 */
12543
OSRAxisMappingStrategy OGRSpatialReference::GetAxisMappingStrategy() const
12544
0
{
12545
0
    TAKE_OPTIONAL_LOCK();
12546
12547
0
    return d->m_axisMappingStrategy;
12548
0
}
12549
12550
/************************************************************************/
12551
/*                      OSRGetAxisMappingStrategy()                     */
12552
/************************************************************************/
12553
12554
/** \brief Return the data axis to CRS axis mapping strategy.
12555
 *
12556
 * See OGRSpatialReference::GetAxisMappingStrategy()
12557
 * @since GDAL 3.0
12558
 */
12559
OSRAxisMappingStrategy OSRGetAxisMappingStrategy(OGRSpatialReferenceH hSRS)
12560
0
{
12561
0
    VALIDATE_POINTER1(hSRS, "OSRGetAxisMappingStrategy", OAMS_CUSTOM);
12562
12563
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAxisMappingStrategy();
12564
0
}
12565
12566
/************************************************************************/
12567
/*                      SetAxisMappingStrategy()                        */
12568
/************************************************************************/
12569
12570
/** \brief Set the data axis to CRS axis mapping strategy.
12571
 *
12572
 * Starting with GDAL 3.5, the OSR_DEFAULT_AXIS_MAPPING_STRATEGY configuration
12573
 * option can be set to "TRADITIONAL_GIS_ORDER" / "AUTHORITY_COMPLIANT" (the
12574
 * later being the default value when the option is not set) to control the
12575
 * value of the data axis to CRS axis mapping strategy when a
12576
 * OSRSpatialReference object is created. Calling SetAxisMappingStrategy() will
12577
 * override this default value.
12578
 *
12579
 * See OGRSpatialReference::GetAxisMappingStrategy()
12580
 * @since GDAL 3.0
12581
 */
12582
void OGRSpatialReference::SetAxisMappingStrategy(
12583
    OSRAxisMappingStrategy strategy)
12584
0
{
12585
0
    TAKE_OPTIONAL_LOCK();
12586
12587
0
    d->m_axisMappingStrategy = strategy;
12588
0
    d->refreshAxisMapping();
12589
0
}
12590
12591
/************************************************************************/
12592
/*                      OSRSetAxisMappingStrategy()                     */
12593
/************************************************************************/
12594
12595
/** \brief Set the data axis to CRS axis mapping strategy.
12596
 *
12597
 * See OGRSpatialReference::SetAxisMappingStrategy()
12598
 * @since GDAL 3.0
12599
 */
12600
void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS,
12601
                               OSRAxisMappingStrategy strategy)
12602
0
{
12603
0
    VALIDATE_POINTER0(hSRS, "OSRSetAxisMappingStrategy");
12604
12605
0
    OGRSpatialReference::FromHandle(hSRS)->SetAxisMappingStrategy(strategy);
12606
0
}
12607
12608
/************************************************************************/
12609
/*                      GetDataAxisToSRSAxisMapping()                   */
12610
/************************************************************************/
12611
12612
/** \brief Return the data axis to SRS axis mapping.
12613
 *
12614
 * The number of elements of the vector will be the number of axis of the CRS.
12615
 * Values start at 1.
12616
 *
12617
 * If m = GetDataAxisToSRSAxisMapping(), then m[0] is the data axis number
12618
 * for the first axis of the CRS.
12619
 *
12620
 * @since GDAL 3.0
12621
 */
12622
const std::vector<int> &OGRSpatialReference::GetDataAxisToSRSAxisMapping() const
12623
0
{
12624
0
    TAKE_OPTIONAL_LOCK();
12625
12626
0
    return d->m_axisMapping;
12627
0
}
12628
12629
/************************************************************************/
12630
/*                     OSRGetDataAxisToSRSAxisMapping()                 */
12631
/************************************************************************/
12632
12633
/** \brief Return the data axis to SRS axis mapping.
12634
 *
12635
 * See OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12636
 *
12637
 * @since GDAL 3.0
12638
 */
12639
const int *OSRGetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12640
                                          int *pnCount)
12641
0
{
12642
0
    VALIDATE_POINTER1(hSRS, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12643
0
    VALIDATE_POINTER1(pnCount, "OSRGetDataAxisToSRSAxisMapping", nullptr);
12644
12645
0
    const auto &v =
12646
0
        OGRSpatialReference::FromHandle(hSRS)->GetDataAxisToSRSAxisMapping();
12647
0
    *pnCount = static_cast<int>(v.size());
12648
0
    return v.data();
12649
0
}
12650
12651
/************************************************************************/
12652
/*                      SetDataAxisToSRSAxisMapping()                   */
12653
/************************************************************************/
12654
12655
/** \brief Set a custom data axis to CRS axis mapping.
12656
 *
12657
 * The number of elements of the mapping vector should be the number of axis
12658
 * of the CRS (as returned by GetAxesCount()) (although this method does not
12659
 * check that, beyond checking there are at least 2 elements, so that this
12660
 * method and setting the CRS can be done in any order).
12661
 * This is taken into account by OGRCoordinateTransformation to transform the
12662
 * order of coordinates to the order expected by the CRS before
12663
 * transformation, and back to the data order after transformation.
12664
 *
12665
 * The mapping[i] value (one based) represents the data axis number for the i(th)
12666
 * axis of the CRS. A negative value can also be used to ask for a sign
12667
 * reversal during coordinate transformation (to deal with northing vs southing,
12668
 * easting vs westing, heights vs depths).
12669
 *
12670
 * When used with OGRCoordinateTransformation,
12671
 * - the only valid values for mapping[0] (data axis number for the first axis
12672
 *   of the CRS) are 1, 2, -1, -2.
12673
 * - the only valid values for mapping[1] (data axis number for the second axis
12674
 *   of the CRS) are 1, 2, -1, -2.
12675
 *  - the only valid values mapping[2] are 3 or -3.
12676
 * Note: this method does not validate the values of mapping[].
12677
 *
12678
 * mapping=[2,1] typically expresses the inversion of axis between the data
12679
 * axis and the CRS axis for a 2D CRS.
12680
 *
12681
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12682
 *
12683
 * This is the same as the C function OSRSetDataAxisToSRSAxisMapping().
12684
 *
12685
 * @param mapping The new data axis to CRS axis mapping.
12686
 *
12687
 * @since GDAL 3.0
12688
 * @see OGRSpatialReference::GetDataAxisToSRSAxisMapping()
12689
 */
12690
OGRErr OGRSpatialReference::SetDataAxisToSRSAxisMapping(
12691
    const std::vector<int> &mapping)
12692
0
{
12693
0
    TAKE_OPTIONAL_LOCK();
12694
12695
0
    if (mapping.size() < 2)
12696
0
        return OGRERR_FAILURE;
12697
0
    d->m_axisMappingStrategy = OAMS_CUSTOM;
12698
0
    d->m_axisMapping = mapping;
12699
0
    return OGRERR_NONE;
12700
0
}
12701
12702
/************************************************************************/
12703
/*                     OSRSetDataAxisToSRSAxisMapping()                 */
12704
/************************************************************************/
12705
12706
/** \brief Set a custom data axis to CRS axis mapping.
12707
 *
12708
 * Automatically implies SetAxisMappingStrategy(OAMS_CUSTOM)
12709
 *
12710
 * This is the same as the C++ method
12711
 * OGRSpatialReference::SetDataAxisToSRSAxisMapping()
12712
 *
12713
 * @since GDAL 3.1
12714
 */
12715
OGRErr OSRSetDataAxisToSRSAxisMapping(OGRSpatialReferenceH hSRS,
12716
                                      int nMappingSize, const int *panMapping)
12717
0
{
12718
0
    VALIDATE_POINTER1(hSRS, "OSRSetDataAxisToSRSAxisMapping", OGRERR_FAILURE);
12719
0
    VALIDATE_POINTER1(panMapping, "OSRSetDataAxisToSRSAxisMapping",
12720
0
                      OGRERR_FAILURE);
12721
12722
0
    if (nMappingSize < 0)
12723
0
        return OGRERR_FAILURE;
12724
12725
0
    std::vector<int> mapping(nMappingSize);
12726
0
    if (nMappingSize)
12727
0
        memcpy(&mapping[0], panMapping, nMappingSize * sizeof(int));
12728
0
    return OGRSpatialReference::FromHandle(hSRS)->SetDataAxisToSRSAxisMapping(
12729
0
        mapping);
12730
0
}
12731
12732
/************************************************************************/
12733
/*                               GetAreaOfUse()                         */
12734
/************************************************************************/
12735
12736
/** \brief Return the area of use of the CRS.
12737
 *
12738
 * This method is the same as the OSRGetAreaOfUse() function.
12739
 *
12740
 * @param pdfWestLongitudeDeg Pointer to a double to receive the western-most
12741
 * longitude, expressed in degree. Might be NULL. If the returned value is
12742
 * -1000, the bounding box is unknown.
12743
 * @param pdfSouthLatitudeDeg Pointer to a double to receive the southern-most
12744
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12745
 * the bounding box is unknown.
12746
 * @param pdfEastLongitudeDeg Pointer to a double to receive the eastern-most
12747
 * longitude, expressed in degree. Might be NULL. If the returned value is
12748
 * -1000, the bounding box is unknown.
12749
 * @param pdfNorthLatitudeDeg Pointer to a double to receive the northern-most
12750
 * latitude, expressed in degree. Might be NULL. If the returned value is -1000,
12751
 * the bounding box is unknown.
12752
 * @param ppszAreaName Pointer to a string to receive the name of the area of
12753
 * use. Might be NULL. Note that *ppszAreaName is short-lived and might be
12754
 * invalidated by further calls.
12755
 * @return true in case of success
12756
 * @since GDAL 3.0
12757
 */
12758
bool OGRSpatialReference::GetAreaOfUse(double *pdfWestLongitudeDeg,
12759
                                       double *pdfSouthLatitudeDeg,
12760
                                       double *pdfEastLongitudeDeg,
12761
                                       double *pdfNorthLatitudeDeg,
12762
                                       const char **ppszAreaName) const
12763
0
{
12764
0
    TAKE_OPTIONAL_LOCK();
12765
12766
0
    d->refreshProjObj();
12767
0
    if (!d->m_pj_crs)
12768
0
    {
12769
0
        return false;
12770
0
    }
12771
0
    d->demoteFromBoundCRS();
12772
0
    const char *pszAreaName = nullptr;
12773
0
    int bSuccess = proj_get_area_of_use(
12774
0
        d->getPROJContext(), d->m_pj_crs, pdfWestLongitudeDeg,
12775
0
        pdfSouthLatitudeDeg, pdfEastLongitudeDeg, pdfNorthLatitudeDeg,
12776
0
        &pszAreaName);
12777
0
    d->undoDemoteFromBoundCRS();
12778
0
    d->m_osAreaName = pszAreaName ? pszAreaName : "";
12779
0
    if (ppszAreaName)
12780
0
        *ppszAreaName = d->m_osAreaName.c_str();
12781
0
    return CPL_TO_BOOL(bSuccess);
12782
0
}
12783
12784
/************************************************************************/
12785
/*                               GetAreaOfUse()                         */
12786
/************************************************************************/
12787
12788
/** \brief Return the area of use of the CRS.
12789
 *
12790
 * This function is the same as the OGRSpatialReference::GetAreaOfUse() method.
12791
 *
12792
 * @since GDAL 3.0
12793
 */
12794
int OSRGetAreaOfUse(OGRSpatialReferenceH hSRS, double *pdfWestLongitudeDeg,
12795
                    double *pdfSouthLatitudeDeg, double *pdfEastLongitudeDeg,
12796
                    double *pdfNorthLatitudeDeg, const char **ppszAreaName)
12797
0
{
12798
0
    VALIDATE_POINTER1(hSRS, "OSRGetAreaOfUse", FALSE);
12799
12800
0
    return OGRSpatialReference::FromHandle(hSRS)->GetAreaOfUse(
12801
0
        pdfWestLongitudeDeg, pdfSouthLatitudeDeg, pdfEastLongitudeDeg,
12802
0
        pdfNorthLatitudeDeg, ppszAreaName);
12803
0
}
12804
12805
/************************************************************************/
12806
/*                     OSRGetCRSInfoListFromDatabase()                  */
12807
/************************************************************************/
12808
12809
/** \brief Enumerate CRS objects from the database.
12810
 *
12811
 * The returned object is an array of OSRCRSInfo* pointers, whose last
12812
 * entry is NULL. This array should be freed with OSRDestroyCRSInfoList()
12813
 *
12814
 * @param pszAuthName Authority name, used to restrict the search.
12815
 * Or NULL for all authorities.
12816
 * @param params Additional criteria. Must be set to NULL for now.
12817
 * @param pnOutResultCount Output parameter pointing to an integer to receive
12818
 * the size of the result list. Might be NULL
12819
 * @return an array of OSRCRSInfo* pointers to be freed with
12820
 * OSRDestroyCRSInfoList(), or NULL in case of error.
12821
 *
12822
 * @since GDAL 3.0
12823
 */
12824
OSRCRSInfo **
12825
OSRGetCRSInfoListFromDatabase(const char *pszAuthName,
12826
                              CPL_UNUSED const OSRCRSListParameters *params,
12827
                              int *pnOutResultCount)
12828
0
{
12829
0
    int nResultCount = 0;
12830
0
    auto projList = proj_get_crs_info_list_from_database(
12831
0
        OSRGetProjTLSContext(), pszAuthName, nullptr, &nResultCount);
12832
0
    if (pnOutResultCount)
12833
0
        *pnOutResultCount = nResultCount;
12834
0
    if (!projList)
12835
0
    {
12836
0
        return nullptr;
12837
0
    }
12838
0
    auto res = new OSRCRSInfo *[nResultCount + 1];
12839
0
    for (int i = 0; i < nResultCount; i++)
12840
0
    {
12841
0
        res[i] = new OSRCRSInfo;
12842
0
        res[i]->pszAuthName = projList[i]->auth_name
12843
0
                                  ? CPLStrdup(projList[i]->auth_name)
12844
0
                                  : nullptr;
12845
0
        res[i]->pszCode =
12846
0
            projList[i]->code ? CPLStrdup(projList[i]->code) : nullptr;
12847
0
        res[i]->pszName =
12848
0
            projList[i]->name ? CPLStrdup(projList[i]->name) : nullptr;
12849
0
        res[i]->eType = OSR_CRS_TYPE_OTHER;
12850
0
        switch (projList[i]->type)
12851
0
        {
12852
0
            case PJ_TYPE_GEOGRAPHIC_2D_CRS:
12853
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_2D;
12854
0
                break;
12855
0
            case PJ_TYPE_GEOGRAPHIC_3D_CRS:
12856
0
                res[i]->eType = OSR_CRS_TYPE_GEOGRAPHIC_3D;
12857
0
                break;
12858
0
            case PJ_TYPE_GEOCENTRIC_CRS:
12859
0
                res[i]->eType = OSR_CRS_TYPE_GEOCENTRIC;
12860
0
                break;
12861
0
            case PJ_TYPE_PROJECTED_CRS:
12862
0
                res[i]->eType = OSR_CRS_TYPE_PROJECTED;
12863
0
                break;
12864
0
            case PJ_TYPE_VERTICAL_CRS:
12865
0
                res[i]->eType = OSR_CRS_TYPE_VERTICAL;
12866
0
                break;
12867
0
            case PJ_TYPE_COMPOUND_CRS:
12868
0
                res[i]->eType = OSR_CRS_TYPE_COMPOUND;
12869
0
                break;
12870
0
            default:
12871
0
                break;
12872
0
        }
12873
0
        res[i]->bDeprecated = projList[i]->deprecated;
12874
0
        res[i]->bBboxValid = projList[i]->bbox_valid;
12875
0
        res[i]->dfWestLongitudeDeg = projList[i]->west_lon_degree;
12876
0
        res[i]->dfSouthLatitudeDeg = projList[i]->south_lat_degree;
12877
0
        res[i]->dfEastLongitudeDeg = projList[i]->east_lon_degree;
12878
0
        res[i]->dfNorthLatitudeDeg = projList[i]->north_lat_degree;
12879
0
        res[i]->pszAreaName = projList[i]->area_name
12880
0
                                  ? CPLStrdup(projList[i]->area_name)
12881
0
                                  : nullptr;
12882
0
        res[i]->pszProjectionMethod =
12883
0
            projList[i]->projection_method_name
12884
0
                ? CPLStrdup(projList[i]->projection_method_name)
12885
0
                : nullptr;
12886
0
    }
12887
0
    res[nResultCount] = nullptr;
12888
0
    proj_crs_info_list_destroy(projList);
12889
0
    return res;
12890
0
}
12891
12892
/************************************************************************/
12893
/*                        OSRDestroyCRSInfoList()                       */
12894
/************************************************************************/
12895
12896
/** \brief Destroy the result returned by
12897
 * OSRGetCRSInfoListFromDatabase().
12898
 *
12899
 * @since GDAL 3.0
12900
 */
12901
void OSRDestroyCRSInfoList(OSRCRSInfo **list)
12902
0
{
12903
0
    if (list)
12904
0
    {
12905
0
        for (int i = 0; list[i] != nullptr; i++)
12906
0
        {
12907
0
            CPLFree(list[i]->pszAuthName);
12908
0
            CPLFree(list[i]->pszCode);
12909
0
            CPLFree(list[i]->pszName);
12910
0
            CPLFree(list[i]->pszAreaName);
12911
0
            CPLFree(list[i]->pszProjectionMethod);
12912
0
            delete list[i];
12913
0
        }
12914
0
        delete[] list;
12915
0
    }
12916
0
}
12917
12918
/************************************************************************/
12919
/*                   OSRGetAuthorityListFromDatabase()                  */
12920
/************************************************************************/
12921
12922
/** \brief Return the list of CRS authorities used in the PROJ database.
12923
 *
12924
 * Such as "EPSG", "ESRI", "PROJ", "IGNF", "IAU_2015", etc.
12925
 *
12926
 * This is a direct mapping of https://proj.org/en/latest/development/reference/functions.html#c.proj_get_authorities_from_database
12927
 *
12928
 * @return nullptr in case of error, or a NULL terminated list of strings to
12929
 * free with CSLDestroy()
12930
 * @since GDAL 3.10
12931
 */
12932
char **OSRGetAuthorityListFromDatabase()
12933
0
{
12934
0
    PROJ_STRING_LIST list =
12935
0
        proj_get_authorities_from_database(OSRGetProjTLSContext());
12936
0
    if (!list)
12937
0
    {
12938
0
        return nullptr;
12939
0
    }
12940
0
    int count = 0;
12941
0
    while (list[count])
12942
0
        ++count;
12943
0
    char **res = static_cast<char **>(CPLCalloc(count + 1, sizeof(char *)));
12944
0
    for (int i = 0; i < count; ++i)
12945
0
        res[i] = CPLStrdup(list[i]);
12946
0
    proj_string_list_destroy(list);
12947
0
    return res;
12948
0
}
12949
12950
/************************************************************************/
12951
/*                    UpdateCoordinateSystemFromGeogCRS()               */
12952
/************************************************************************/
12953
12954
/*! @cond Doxygen_Suppress */
12955
/** \brief Used by gt_wkt_srs.cpp to create projected 3D CRS. Internal use only
12956
 *
12957
 * @since GDAL 3.1
12958
 */
12959
void OGRSpatialReference::UpdateCoordinateSystemFromGeogCRS()
12960
0
{
12961
0
    TAKE_OPTIONAL_LOCK();
12962
12963
0
    d->refreshProjObj();
12964
0
    if (!d->m_pj_crs)
12965
0
        return;
12966
0
    if (d->m_pjType != PJ_TYPE_PROJECTED_CRS)
12967
0
        return;
12968
0
    if (GetAxesCount() == 3)
12969
0
        return;
12970
0
    auto ctxt = d->getPROJContext();
12971
0
    auto baseCRS = proj_crs_get_geodetic_crs(ctxt, d->m_pj_crs);
12972
0
    if (!baseCRS)
12973
0
        return;
12974
0
    auto baseCRSCS = proj_crs_get_coordinate_system(ctxt, baseCRS);
12975
0
    if (!baseCRSCS)
12976
0
    {
12977
0
        proj_destroy(baseCRS);
12978
0
        return;
12979
0
    }
12980
0
    if (proj_cs_get_axis_count(ctxt, baseCRSCS) != 3)
12981
0
    {
12982
0
        proj_destroy(baseCRSCS);
12983
0
        proj_destroy(baseCRS);
12984
0
        return;
12985
0
    }
12986
0
    auto projCS = proj_crs_get_coordinate_system(ctxt, d->m_pj_crs);
12987
0
    if (!projCS || proj_cs_get_axis_count(ctxt, projCS) != 2)
12988
0
    {
12989
0
        proj_destroy(baseCRSCS);
12990
0
        proj_destroy(baseCRS);
12991
0
        proj_destroy(projCS);
12992
0
        return;
12993
0
    }
12994
12995
0
    PJ_AXIS_DESCRIPTION axis[3];
12996
0
    for (int i = 0; i < 3; i++)
12997
0
    {
12998
0
        const char *name = nullptr;
12999
0
        const char *abbreviation = nullptr;
13000
0
        const char *direction = nullptr;
13001
0
        double unit_conv_factor = 0;
13002
0
        const char *unit_name = nullptr;
13003
0
        proj_cs_get_axis_info(ctxt, i < 2 ? projCS : baseCRSCS, i, &name,
13004
0
                              &abbreviation, &direction, &unit_conv_factor,
13005
0
                              &unit_name, nullptr, nullptr);
13006
0
        axis[i].name = CPLStrdup(name);
13007
0
        axis[i].abbreviation = CPLStrdup(abbreviation);
13008
0
        axis[i].direction = CPLStrdup(direction);
13009
0
        axis[i].unit_name = CPLStrdup(unit_name);
13010
0
        axis[i].unit_conv_factor = unit_conv_factor;
13011
0
        axis[i].unit_type = PJ_UT_LINEAR;
13012
0
    }
13013
0
    proj_destroy(baseCRSCS);
13014
0
    proj_destroy(projCS);
13015
0
    auto cs = proj_create_cs(ctxt, PJ_CS_TYPE_CARTESIAN, 3, axis);
13016
0
    for (int i = 0; i < 3; i++)
13017
0
    {
13018
0
        CPLFree(axis[i].name);
13019
0
        CPLFree(axis[i].abbreviation);
13020
0
        CPLFree(axis[i].direction);
13021
0
        CPLFree(axis[i].unit_name);
13022
0
    }
13023
0
    if (!cs)
13024
0
    {
13025
0
        proj_destroy(baseCRS);
13026
0
        return;
13027
0
    }
13028
0
    auto conversion = proj_crs_get_coordoperation(ctxt, d->m_pj_crs);
13029
0
    auto crs = proj_create_projected_crs(ctxt, d->getProjCRSName(), baseCRS,
13030
0
                                         conversion, cs);
13031
0
    proj_destroy(baseCRS);
13032
0
    proj_destroy(conversion);
13033
0
    proj_destroy(cs);
13034
0
    d->setPjCRS(crs);
13035
0
}
13036
13037
/*! @endcond */
13038
13039
/************************************************************************/
13040
/*                             PromoteTo3D()                            */
13041
/************************************************************************/
13042
13043
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13044
 *
13045
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
13046
 * units.
13047
 *
13048
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13049
 * be used.
13050
 * @return OGRERR_NONE if no error occurred.
13051
 * @since GDAL 3.1 and PROJ 6.3
13052
 */
13053
OGRErr OGRSpatialReference::PromoteTo3D(const char *pszName)
13054
0
{
13055
0
    TAKE_OPTIONAL_LOCK();
13056
13057
0
    d->refreshProjObj();
13058
0
    if (!d->m_pj_crs)
13059
0
        return OGRERR_FAILURE;
13060
0
    auto newPj =
13061
0
        proj_crs_promote_to_3D(d->getPROJContext(), pszName, d->m_pj_crs);
13062
0
    if (!newPj)
13063
0
        return OGRERR_FAILURE;
13064
0
    d->setPjCRS(newPj);
13065
0
    return OGRERR_NONE;
13066
0
}
13067
13068
/************************************************************************/
13069
/*                             OSRPromoteTo3D()                         */
13070
/************************************************************************/
13071
13072
/** \brief "Promotes" a 2D CRS to a 3D CRS one.
13073
 *
13074
 * See OGRSpatialReference::PromoteTo3D()
13075
 *
13076
 * @since GDAL 3.1 and PROJ 6.3
13077
 */
13078
OGRErr OSRPromoteTo3D(OGRSpatialReferenceH hSRS, const char *pszName)
13079
0
{
13080
0
    VALIDATE_POINTER1(hSRS, "OSRPromoteTo3D", OGRERR_FAILURE);
13081
13082
0
    return OGRSpatialReference::FromHandle(hSRS)->PromoteTo3D(pszName);
13083
0
}
13084
13085
/************************************************************************/
13086
/*                             DemoteTo2D()                             */
13087
/************************************************************************/
13088
13089
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13090
 *
13091
 * @param pszName New name for the CRS. If set to NULL, the previous name will
13092
 * be used.
13093
 * @return OGRERR_NONE if no error occurred.
13094
 * @since GDAL 3.2 and PROJ 6.3
13095
 */
13096
OGRErr OGRSpatialReference::DemoteTo2D(const char *pszName)
13097
0
{
13098
0
    TAKE_OPTIONAL_LOCK();
13099
13100
0
    d->refreshProjObj();
13101
0
    if (!d->m_pj_crs)
13102
0
        return OGRERR_FAILURE;
13103
0
    auto newPj =
13104
0
        proj_crs_demote_to_2D(d->getPROJContext(), pszName, d->m_pj_crs);
13105
0
    if (!newPj)
13106
0
        return OGRERR_FAILURE;
13107
0
    d->setPjCRS(newPj);
13108
0
    return OGRERR_NONE;
13109
0
}
13110
13111
/************************************************************************/
13112
/*                             OSRDemoteTo2D()                          */
13113
/************************************************************************/
13114
13115
/** \brief "Demote" a 3D CRS to a 2D CRS one.
13116
 *
13117
 * See OGRSpatialReference::DemoteTo2D()
13118
 *
13119
 * @since GDAL 3.2 and PROJ 6.3
13120
 */
13121
OGRErr OSRDemoteTo2D(OGRSpatialReferenceH hSRS, const char *pszName)
13122
0
{
13123
0
    VALIDATE_POINTER1(hSRS, "OSRDemoteTo2D", OGRERR_FAILURE);
13124
13125
0
    return OGRSpatialReference::FromHandle(hSRS)->DemoteTo2D(pszName);
13126
0
}
13127
13128
/************************************************************************/
13129
/*                           GetEPSGGeogCS()                            */
13130
/************************************************************************/
13131
13132
/** Try to establish what the EPSG code for this coordinate systems
13133
 * GEOGCS might be.  Returns -1 if no reasonable guess can be made.
13134
 *
13135
 * @return EPSG code
13136
 */
13137
13138
int OGRSpatialReference::GetEPSGGeogCS() const
13139
13140
0
{
13141
0
    TAKE_OPTIONAL_LOCK();
13142
13143
    /* -------------------------------------------------------------------- */
13144
    /*      Check axis order.                                               */
13145
    /* -------------------------------------------------------------------- */
13146
0
    auto poGeogCRS = std::unique_ptr<OGRSpatialReference>(CloneGeogCS());
13147
0
    if (!poGeogCRS)
13148
0
        return -1;
13149
13150
0
    bool ret = false;
13151
0
    poGeogCRS->d->demoteFromBoundCRS();
13152
0
    auto cs = proj_crs_get_coordinate_system(d->getPROJContext(),
13153
0
                                             poGeogCRS->d->m_pj_crs);
13154
0
    poGeogCRS->d->undoDemoteFromBoundCRS();
13155
0
    if (cs)
13156
0
    {
13157
0
        const char *pszDirection = nullptr;
13158
0
        if (proj_cs_get_axis_info(d->getPROJContext(), cs, 0, nullptr, nullptr,
13159
0
                                  &pszDirection, nullptr, nullptr, nullptr,
13160
0
                                  nullptr))
13161
0
        {
13162
0
            if (EQUAL(pszDirection, "north"))
13163
0
            {
13164
0
                ret = true;
13165
0
            }
13166
0
        }
13167
13168
0
        proj_destroy(cs);
13169
0
    }
13170
0
    if (!ret)
13171
0
        return -1;
13172
13173
    /* -------------------------------------------------------------------- */
13174
    /*      Do we already have it?                                          */
13175
    /* -------------------------------------------------------------------- */
13176
0
    const char *pszAuthName = GetAuthorityName("GEOGCS");
13177
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg"))
13178
0
        return atoi(GetAuthorityCode("GEOGCS"));
13179
13180
    /* -------------------------------------------------------------------- */
13181
    /*      Get the datum and geogcs names.                                 */
13182
    /* -------------------------------------------------------------------- */
13183
13184
0
    const char *pszGEOGCS = GetAttrValue("GEOGCS");
13185
0
    const char *pszDatum = GetAttrValue("DATUM");
13186
13187
    // We can only operate on coordinate systems with a geogcs.
13188
0
    OGRSpatialReference oSRSTmp;
13189
0
    if (pszGEOGCS == nullptr || pszDatum == nullptr)
13190
0
    {
13191
        // Calling GetAttrValue("GEOGCS") will fail on a CRS that can't be
13192
        // export to WKT1, so try to extract the geographic CRS through PROJ
13193
        // API with CopyGeogCSFrom() and get the nodes' values from it.
13194
0
        oSRSTmp.CopyGeogCSFrom(this);
13195
0
        pszGEOGCS = oSRSTmp.GetAttrValue("GEOGCS");
13196
0
        pszDatum = oSRSTmp.GetAttrValue("DATUM");
13197
0
        if (pszGEOGCS == nullptr || pszDatum == nullptr)
13198
0
        {
13199
0
            return -1;
13200
0
        }
13201
0
    }
13202
13203
    // Lookup geographic CRS name
13204
0
    const PJ_TYPE type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
13205
0
    PJ_OBJ_LIST *list = proj_create_from_name(
13206
0
        d->getPROJContext(), nullptr, pszGEOGCS, &type, 1, false, 1, nullptr);
13207
0
    if (list)
13208
0
    {
13209
0
        const auto listSize = proj_list_get_count(list);
13210
0
        if (listSize == 1)
13211
0
        {
13212
0
            auto crs = proj_list_get(d->getPROJContext(), list, 0);
13213
0
            if (crs)
13214
0
            {
13215
0
                pszAuthName = proj_get_id_auth_name(crs, 0);
13216
0
                const char *pszCode = proj_get_id_code(crs, 0);
13217
0
                if (pszAuthName && pszCode && EQUAL(pszAuthName, "EPSG"))
13218
0
                {
13219
0
                    const int nCode = atoi(pszCode);
13220
0
                    proj_destroy(crs);
13221
0
                    proj_list_destroy(list);
13222
0
                    return nCode;
13223
0
                }
13224
0
                proj_destroy(crs);
13225
0
            }
13226
0
        }
13227
0
        proj_list_destroy(list);
13228
0
    }
13229
13230
    /* -------------------------------------------------------------------- */
13231
    /*      Is this a "well known" geographic coordinate system?            */
13232
    /* -------------------------------------------------------------------- */
13233
0
    const bool bWGS = strstr(pszGEOGCS, "WGS") != nullptr ||
13234
0
                      strstr(pszDatum, "WGS") ||
13235
0
                      strstr(pszGEOGCS, "World Geodetic System") ||
13236
0
                      strstr(pszGEOGCS, "World_Geodetic_System") ||
13237
0
                      strstr(pszDatum, "World Geodetic System") ||
13238
0
                      strstr(pszDatum, "World_Geodetic_System");
13239
13240
0
    const bool bNAD = strstr(pszGEOGCS, "NAD") != nullptr ||
13241
0
                      strstr(pszDatum, "NAD") ||
13242
0
                      strstr(pszGEOGCS, "North American") ||
13243
0
                      strstr(pszGEOGCS, "North_American") ||
13244
0
                      strstr(pszDatum, "North American") ||
13245
0
                      strstr(pszDatum, "North_American");
13246
13247
0
    if (bWGS && (strstr(pszGEOGCS, "84") || strstr(pszDatum, "84")))
13248
0
        return 4326;
13249
13250
0
    if (bWGS && (strstr(pszGEOGCS, "72") || strstr(pszDatum, "72")))
13251
0
        return 4322;
13252
13253
    // This is questionable as there are several 'flavors' of NAD83 that
13254
    // are not the same as 4269
13255
0
    if (bNAD && (strstr(pszGEOGCS, "83") || strstr(pszDatum, "83")))
13256
0
        return 4269;
13257
13258
0
    if (bNAD && (strstr(pszGEOGCS, "27") || strstr(pszDatum, "27")))
13259
0
        return 4267;
13260
13261
    /* -------------------------------------------------------------------- */
13262
    /*      If we know the datum, associate the most likely GCS with        */
13263
    /*      it.                                                             */
13264
    /* -------------------------------------------------------------------- */
13265
0
    const OGRSpatialReference &oActiveObj = oSRSTmp.IsEmpty() ? *this : oSRSTmp;
13266
0
    pszAuthName = oActiveObj.GetAuthorityName("GEOGCS|DATUM");
13267
0
    if (pszAuthName != nullptr && EQUAL(pszAuthName, "epsg") &&
13268
0
        GetPrimeMeridian() == 0.0)
13269
0
    {
13270
0
        const int nDatum = atoi(oActiveObj.GetAuthorityCode("GEOGCS|DATUM"));
13271
13272
0
        if (nDatum >= 6000 && nDatum <= 6999)
13273
0
            return nDatum - 2000;
13274
0
    }
13275
13276
0
    return -1;
13277
0
}
13278
13279
/************************************************************************/
13280
/*                          SetCoordinateEpoch()                        */
13281
/************************************************************************/
13282
13283
/** Set the coordinate epoch, as decimal year.
13284
 *
13285
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13286
 * change with time. To be unambiguous the coordinates must always be qualified
13287
 * with the epoch at which they are valid. The coordinate epoch is not
13288
 * necessarily the epoch at which the observation was collected.
13289
 *
13290
 * Pedantically the coordinate epoch of an observation belongs to the
13291
 * observation, and not to the CRS, however it is often more practical to
13292
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13293
 * CRS (see IsDynamic())
13294
 *
13295
 * This method is the same as the OSRSetCoordinateEpoch() function.
13296
 *
13297
 * @param dfCoordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3)
13298
 * @since OGR 3.4
13299
 */
13300
13301
void OGRSpatialReference::SetCoordinateEpoch(double dfCoordinateEpoch)
13302
0
{
13303
0
    d->m_coordinateEpoch = dfCoordinateEpoch;
13304
0
}
13305
13306
/************************************************************************/
13307
/*                      OSRSetCoordinateEpoch()                         */
13308
/************************************************************************/
13309
13310
/** \brief Set the coordinate epoch, as decimal year.
13311
 *
13312
 * See OGRSpatialReference::SetCoordinateEpoch()
13313
 *
13314
 * @since OGR 3.4
13315
 */
13316
void OSRSetCoordinateEpoch(OGRSpatialReferenceH hSRS, double dfCoordinateEpoch)
13317
0
{
13318
0
    VALIDATE_POINTER0(hSRS, "OSRSetCoordinateEpoch");
13319
13320
0
    return OGRSpatialReference::FromHandle(hSRS)->SetCoordinateEpoch(
13321
0
        dfCoordinateEpoch);
13322
0
}
13323
13324
/************************************************************************/
13325
/*                          GetCoordinateEpoch()                        */
13326
/************************************************************************/
13327
13328
/** Return the coordinate epoch, as decimal year.
13329
 *
13330
 * In a dynamic CRS, coordinates of a point on the surface of the Earth may
13331
 * change with time. To be unambiguous the coordinates must always be qualified
13332
 * with the epoch at which they are valid. The coordinate epoch is not
13333
 * necessarily the epoch at which the observation was collected.
13334
 *
13335
 * Pedantically the coordinate epoch of an observation belongs to the
13336
 * observation, and not to the CRS, however it is often more practical to
13337
 * bind it to the CRS. The coordinate epoch should be specified for dynamic
13338
 * CRS (see IsDynamic())
13339
 *
13340
 * This method is the same as the OSRGetCoordinateEpoch() function.
13341
 *
13342
 * @return coordinateEpoch Coordinate epoch as decimal year (e.g. 2021.3), or 0
13343
 *                         if not set, or relevant.
13344
 * @since OGR 3.4
13345
 */
13346
13347
double OGRSpatialReference::GetCoordinateEpoch() const
13348
0
{
13349
0
    return d->m_coordinateEpoch;
13350
0
}
13351
13352
/************************************************************************/
13353
/*                      OSRGetCoordinateEpoch()                        */
13354
/************************************************************************/
13355
13356
/** \brief Get the coordinate epoch, as decimal year.
13357
 *
13358
 * See OGRSpatialReference::GetCoordinateEpoch()
13359
 *
13360
 * @since OGR 3.4
13361
 */
13362
double OSRGetCoordinateEpoch(OGRSpatialReferenceH hSRS)
13363
0
{
13364
0
    VALIDATE_POINTER1(hSRS, "OSRGetCoordinateEpoch", 0);
13365
13366
0
    return OGRSpatialReference::FromHandle(hSRS)->GetCoordinateEpoch();
13367
0
}